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 iç SELECT
olarak 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ır ile 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 tags
onHandQuantities
ç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 SELECT
WHERE
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": "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb",
"name": "Winter glove"
},
{
"id": "bbbbbbbb-1111-2222-3333-cccccccccccc"
},
{
"id": "cccccccc-2222-3333-4444-dddddddddddd"
}
]
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 undefined
EXISTS
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"
}
]