Aracılığıyla paylaş


NoSQL için Azure Cosmos DB'deki alt sorgular

UYGULANANLAR: NoSQL

Alt sorgu, NoSQL için Azure Cosmos DB içinde başka bir sorgu içinde iç içe yerleştirilmiş bir sorgudur. Alt sorgu, iç sorgu veya SELECTolarak da adlandırılır. Alt sorgu içeren deyim genellikle dış sorgu olarak adlandırılır.

Alt sorgu türleri

İki ana alt sorgu türü vardır:

  • Bağıntılı: Dış sorgudaki değerlere başvuran bir alt sorgu. Alt sorgu, dış sorgunun işlediği her satır için bir kez değerlendirilir.
  • Bağıntısız: Dış sorgudan bağımsız bir alt sorgu. Dış sorguya bağlı kalmadan kendi başına çalıştırılabilir.

Not

Azure Cosmos DB yalnızca bağıntılı alt sorguları destekler.

Alt sorgular, döndürdikleri satır ve sütun sayısına göre daha fazla sınıflandırılabilir. Üç tür kur vardır:

  • Tablo: Birden çok satır ve birden çok sütun döndürür.
  • Çoklu değer: Birden çok satır ve tek bir sütun döndürür.
  • Skaler: Tek bir satır ve tek bir sütun döndürür.

NoSQL için Azure Cosmos DB'deki sorgular her zaman tek bir sütun döndürür (basit bir değer veya karmaşık bir öğe). Bu nedenle, yalnızca çok değerli ve skaler alt sorgular uygulanabilir. Çok değerli alt sorguyu yalnızca yan tümcesinde FROM ilişkisel ifade olarak kullanabilirsiniz. Skaler alt sorguyu or WHERE yan tümcesinde SELECT skaler ifade olarak veya yan tümcesinde FROM ilişkisel ifade olarak kullanabilirsiniz.

Çok değerli alt sorgular

Çok değerli alt sorgular bir öğe kümesi döndürür ve her zaman yan tümcesinde FROM kullanılır. Bunlar şu amaçla kullanılır:

  • İfadeleri iyileştirme JOIN (kendi kendine birleştirme).
  • Pahalı ifadeleri bir kez değerlendirme ve birden çok kez başvurma.

Kendi kendine birleştirme ifadelerini iyileştirme

Çok değerli alt sorgular, yan tümcedeki WHERE tüm çapraz birleşimlerden sonra değil, her select-many ifadesinden sonra koşul göndererek ifadeleri iyileştirebilirJOIN.

Aşağıdaki sorguyu göz önünde bulundurun:

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

Bu sorgu için dizin, etiketi "kış" veya "sonbahar" olan herhangi bir nameöğeyle, sıfırile on arasında en az bir quantity ve öğesinin olduğu backstock en az bir ambarla eşleşirfalse. Buradaki JOIN ifade, herhangi bir filtre uygulanmadan önce eşleşen her öğe için , ve warehouseStock dizilerinin tüm öğelerinin tagsonHandQuantitiesçarpımını gerçekleştirir.

Yan WHERE tümcesi daha sonra filtre koşulunu her <c, t, n, s> tanımlama grubuna uygular. Örneğin, eşleşen bir öğenin üç dizinin her birinde on öğe varsa, bu öğe (1.000) tanımlama grubuna 1 x 10 x 10 x 10 genişletilir. Burada alt sorgular kullanmak, sonraki ifadeyle birleştirmeden önce birleştirilmiş dizi öğelerini filtrelemeye yardımcı olabilir.

Bu sorgu, önceki sorguya eşdeğerdir ancak alt sorgular kullanır:

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)

Etiketler dizisindeki yalnızca bir öğenin filtreyle eşleşdiğini ve hem miktar hem de hisse senedi dizileri için beş öğe olduğunu varsayalım. İfadeler JOIN daha sonra ilk sorgudaki 1.000 öğenin aksine (25) öğeye genişletilir 1 x 1 x 5 x 5 .

Bir kez değerlendirme ve birçok kez başvurma

Alt sorgular, kullanıcı tanımlı işlevler (UDF'ler), karmaşık dizeler veya aritmetik ifadeler gibi pahalı ifadelerle sorguları iyileştirmeye yardımcı olabilir. bir alt sorguyu ifadeyle birlikte kullanarak ifadeyi bir JOIN kez değerlendirebilir ancak birçok kez başvurabilirsiniz.

Aşağıdaki UDF'nin (getTotalWithTax) tanımlandığını varsayalım.

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

Aşağıdaki sorgu UDF'yi getTotalWithTax birden çok kez çalıştırır:

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

UDF'yi yalnızca bir kez çalıştıran eşdeğer bir sorgu aşağıdadır:

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

İpucu

İfadelerin ürün arası davranışını JOIN göz önünde bulundurun. UDF ifadesi olarak değerlendirebiliyorsa undefined, ifadenin JOIN doğrudan değer yerine alt sorgudan bir nesne döndürerek her zaman tek bir satır ürettiğinden emin olmanız gerekir.

Dış başvuru verileriyle birleştirmeyi taklit edin

Genellikle ölçü birimleri gibi nadiren değişen statik verilere başvurmanız gerekebilir. Bir sorgudaki her öğe için statik verileri yinelememek idealdir. Bu yinelemeyi önlemek, tek tek öğe boyutunu daha küçük tutarak depolamadan tasarruf sağlar ve yazma performansını artırır. Statik başvuru verileri koleksiyonuyla iç birleşim semantiğini taklit etmek için bir alt sorgu kullanabilirsiniz.

Örneğin, şu ölçü kümesini göz önünde bulundurun:

Ad Çarpan Temel birim
ng Nanogram 1.00E-09 Gram
µg Mikrogram 1.00E-06 Gram
mg Miligram 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

Aşağıdaki sorgu, çıktıya birimin adını eklemeniz için bu verilerle birleştirmeyi taklit eder:

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

Skaler alt sorgular

Skaler alt sorgu ifadesi, tek bir değer olarak değerlendirilen bir alt sorgudur. Skaler alt sorgu ifadesinin değeri, alt sorgunun projeksiyonunun (SELECT yan tümcesi) değeridir. Skaler ifadenin geçerli olduğu birçok yerde skaler alt sorgu ifadesi kullanabilirsiniz. Örneğin, hem ve SELECTWHERE yan tümcelerindeki herhangi bir ifadede skaler alt sorgu kullanabilirsiniz.

Skaler alt sorgu kullanmak her zaman sorgunuzu iyileştirmeye yardımcı olmaz. Örneğin, skaler alt sorguların bir sisteme veya kullanıcı tanımlı işlevlere bağımsız değişken olarak geçirilmesi, kaynak birimi (RU) tüketimini veya gecikme süresini azaltmada hiçbir fayda sağlamaz.

Skaler alt sorgular şu şekilde sınıflandırılabilir:

  • Basit ifade skaler alt sorguları
  • Skaler alt sorguları toplama

Basit ifade skaler alt sorguları

Basit ifade skaler alt sorgusu, herhangi bir toplama ifadesi içermeyen yan tümcesine sahip bağıntılı bir SELECT alt sorgudur. Derleyici bunları daha büyük bir basit ifadeye dönüştürdüğünden bu alt sorgular iyileştirme avantajı sağlamaz. İç ve dış sorgular arasında bağıntılı bağlam yoktur.

İlk örnek olarak, bu önemsiz sorguyu göz önünde bulundurun.

SELECT
    1 AS a,
    2 AS b

Basit ifadeli skaler alt sorgu kullanarak bu sorguyu yeniden yazabilirsiniz.

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

Her iki sorgu da aynı çıkışı üretir.

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

Bu sonraki örnek sorgu, benzersiz tanımlayıcıyı basit ifade skaler alt sorgu olarak bir önek ile birleştirir.

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

Bu örnek, her öğe için yalnızca ilgili alanları döndürmek için basit ifade skaler alt sorgu kullanır. Sorgu her öğe için bir çıkış oluşturur, ancak yalnızca alt sorgudaki filtreyi karşılıyorsa yansıtılan alanı içerir.

SELECT
    p.id,
    (SELECT p.name WHERE CONTAINS(p.name, "glove")).name
FROM
    products p
[
  {
    "id": "03230",
    "name": "Winter glove"
  },
  {
    "id": "03238"
  },
  {
    "id": "03229"
  }
]

Skaler alt sorguları toplama

Toplam skaler alt sorgu, projeksiyonunda veya filtresinde tek bir değere göre değerlendirilen bir toplama işlevine sahip olan bir alt sorgudur.

İlk örnek olarak, aşağıdaki alanlara sahip bir öğeyi göz önünde bulundurun.

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

Aşağıda projeksiyonunda tek bir toplama işlevi ifadesi bulunan bir alt sorgu verilmiştir. Bu sorgu, her öğe için tüm etiketleri sayar.

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

Filtre içeren aynı alt sorgu aşağıda verilmiştır.

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
  }
]

Birden çok toplama işlevi ifadesi içeren başka bir alt sorgu aşağıda verilmiştir:

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
    }
  }
]

Son olarak, hem projeksiyonda hem de filtrede toplama alt sorgusunu içeren bir sorgu aşağıda verilmişti:

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
  }
]

Bu sorguyu yazmanın daha uygun bir yolu, alt sorguya katılmak ve hem SELECT hem de WHERE yan tümcelerinde alt sorgu diğer adına başvurmaktır. Alt sorguyu hem yansıtma hem de filtrede değil yalnızca join deyimi içinde yürütmeniz gerektiğinden bu sorgu daha verimlidir.

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 ifadesi

NoSQL için Azure Cosmos DB'nin sorgu altyapısı ifadeleri destekler EXISTS . Bu ifade, NoSQL için Azure Cosmos DB'de yerleşik olarak bulunan toplu bir skaler alt sorgudur. EXISTS bir alt sorgu ifadesi alır ve alt sorgu herhangi bir satır döndürürse döndürür true . Aksi takdirde döndürür false.

Sorgu altyapısı boole ifadeleri ile diğer skaler ifadeler arasında ayrım yapmadığından, hem WHERE hem de SELECT yan tümcelerinde kullanabilirsinizEXISTS. Bu davranış, boole ifadesinin yalnızca filtrelerle kısıtlandığı T-SQL'in aksinedir.

EXISTS Alt sorgu olan tek bir değer undefinedEXISTS döndürürse false olarak değerlendirilir. Örneğin, hiçbir şey döndürmeden aşağıdaki sorguyu göz önünde bulundurun.

SELECT VALUE
    undefined

İfadeyi EXISTS ve önceki sorguyu bir alt sorgu olarak kullanırsanız, ifade döndürür false.

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

Önceki alt sorgudaki VALUE anahtar sözcüğü atlanırsa, alt sorgu tek bir boş nesne içeren bir dizi olarak değerlendirilir.

SELECT
    undefined
[
  {}
]

Bu noktada, EXISTS nesne ({}) teknik olarak çıktığı için true ifade olarak değerlendirilir.

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

yaygın kullanım örneğinden ARRAY_CONTAINS biri, bir öğeyi dizideki bir öğenin varlığına göre filtrelemektir. Bu durumda dizinin "dış giyim" adlı bir öğe içerip içermediğini tags kontrol ediyoruz.

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

Aynı sorgu alternatif bir seçenek olarak kullanılabilir EXISTS .

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

Ayrıca, ARRAY_CONTAINS yalnızca bir değerin dizi içindeki herhangi bir öğeye eşit olup olmadığını denetleyebilirsiniz. Dizi özelliklerinde daha karmaşık filtrelere ihtiyacınız varsa, bunun yerine kullanın JOIN .

Her birinde dizi içeren accessories birden çok öğe içeren bir kümedeki bu örnek öğeyi göz önünde bulundurun.

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

Şimdi, her öğedeki dizideki ve quantityOnHand özelliklerine type göre filtreleyen aşağıdaki sorguyu göz önünde bulundurun.

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

Koleksiyondaki öğelerin her biri için, dizi öğeleriyle bir çapraz ürün gerçekleştirilir. Bu JOIN işlem, dizi içindeki özelliklere göre filtrelemeyi mümkün kılar. Ancak, bu sorguyu RU tüketimi önemlidir. Örneğin, 1.000 öğenin her dizide 100 öğesi varsa, bu değer (100.000) tanımlama gruplarına genişletilir 1,000 x 100 .

Kullanmak EXISTS , bu pahalı çapraz üründen kaçınmaya yardımcı olabilir. Bu sonraki örnekte sorgu, alt sorgu içindeki dizi öğelerine EXISTS filtre ekler. Bir dizi öğesi filtreyle eşleşiyorsa, bunu yansıtıp EXISTS true olarak değerlendirirsiniz.

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

Sorgular ayrıca projeksiyonda diğer ad EXISTS kullanabilir ve diğer adlara başvurabilir:

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 ifadesi

Sorgunun sonuçlarını dizi olarak yansıtmak için ifadeyi ARRAY kullanabilirsiniz. Bu ifadeyi yalnızca sorgunun SELECT yan tümcesinde kullanabilirsiniz.

Bu örnekler için en azından bu öğeye sahip bir kapsayıcı olduğunu varsayalım.

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

Bu ilk örnekte ifade yan tümcesi SELECT içinde kullanılır.

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

Diğer alt sorgularda olduğu gibi, ifadeye ARRAY sahip filtreler de mümkündür.

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

Dizi ifadeleri, alt sorgulardaki yan tümceden FROM sonra da gelebilir.

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