Introduction

Cosmos DB SQL queries can become slow due to cross-partition scans, missing indexes, or inefficient query patterns. Slow queries consume more RUs, increase latency, and can cause application timeouts.

Symptoms

High query latency:

```sql -- Query takes seconds to complete SELECT * FROM c WHERE c.email = 'user@example.com'

-- Request charge shows high RU consumption -- x-ms-request-charge: 500.0 RUs (expensive!) ```

Cross-partition scan:

bash
# In query metrics:
"Cross Partition Query" : true
"Index Utilization" : 0%  // Full scan
"Documents Examined" : 100000  // Many documents scanned
"Documents Returned" : 1  // Few returned

Query timeout:

csharp
// Application timeout
Microsoft.Azure.Documents.DocumentClientException: Request timeout
RequestCharge: 50000 RUs

Common Causes

  1. 1.Missing index - Property in WHERE clause not indexed
  2. 2.Cross-partition query - Not using partition key
  3. 3.Inefficient query - Using functions on indexed properties
  4. 4.Large result set - Returning too many documents
  5. 5.SELECT * - Retrieving all properties including large ones
  6. 6.Missing TOP/LIMIT - No pagination
  7. 7. ORDER BY without index - Sorting without composite index

Step-by-Step Fix

  1. 1.Check logs for specific error messages
  2. 2.Verify configuration settings
  3. 3.Test network connectivity
  4. 4.Review recent changes
  5. 5.Apply corrective action
  6. 6.Verify the fix

Step 1: Get Query Metrics

```csharp // Enable query metrics var queryOptions = new QueryRequestOptions { PopulateQueryMetrics = true, MaxItemCount = 100 };

var query = container.GetItemQueryIterator<dynamic>( "SELECT * FROM c WHERE c.status = 'active'", null, queryOptions);

var response = await query.ReadNextAsync(); var metrics = response.Diagnostics;

Console.WriteLine($"Request Charge: {response.RequestCharge} RUs"); Console.WriteLine($"Metrics: {metrics}"); ```

Step 2: Check Index Utilization

```sql -- Use Query Explorer in Azure Portal to see: -- - Index Utilization Score -- - Documents Examined vs Returned -- - Execution Time

-- Bad query (low index utilization): SELECT * FROM c WHERE UPPER(c.email) = 'USER@EXAMPLE.COM' -- Index can't be used due to UPPER() function

-- Good query (high index utilization): SELECT * FROM c WHERE c.email = 'user@example.com' -- Index can be used directly ```

Step 3: Add Indexing Policy

json
{
  "indexingPolicy": {
    "automatic": true,
    "indexingMode": "consistent",
    "includedPaths": [
      {
        "path": "/email/?",
        "indexes": [
          { "kind": "Hash", "dataType": "String", "precision": -1 }
        ]
      },
      {
        "path": "/status/?",
        "indexes": [
          { "kind": "Hash", "dataType": "String", "precision": -1 }
        ]
      },
      {
        "path": "/createdAt/?",
        "indexes": [
          { "kind": "Range", "dataType": "String", "precision": -1 }
        ]
      }
    ],
    "excludedPaths": [
      { "path": "/largeField/*" },
      { "path": "/nestedObject/*" }
    ]
  }
}

Apply via CLI:

bash
az cosmosdb sql container update \
  --account-name my-cosmos \
  --resource-group my-rg \
  --database-name my-db \
  --name my-container \
  --idx @indexing-policy.json

Step 4: Use Partition Key in Query

```csharp // Bad: Cross-partition query var query = container.GetItemQueryIterator<dynamic>( "SELECT * FROM c WHERE c.userId = 'user123'");

// Good: Single-partition query var query = container.GetItemQueryIterator<dynamic>( new QueryDefinition("SELECT * FROM c WHERE c.userId = @userId AND c.id = @id"), null, new QueryRequestOptions { PartitionKey = new PartitionKey("user123"), IndexingDirective = IndexingDirective.Include }); ```

Step 5: Optimize SELECT Statement

```sql -- Bad: SELECT * returns all properties SELECT * FROM c WHERE c.status = 'active'

-- Good: Select only needed properties SELECT c.id, c.name, c.email FROM c WHERE c.status = 'active'

-- Reduces payload size and RU consumption ```

Step 6: Add Pagination

```csharp // Use continuation tokens for pagination var queryOptions = new QueryRequestOptions { MaxItemCount = 100 // Return 100 items per request };

var query = container.GetItemQueryIterator<dynamic>( "SELECT * FROM c WHERE c.status = 'active'", continuationToken, queryOptions);

while (query.HasMoreResults) { var page = await query.ReadNextAsync(); var token = page.ContinuationToken;

// Process page, save token for next request } ```

Step 7: Create Composite Index for ORDER BY

json
{
  "indexingPolicy": {
    "compositeIndexes": [
      [
        { "path": "/status", "order": "ascending" },
        { "path": "/createdAt", "order": "descending" }
      ]
    ]
  }
}
sql
-- ORDER BY requires composite index for multiple properties
SELECT c.id, c.name FROM c 
WHERE c.status = 'active'
ORDER BY c.status ASC, c.createdAt DESC

Step 8: Avoid Functions on Indexed Properties

```sql -- Bad: Function prevents index use SELECT * FROM c WHERE LEFT(c.name, 3) = 'Joh' SELECT * FROM c WHERE c.createdAt >= DATEADD('day', -7, GetCurrentDateTime())

-- Good: Use parameterized values SELECT * FROM c WHERE STARTSWITH(c.name, 'Joh') -- Use STARTSWITH SELECT * FROM c WHERE c.createdAt >= @sevenDaysAgo -- Calculate in app

-- Even better: Use BETWEEN for ranges SELECT * FROM c WHERE c.createdAt BETWEEN @start AND @end ```

Step 9: Use TOP for Known Limits

```sql -- Use TOP when you know result limit SELECT TOP 10 c.id, c.name FROM c WHERE c.status = 'active' ORDER BY c.createdAt DESC

-- Or use OFFSET LIMIT (more expensive due to state tracking) SELECT c.id, c.name FROM c WHERE c.status = 'active' ORDER BY c.createdAt DESC OFFSET 0 LIMIT 10 ```

Step 10: Monitor Query Performance

```bash # Enable diagnostic settings for query logs az monitor diagnostic-settings create \ --name cosmos-query-logs \ --resource /subscriptions/SUB/resourceGroups/my-rg/providers/Microsoft.DocumentDB/databaseAccounts/my-cosmos \ --workspace /subscriptions/SUB/resourcegroups/my-rg/providers/microsoft.operationalinsights/workspaces/my-workspace \ --logs '[{"category":"DataPlaneRequests","enabled":true}]'

Cosmos DB Query Optimization Checklist

CheckBeforeAfter
Index utilization0%>80%
Documents examined100000<1000
Request charge500 RUs<50 RUs
Partition scanCross-partitionSingle-partition

Verification

```sql -- Test query performance after optimization SELECT c.id, c.name FROM c WHERE c.status = 'active' AND c.partitionKey = 'value' ORDER BY c.createdAt DESC

-- Check metrics: -- - Request charge should be low (<10 RUs for single item) -- - Index utilization should be high (>90%) -- - Query latency should be <10ms ```

  • [Fix Azure Cosmos DB Throttling](/articles/fix-azure-cosmos-db-throttling)
  • [Fix Azure Cosmos DB Partition Hotspot](/articles/fix-azure-cosmos-db-partition-hotspot)
  • [Fix Azure Cosmos DB Change Feed Lag](/articles/fix-azure-cosmos-db-change-feed-lag)
  • [Technical troubleshooting: Fix Azure Aks Pod Crashloopbackoff Issue in Azure](azure-aks-pod-crashloopbackoff)
  • [Technical troubleshooting: Fix Azure Api Management Policy Expression Runtime](azure-api-management-policy-expression-runtime-error)
  • [Technical troubleshooting: Fix Azure App Configuration Feature Flag Not Refre](azure-app-configuration-feature-flag-not-refreshing)
  • [Technical troubleshooting: Fix Azure App Service 503 Always On Disabled Issue](azure-app-service-503-always-on-disabled)
  • [Technical troubleshooting: Fix Azure Application Gateway Err SSL Unrecognized](azure-application-gateway-err-ssl-unrecognized-name-alert)

<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "Fix Azure Cosmos DB SQL Query Slow", "description": "Troubleshoot slow Cosmos DB SQL queries. Add indexes, use partition keys, and optimize query patterns for performance.", "url": "https://www.fixwikihub.com/fix-azure-cosmos-db-sql-query-slow", "publisher": { "@type": "Organization", "name": "FixWikiHub", "url": "https://www.fixwikihub.com" }, "author": { "@type": "Person", "name": "FixWikiHub Editorial Team" }, "datePublished": "2026-04-02T12:34:34.508Z", "dateModified": "2026-04-02T12:34:34.508Z" } </script>