共用方式為


適用於 NoSQL 的 Azure Cosmos DB 中的子查詢

適用於:NoSQL

子查詢是巢狀於適用於 NoSQL 的 Azure Cosmos DB 內另一個查詢內的查詢。 子查詢也稱為 內部查詢內部 SELECT。 包含子查詢的語句通常稱為 外部查詢

子查詢的類型

子查詢有兩種主要類型:

  • 相互關聯:子查詢參考外部查詢的值。 針對外部查詢處理的每個資料列,子查詢都經過一次評估。
  • 非相互關聯:子查詢與外部查詢無關。 可獨立執行,不依賴外部查詢。

注意

Azure Cosmos DB 僅支援相互關聯的子查詢。

根據傳回的資料列和資料行數目,子查詢可進一步分類。 有三種類型:

  • 資料表:傳回多個資料列和多個資料行。
  • 多重值:傳回多個資料列和單一資料行。
  • 純量:傳回單一資料列和單一資料行。

適用於 NoSQL 的 Azure Cosmos DB 查詢一律會傳回單一數據行(簡單值或複雜專案)。 因此,僅適用於多重值和純量子查詢。 您只能在 子句中使用 FROM 多重值子查詢作為關係表達式。 您可以使用純量子查詢做為 或 WHERE 子句中的SELECT純量表達式,或在 子句中FROM做為關係表達式。

多重值子查詢

多重值子查詢會傳回一組專案,而且一律會在 子句內 FROM 使用。 用途包括:

  • 優化 JOIN (自我聯結) 表達式。
  • 耗費資源的運算式只評估一次,然後參考多次。

優化自我聯結表達式

多重值子查詢可以藉由在每個 select-many 運算式之後推送述詞,而不是在 子句中的所有WHERE交叉聯結之後,來優化JOIN表達式。

請考慮以下查詢:

SELECT VALUE
    COUNT(1)
FROM
    products p
JOIN 
    t in p.tags
JOIN 
    q in p.onHandQuantities
JOIN 
    s in p.warehouseStock
WHERE 
    t.name IN ("winter", "fall") AND
    (q.quantity BETWEEN 0 AND 10) AND
    NOT s.backstock

針對此查詢,索引會比對具有 「winter」 或 「fall」 之標籤name的任何專案,至少一個quantity介於零10 之間,以及至少一個是的backstockfalse倉儲。 此處的JOIN表達式會在套用任何篩選之前,針對每個相符專案tags執行、 onHandQuantitieswarehouseStock 陣列的交叉乘積。

WHERE 句接著會在每個 <c, t, n, s> Tuple 上套用篩選述詞。 例如,如果相符的專案在三個陣列中各有 10個專案,則會展開至 1 x 10 x 10 x 10 (也就是1,000個) Tuple。 在這裡使用子查詢有助於在聯結下一個運算式之前,先篩選掉聯結的陣列項目。

此查詢相當於上述其中一項,但是使用的是子查詢:

SELECT VALUE
    COUNT(1)
FROM
    products p
JOIN 
    (SELECT VALUE t FROM t IN p.tags WHERE t.name IN ("winter", "fall"))
JOIN 
    (SELECT VALUE q FROM q IN p.onHandQuantities WHERE q.quantity BETWEEN 0 AND 10)
JOIN 
    (SELECT VALUE s FROM s IN p.warehouseStock WHERE NOT s.backstock)

假設標記陣列中只有一個專案符合篩選條件,而且數量和庫存陣列都有五個專案。 表達式 JOIN 接著會展開至 1 x 1 x 5 x 525) 個專案,而不是 第一個查詢中的 1,000 個專案。

評估一次並參考多次

如果查詢中的運算式耗費資源,例如使用者定義函數 (UDF)、複雜字串或算術運算式,子查詢有助於將查詢最佳化。 您可以使用子查詢和 JOIN 表達式來評估表達式一次,但多次參考它。

假設您已定義下列 UDF (getTotalWithTax)。

function getTotalWithTax(subTotal){
  return subTotal * 1.25;
}

下列查詢會多次執行 UDF getTotalWithTax

SELECT VALUE {
    subtotal: p.price,
    total: udf.getTotalWithTax(p.price)
}
FROM
    products p
WHERE
    udf.getTotalWithTax(p.price) < 22.25

以下同樣的查詢只執行 UDF 一次:

SELECT VALUE {
    subtotal: p.price,
    total: totalPrice
}
FROM
    products p
JOIN
    (SELECT VALUE udf.getTotalWithTax(p.price)) totalPrice
WHERE
    totalPrice < 22.25

提示

請記住表達式的 JOIN 交叉乘積行為。 如果 UDF 運算式可以評估為 undefined,您應該確定 JOIN 表示式一律會從子查詢傳回 物件,而不是直接從值產生單一數據列。

以外部參考資料來模擬聯結

您可能需要參考很少變更的靜態數據,例如 度量單位。 理想的做法是不要針對查詢中的每個項目重複靜態數據。 避免此重複作業可節省記憶體,並藉由將個別專案大小保持在較小的狀態來改善寫入效能。 您可以使用子查詢來模擬內部聯結語意與靜態參考數據的集合。

例如,請考慮此一組度量:

名稱 乘數 基礎單位
ng Nanogram 1.00E-09 Gram
µg Microgram 1.00E-06 Gram
mg Milligram 1.00E-03 Gram
g Gram 1.00E+00 Gram
kg Kilogram 1.00E+03 Gram
Mg Megagram 1.00E+06 Gram
Gg Gigagram 1.00E+09 Gram

下列查詢以這組資料來模擬聯結,讓您將單位名稱加入至輸出:

SELECT
    s.id,
    (s.weight.quantity * m.multiplier) AS calculatedWeight,
    m.unit AS unitOfWeight
FROM
    shipments s
JOIN m IN (
    SELECT VALUE [
        {unit: 'ng', name: 'nanogram', multiplier: 0.000000001, baseUnit: 'gram'},
        {unit: 'µg', name: 'microgram', multiplier: 0.000001, baseUnit: 'gram'},
        {unit: 'mg', name: 'milligram', multiplier: 0.001, baseUnit: 'gram'},
        {unit: 'g', name: 'gram', multiplier: 1, baseUnit: 'gram'},
        {unit: 'kg', name: 'kilogram', multiplier: 1000, baseUnit: 'gram'},
        {unit: 'Mg', name: 'megagram', multiplier: 1000000, baseUnit: 'gram'},
        {unit: 'Gg', name: 'gigagram', multiplier: 1000000000, baseUnit: 'gram'}
    ]
)
WHERE
    s.weight.units = m.unit

純量子查詢

純量子查詢運算式是評估為單一值的子查詢。 純量子查詢表達式的值是子查詢的投影 (SELECT 子句) 值。 在純量運算式有效的許多地方都可以使用純量子查詢運算式。 例如,您可以在 和 WHERE 子句中的任何SELECT表達式中使用純量子查詢。

使用純量子查詢不一定有助於優化查詢。 例如,將純量子查詢當做自變數傳遞至系統或使用者定義函式,對於減少資源單位 (RU) 耗用量或延遲沒有好處。

純量子查詢可以進一步分類為:

  • 簡單運算式純量子查詢
  • 彙總純量子查詢

簡單運算式純量子查詢

簡單表達式純量子查詢是具有不包含任何匯總表達式之子句的相互關聯子查詢 SELECT 。 這些子查詢會經由編譯器轉換成一個較大的簡單運算式,所以無益於最佳化。 內部和外部查詢之間沒有相互關聯的內容。

作為第一個範例,請考慮此簡單查詢。

SELECT
    1 AS a,
    2 AS b

您可以使用簡單表示式純量子查詢來重寫此查詢。

SELECT
    (SELECT VALUE 1) AS a, 
    (SELECT VALUE 2) AS b

這兩個查詢都會產生相同的輸出。

[
  {
    "a": 1,
    "b": 2
  }
]

下一個範例查詢會將具有前置詞的唯一標識符串連為簡單表達式純量子查詢。

SELECT 
    (SELECT VALUE Concat('ID-', p.id)) AS internalId
FROM
    products p

此範例會使用簡單表達式純量子查詢,只傳回每個專案的相關欄位。 查詢會針對每個項目輸出某些專案,但如果它符合子查詢內的篩選條件,則只會包含投影字段。

SELECT
    p.id,
    (SELECT p.name WHERE CONTAINS(p.name, "glove")).name
FROM
    products p
[
  {
    "id": "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb",
    "name": "Winter glove"
  },
  {
    "id": "bbbbbbbb-1111-2222-3333-cccccccccccc"
  },
  {
    "id": "cccccccc-2222-3333-4444-dddddddddddd"
  }
]

彙總純量子查詢

彙總純量子查詢的投影或篩選條件中,有一個彙總函式評估為單一值。

作為第一個範例,請考慮具有下列欄位的專案。

{
  "name": "Snow coat",
  "inventory": [
    {
      "location": "Redmond, WA",
      "quantity": 50
    },
    {
      "location": "Seattle, WA",
      "quantity": 30
    },
    {
      "location": "Washington, DC",
      "quantity": 25
    }
  ]
}

以下是其投影中具有單一聚合函數表達式的子查詢。 此查詢會計算每個專案的所有標記。

SELECT
    p.name,
    (SELECT VALUE COUNT(1) FROM i IN p.inventory) AS locationCount
FROM
    products p
[
  {
    "name": "Snow coat",
    "locationCount": 3
  }
]

以下是與篩選條件相同的子查詢。

SELECT
    p.name,
    (SELECT VALUE COUNT(1) FROM i IN p.inventory WHERE ENDSWITH(i.location, "WA")) AS washingtonLocationCount
FROM
    products p
[
  {
    "name": "Snow coat",
    "washingtonLocationCount": 2
  }
]

以下是具有多個聚合函數表達式的另一個子查詢:

SELECT
    p.name,
    (SELECT
        COUNT(1) AS locationCount,
        SUM(i.quantity) AS totalQuantity
    FROM i IN p.inventory) AS inventoryData
FROM
    products p
[
  {
    "name": "Snow coat",
    "inventoryData": {
      "locationCount": 2,
      "totalQuantity": 75
    }
  }
]

最後,以下是在投影和篩選中具有匯總子查詢的查詢:

SELECT
    p.name,
    (SELECT VALUE AVG(q.quantity) FROM q IN p.inventory WHERE q.quantity > 10) AS averageInventory
FROM
    products p
WHERE
    (SELECT VALUE COUNT(1) FROM i IN p.inventory WHERE i.quantity > 10) >= 1
[
  {
    "name": "Snow coat",
    "averageInventory": 35
  }
]

撰寫此查詢更好的方式是在子查詢上聯結,然後在 SELECT 和 WHERE 子句中參考子查詢別名。 此查詢更有效率,因為您只需要在 join 陳述式內執行子查詢,而不需要在投影和篩選條件中都執行子查詢。

SELECT
    p.name,
    inventoryData.inventoryAverage
FROM
    products p
JOIN
    (SELECT 
        COUNT(1) AS inventoryCount, 
        AVG(i.quantity) as inventoryAverage 
    FROM i IN p.inventory 
    WHERE i.quantity > 10) AS inventoryData
WHERE
    inventoryData.inventoryCount >= 1

EXISTS 運算式

適用於 NoSQL 的 Azure Cosmos DB 查詢引擎支援 EXISTS 表達式。 此表達式是內建於適用於 NoSQL 的 Azure Cosmos DB 中的匯總純量子查詢。 EXISTS 會取得子查詢表示式,並在子查詢傳回任何數據列時傳回 true 。 否則會傳回 false

因為查詢引擎不會區分布爾表達式和任何其他純量表示式,因此您可以在 和 WHERE 子句中使用。EXISTS SELECT 此行為與 T-SQL 不同,其中布爾表示式僅限於篩選條件。

如果子查詢傳EXISTS回單一值,undefinedEXISTS則評估為 false。 例如,請考慮下列不會傳回任何項目的查詢。

SELECT VALUE
    undefined

如果您使用 EXISTS 表示式和上述查詢做為子查詢,則表示式會傳 false回 。

SELECT
    EXISTS (SELECT VALUE undefined)
[
  {
    "$1": false
  }
]

如果省略上述子查詢中的 VALUE 關鍵詞,則子查詢會評估為具有單一空白對象的數位。

SELECT
    undefined
[
  {}
]

此時, EXISTS 表達式會評估為 true ,因為物件 ({}) 在技術上結束。

SELECT 
    EXISTS (SELECT undefined) 
[
  {
    "$1": true
  }
]

的常見使用案例 ARRAY_CONTAINS 是依據陣列中專案是否存在來篩選專案。 在此情況下,我們會檢查陣列是否 tags 包含名為 「outerwear」 的專案。

SELECT
    p.name,
    p.tags
FROM
    products p
WHERE
    ARRAY_CONTAINS(p.tags, "outerwear")

相同的查詢可以使用 EXISTS 做為替代選項。

SELECT
    p.name,
    p.tags
FROM
    products p
WHERE
    EXISTS (SELECT VALUE t FROM t IN p.tags WHERE t = "outerwear")

此外, ARRAY_CONTAINS 只能檢查值是否等於數位中的任何專案。 如果您需要陣列屬性上更複雜的篩選,請改用 JOIN

請考慮集合中的這個範例專案,其中每個專案都包含一個 accessories 數位列。

{
  "name": "Unobtani road bike",
  "accessories": [
    {
      "name": "Front/rear tire",
      "type": "tire",
      "quantityOnHand": 5
    },
    {
      "name": "9-speed chain",
      "type": "chains",
      "quantityOnHand": 25
    },
    {
      "name": "Clip-in pedals",
      "type": "pedals",
      "quantityOnHand": 15
    }
  ]
}

現在,請考慮下列查詢,根據 type 每個項目內陣列中的和 quantityOnHand 屬性進行篩選。

SELECT
    p.name,
    a.name AS accessoryName
FROM
    products p
JOIN
    a IN p.accessories
WHERE
    a.type = "chains" AND
    a.quantityOnHand >= 10
[
  {
    "name": "Unobtani road bike",
    "accessoryName": "9-speed chain"
  }
]

針對集合中的每個專案,會使用其數位元素執行交叉乘積。 這項 JOIN 作業可讓您篩選陣列內的屬性。 不過,此查詢的 RU 耗用量相當重要。 例如,如果 每個陣列中有1,000 個專案有 100 個專案,則會展開至 1,000 x 100 (也就是 100,000個) Tuple。

使用 EXISTS 有助於避免這種昂貴的跨產品。 在下一個範例中,查詢會篩選子查詢內的 EXISTS 陣列元素。 如果陣列元素符合篩選條件,則會投影它並 EXISTS 評估為 true。

SELECT VALUE
    p.name
FROM
    products p
WHERE
    EXISTS (SELECT VALUE 
        a 
    FROM 
        a IN p.accessories
    WHERE
        a.type = "chains" AND
        a.quantityOnHand >= 10)
[
  "Unobtani road bike"
]

查詢也可以為 EXISTS 投影中的別名和參考別名:

SELECT
    p.name,
    EXISTS (SELECT VALUE
        a 
    FROM 
        a IN p.accessories
    WHERE
        a.type = "chains" AND
        a.quantityOnHand >= 10) AS chainAccessoryAvailable
FROM
    products p
[
  {
    "name": "Unobtani road bike",
    "chainAccessoryAvailable": true
  }
]

ARRAY 運算式

您可以使用 ARRAY 表達式,將查詢的結果投影為陣列。 您只能在查詢的 子句內 SELECT 使用此表達式。

針對這些範例,假設有至少具有此專案的容器。

{
  "name": "Radimer mountain bike",
  "tags": [
    {
      "name": "road"
    },
    {
      "name": "bike"
    },
    {
      "name": "competitive"
    }
  ]
}

在第一個範例中,表達式會在 子句內 SELECT 使用。

SELECT
    p.name,
    ARRAY (SELECT VALUE t.name FROM t in p.tags) AS tagNames
FROM
    products p
[
  {
    "name": "Radimer mountain bike",
    "tagNames": [
      "road",
      "bike",
      "competitive"
    ]
  }
]

與其他子查詢一樣,可能會有表達式的 ARRAY 篩選。

SELECT
    p.name,
    ARRAY (SELECT VALUE t.name FROM t in p.tags) AS tagNames,
    ARRAY (SELECT VALUE t.name FROM t in p.tags WHERE CONTAINS(t.name, "bike")) AS bikeTagNames
FROM
    products p
[
  {
    "name": "Radimer mountain bike",
    "tagNames": [
      "road",
      "bike",
      "competitive"
    ],
    "bikeTagNames": [
      "bike"
    ]
  }
]

數位表示式也可以位於子查詢中的 子句之後 FROM

SELECT
    p.name,
    n.t.name AS nonBikeTagName
FROM
    products p
JOIN
    n IN (SELECT VALUE ARRAY(SELECT t FROM t in p.tags WHERE t.name NOT LIKE "%bike%"))
[
  {
    "name": "Radimer mountain bike",
    "nonBikeTagName": "road"
  },
  {
    "name": "Radimer mountain bike",
    "nonBikeTagName": "competitive"
  }
]