適用于 NoSQL 的 Azure Cosmos DB 中的自我聯結

適用於:NoSQL

在適用于 NoSQL 的 Azure Cosmos DB 中,資料是無架構的,且通常會反正規化。 就像在關係資料庫中一樣,不要跨實體和集合聯結資料,而是在單一專案中發生聯結。 具體來說,聯結的範圍是該專案,而且無法在多個專案和容器之間發生。

提示

如果您發現自己需要跨專案和容器聯結,請考慮重新建立 資料模型 以避免這種情況。

與單一專案自我聯結

讓我們看看專案內自我聯結的範例。 請考慮具有單一專案的容器。 此專案代表具有各種標籤的產品:

[
  {
    "id": "863e778d-21c9-4e2a-a984-d31f947c665c",
    "categoryId": "e592b992-d453-42ee-a74e-0de2cc97db42",
    "name": "Teapo Surfboard (6'10\") Grape",
    "sku": "teapo-surfboard-72109",
    "tags": [
      {
        "id": "556dc4f5-1dbd-41dc-9674-fda626e5d15c",
        "slug": "tail-shape-swallow",
        "name": "Tail Shape: Swallow"
      },
      {
        "id": "ac097b9a-8a30-4fd1-8cb6-69d3388ee8a2",
        "slug": "length-inches-82",
        "name": "Length: 82 inches"
      },
      {
        "id": "ce62b524-8e96-4999-b3e1-61ae7a672e2e",
        "slug": "color-group-purple",
        "name": "Color Group: Purple"
      }
    ]
  }
]

如果您需要尋找此產品的 色彩群組 ,該怎麼辦? 一般而言,您需要撰寫查詢,其具有篩選準則,以檢查陣列中 tags 每個可能索引的值,其前置詞為 color-group-

SELECT
  * 
FROM
  products p
WHERE
  STARTSWITH(p.tags[0].slug, "color-group-") OR
  STARTSWITH(p.tags[1].slug, "color-group-") OR
  STARTSWITH(p.tags[2].slug, "color-group-")

這項技術可能會變得無法快速取得。 查詢語法的複雜度或長度會增加陣列中潛在專案的數目。 此外,此查詢不夠彈性,無法處理未來可能有超過三個標籤的產品。

在傳統的關係資料庫中,標記會分隔成個別的資料表,而且會執行跨資料表聯結,並套用至結果的篩選準則。 在 NoSQL 的 API 中,我們可以使用 關鍵字在專案中 JOIN 執行自我聯結作業。

SELECT
  p.id,
  p.sku,
  t.slug
FROM
  products p
JOIN
  t IN p.tags

此查詢會傳回簡單的陣列,其中包含標記陣列中每個值的專案。

[
  {
    "id": "863e778d-21c9-4e2a-a984-d31f947c665c",
    "sku": "teapo-surfboard-72109",
    "slug": "tail-shape-swallow"
  },
  {
    "id": "863e778d-21c9-4e2a-a984-d31f947c665c",
    "sku": "teapo-surfboard-72109",
    "slug": "length-inches-82"
  },
  {
    "id": "863e778d-21c9-4e2a-a984-d31f947c665c",
    "sku": "teapo-surfboard-72109",
    "slug": "color-group-purple"
  }
]

讓我們細分查詢。 查詢現在有兩個別名: p 針對結果集中的每個產品專案,以及 t 針對自我聯結 tags 陣列。 *如果關鍵字可以推斷輸入集,則關鍵字僅能投影所有欄位,但現在有兩個輸入集 (pt) 。 由於此條件約束,我們必須明確地將傳回的欄位定義為 idsku 產品 slug 以及標記中的 和 。 為了讓此查詢更容易閱讀和瞭解,我們可以卸載 id 欄位,並使用標籤欄位的 name 別名將它重新命名為 tag

SELECT
  p.sku,
  t.name AS tag
FROM
  products p
JOIN
  t IN p.tags
[
  {
    "sku": "teapo-surfboard-72109",
    "tag": "Tail Shape: Swallow"
  },
  {
    "sku": "teapo-surfboard-72109",
    "tag": "Length: 82 inches"
  },
  {
    "sku": "teapo-surfboard-72109",
    "tag": "Color Group: Purple"
  }
]

最後,我們可以使用篩選來尋找 標記 color-group-purple 。 因為我們使用 JOIN 關鍵字,所以篩選準則彈性足以處理任何可變數目的標記。

SELECT
  p.sku,
  t.name AS tag
FROM
  products p
JOIN
  t IN p.tags
WHERE
  STARTSWITH(t.slug, "color-group-")
[
  {
    "sku": "teapo-surfboard-72109",
    "tag": "Color Group: Purple"
  }
]

自我聯結多個專案

讓我們繼續進行範例,其中我們需要在存在於多個專案中的陣列內尋找值。 在此範例中,請考慮具有兩個產品專案的容器。 每個專案都包含該專案的相關標籤。

[
  {
    "id": "80d62f31-9892-48e5-9b9b-5714d551b8b3",
    "categoryId": "19cd9b93-bdc5-4082-97fe-2c80c2fd77dd",
    "categoryName": "Sleeping Bags",
    "name": "Maresse Sleeping Bag (6') Ming",
    "sku": "maresse-sleeping-bag-65503",
    "tags": [
      {
        "id": "f50f3ee1-e150-4821-922b-ebe6ad82f313",
        "slug": "bag-shape-mummy",
        "name": "Bag Shape: Mummy"
      },
      {
        "id": "8564fb66-63ea-464a-872a-7598433b9479",
        "slug": "bag-insulation-down-fill",
        "name": "Bag Insulation: Down Fill"
      }
    ]
  },
  {
    "id": "6e9f51c1-6b45-440f-af5a-2abc96cd083d",
    "categoryId": "19cd9b93-bdc5-4082-97fe-2c80c2fd77dd",
    "categoryName": "Sleeping Bags",
    "name": "Vareno Sleeping Bag (6') Turmeric",
    "sku": "vareno-sleeping-bag-65508",
    "tags": [
      {
        "id": "e02502ce-367e-4fb4-940e-93d994fa6062",
        "slug": "bag-insulation-synthetic-fill",
        "name": "Bag Insulation: Synthetic Fill"
      },
      {
        "id": "c0844995-3db9-4dbb-8d9d-d2c2a6151b94",
        "slug": "color-group-yellow",
        "name": "Color Group: Yellow"
      },
      {
        "id": "f50f3ee1-e150-4821-922b-ebe6ad82f313",
        "slug": "bag-shape-mummy",
        "name": "Bag Shape: Mummy"
      }
    ]
  }
]

如果您需要尋找具有 多工包 形狀的每個專案,該怎麼辦? 您可以搜尋 標籤 bag-shape-mummy ,但您必須撰寫複雜的查詢,以說明這些專案的兩個特性:

  • 具有 bag-shape- 前置詞的標記會在每個陣列中的不同索引發生。 針對 Vareno 睡眠包,標籤是第三個專案 (索引: 2) 。 對於 Maresse 睡眠包,標籤是第一個專案 (索引: 0) 。

  • tags每個專案的陣列長度不同。 Vareno睡眠包有兩個標記,而Maresse睡眠包有三個標記。

在這裡, JOIN 關鍵字是建立專案和標記交叉乘積的絕佳工具。 聯結會建立參與聯結之集合的完整交叉乘積。 結果是一組 Tuple,其中包含專案的每個排列和目標陣列中的值。

我們範例睡眠包產品和標記的聯結作業會建立下列專案:

項目 標籤
Maresse 睡眠包 (6') Ming 包形狀:多工
Maresse 睡眠包 (6') Ming 包箱:向下填滿
Vareno 睡眠包 (6') 圖文 包箱:綜合填滿
Vareno 睡眠包 (6') 圖文 色彩群組:黃色
Vareno 睡眠包 (6') 圖文 包形狀:多工

以下是包含容器中多個專案的聯結的 SQL 查詢和 JSON 結果集。

SELECT
  p.sku,
  t.name AS tag
FROM
  products p
JOIN
  t IN p.tags
WHERE
  p.categoryName = "Sleeping Bags"
[
  {
    "sku": "maresse-sleeping-bag-65503",
    "tag": "Bag Shape: Mummy"
  },
  {
    "sku": "maresse-sleeping-bag-65503",
    "tag": "Bag Insulation: Down Fill"
  },
  {
    "sku": "vareno-sleeping-bag-65508",
    "tag": "Bag Insulation: Synthetic Fill"
  },
  {
    "sku": "vareno-sleeping-bag-65508",
    "tag": "Color Group: Yellow"
  },
  {
    "sku": "vareno-sleeping-bag-65508",
    "tag": "Bag Shape: Mummy"
  }
]

就像使用單一專案一樣,您可以在這裡套用篩選,只尋找符合特定標籤的專案。 例如,此查詢會尋找名稱為 bag-shape-mummy 的所有專案,以符合本節稍早所述的初始需求。

SELECT
  p.sku,
  t.name AS tag
FROM
  products p
JOIN
  t IN p.tags
WHERE
  p.categoryName = "Sleeping Bags" AND
  t.slug = "bag-shape-mummy"
[
  {
    "sku": "maresse-sleeping-bag-65503",
    "tag": "Bag Shape: Mummy"
  },
  {
    "sku": "vareno-sleeping-bag-65508",
    "tag": "Bag Shape: Mummy"
  }
]

您也可以變更篩選來取得不同的結果集。 例如,此查詢會尋找具有名為 bag-insulation-synthetic-fill 之標籤的所有專案。

SELECT
  p.sku,
  t.name AS tag
FROM
  products p
JOIN
  t IN p.tags
WHERE
  p.categoryName = "Sleeping Bags" AND
  t.slug = "bag-insulation-synthetic-fill"
[
  {
    "sku": "vareno-sleeping-bag-65508",
    "tag": "Bag Insulation: Synthetic Fill"
  }
]