Azure Cosmos DB for NoSQL의 셀프 조인

적용 대상: NoSQL

Azure Cosmos DB for NoSQL에서 데이터는 스키마가 없고 일반적으로 비정규화됩니다. 관계형 데이터베이스처럼 엔터티 및 세트 간에 데이터를 조인하는 대신 조인은 단일 항목 내에서 발생합니다. 특히 조인은 해당 항목으로 범위가 지정되며 여러 항목 및 컨테이너에서 발생할 수 없습니다.

항목 및 컨테이너 간에 조인해야 하는 경우 이를 방지하기 위해 데이터 모델을 다시 작성하는 것이 좋습니다.

단일 항목으로 셀프 조인

한 항목 내 셀프 조인 예제를 살펴보겠습니다. 단일 항목이 있는 컨테이너를 고려합니다. 이 항목은 다양한 태그가 있는 제품을 나타냅니다.

[
  {
    "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"
      }
    ]
  }
]

이 제품의 색 그룹을 찾아야 하면 어떻게 하나요? 일반적으로 color-group- 접두사를 사용하는 값에 대한 tags 배열의 모든 잠재적 인덱스를 확인하는 필터가 있는 쿼리를 작성해야 합니다.

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-")

이 기술은 빠르게 부적절해질 수 있습니다. 쿼리 구문 길이의 복잡성은 배열의 잠재적 항목 수에 따라 증가합니다. 또한 이 쿼리는 3개를 초과하는 태그가 있을 수 있는 향후 제품을 처리할 만큼 유연하지 않습니다.

기존 관계형 데이터베이스에서는 태그를 별도의 테이블로 구분하고 결과에 적용된 필터를 사용하여 테이블 간 조인을 수행합니다. API for NoSQL에서 JOIN 키워드를 사용하여 항목 내에서 셀프 조인 작업을 수행할 수 있습니다.

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

이 쿼리는 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 및 셀프 조인된 tags 배열에 대한 t입니다. * 키워드는 입력 집합을 유추할 수 있는 경우에만 모든 필드를 프로젝션할 수 있지만 이제 두 개의 입력 집합(pt)이 있습니다. 이 제약 조건으로 인해 반환된 필드는 tags의 slug와 함께 제품의 idsku로 명시적으로 정의해야 합니다. 이 쿼리를 더 쉽게 읽고 이해할 수 있도록 하려면 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"
      }
    ]
  }
]

mummy 가방 모양의 모든 항목을 찾아야 하면 어떻게 하나요? 태그 bag-shape-mummy를 검색할 수 있지만 이러한 항목의 두 가지 특성을 고려하는 복잡한 쿼리를 작성해야 합니다.

  • bag-shape- 접두사가 있는 태그는 각 배열의 여러 인덱스에서 발생합니다. Vareno 침낭의 경우 태그는 세 번째 항목(인덱스: 2)입니다. Maresse 침낭의 경우 태그는 첫 번째 항목(인덱스: 0)입니다.

  • 각 항목의 tags 배열 길이가 서로 다릅니다. Vareno 침낭에는 두 개의 태그가 있지만 Maresse 침낭에는 세 개의 태그가 있습니다.

여기서 JOIN 키워드는 항목과 태그의 교차 제품을 만드는 데 유용한 도구입니다. 조인은 조인에 참여하는 집합의 전체 교차 제품을 만듭니다. 결과는 항목의 모든 순열과 대상 배열 내의 값이 포함된 튜플 세트입니다.

샘플 침낭 제품 및 태그에 대한 조인 작업은 다음 항목을 만듭니다.

항목 태그
Maresse Sleeping Bag (6') Ming Bag Shape: Mummy
Maresse Sleeping Bag (6') Ming Bag Insulation: Down Fill
Vareno Sleeping Bag (6') Turmeric Bag Insulation: Synthetic Fill
Vareno Sleeping Bag (6') Turmeric Color Group: Yellow
Vareno Sleeping Bag (6') Turmeric Bag Shape: Mummy

컨테이너에 여러 항목이 포함된 조인에 대한 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"
  }
]