NoSQL용 Azure Cosmos DB의 자체 조인

적용 대상: NoSQL

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

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

단일 항목으로 자체 조인

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

[
  {
    "id": "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb",
    "categoryId": "bbbbbbbb-1111-2222-3333-cccccccccccc",
    "name": "Teapo Surfboard (6'10\") Grape",
    "sku": "teapo-surfboard-72109",
    "tags": [
      {
        "id": "cccccccc-2222-3333-4444-dddddddddddd",
        "slug": "tail-shape-swallow",
        "name": "Tail Shape: Swallow"
      },
      {
        "id": "dddddddd-3333-4444-5555-eeeeeeeeeeee",
        "slug": "length-inches-82",
        "name": "Length: 82 inches"
      },
      {
        "id": "eeeeeeee-4444-5555-6666-ffffffffffff",
        "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개 이상의 태그가 있을 수 있는 향후 제품을 처리할 만큼 유연하지 않습니다.

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

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

이 쿼리는 태그 배열의 각 값에 대한 항목이 있는 단순 배열을 반환합니다.

[
  {
    "id": "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb",
    "sku": "teapo-surfboard-72109",
    "slug": "tail-shape-swallow"
  },
  {
    "id": "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb",
    "sku": "teapo-surfboard-72109",
    "slug": "length-inches-82"
  },
  {
    "id": "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb",
    "sku": "teapo-surfboard-72109",
    "slug": "color-group-purple"
  }
]

쿼리를 세어보겠습니다. 이제 쿼리에는 결과 집합의 각 제품 항목과 t 자체 조인 배열에 대한 두 개의 별칭 ptags 있습니다. 키워드는 * 입력 집합을 유추할 수 있는 경우에만 모든 필드를 프로젝션하는 데 유효하지만, 이제 두 개의 입력 집합(pt)이 있습니다. 이 제약 조건으로 인해 태그와 함께 slug 제품에서 반환된 필드를 id sku 명시적으로 정의해야 합니다. 이 쿼리를 더 쉽게 읽고 이해할 수 있도록 필드를 삭제 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": "ffffffff-5555-6666-7777-aaaaaaaaaaaa",
    "categoryId": "cccccccc-8888-9999-0000-dddddddddddd",
    "categoryName": "Sleeping Bags",
    "name": "Maresse Sleeping Bag (6') Ming",
    "sku": "maresse-sleeping-bag-65503",
    "tags": [
      {
        "id": "b1b1b1b1-cccc-dddd-eeee-f2f2f2f2f2f2",
        "slug": "bag-shape-mummy",
        "name": "Bag Shape: Mummy"
      },
      {
        "id": "bbbbbbbb-7777-8888-9999-cccccccccccc",
        "slug": "bag-insulation-down-fill",
        "name": "Bag Insulation: Down Fill"
      }
    ]
  },
  {
    "id": "c2c2c2c2-dddd-eeee-ffff-a3a3a3a3a3a3",
    "categoryId": "cccccccc-8888-9999-0000-dddddddddddd",
    "categoryName": "Sleeping Bags",
    "name": "Vareno Sleeping Bag (6') Turmeric",
    "sku": "vareno-sleeping-bag-65508",
    "tags": [
      {
        "id": "dddddddd-9999-0000-1111-eeeeeeeeeeee",
        "slug": "bag-insulation-synthetic-fill",
        "name": "Bag Insulation: Synthetic Fill"
      },
      {
        "id": "a0a0a0a0-bbbb-cccc-dddd-e1e1e1e1e1e1",
        "slug": "color-group-yellow",
        "name": "Color Group: Yellow"
      },
      {
        "id": "b1b1b1b1-cccc-dddd-eeee-f2f2f2f2f2f2",
        "slug": "bag-shape-mummy",
        "name": "Bag Shape: Mummy"
      }
    ]
  }
]

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

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

  • tags 각 항목의 배열 길이가 다릅니다. 바레노 침낭에는 두 개의 태그가 있으며 Maresse 침낭에는 3개의 태그가 있습니다.

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

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

Item 태그
마레스 침낭 (6') 밍 가방 모양: 미라
마레스 침낭 (6') 밍 가방 절연 : 아래로 채우기
바레노 침낭 (6') 심황 가방 단열재: 합성 채우기
바레노 침낭 (6') 심황 색 그룹: 노란색
바레노 침낭 (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"
  }
]