Podzapytania w usłudze Azure Cosmos DB for NoSQL
DOTYCZY: NoSQL
Podzapytanie to zapytanie zagnieżdżone w ramach innego zapytania w usłudze Azure Cosmos DB for NoSQL. Podzapytanie jest również nazywane zapytaniem wewnętrznym lub wewnętrznym SELECT
. Instrukcja zawierająca podzapytywanie jest zwykle nazywana zapytaniem zewnętrznym.
Typy podzapytania
Istnieją dwa główne typy podzapytania:
- Skorelowane: podzapytywanie odwołujące się do wartości z zapytania zewnętrznego. Podzapytywanie jest oceniane raz dla każdego wiersza, który przetwarza zapytanie zewnętrzne.
- Niezrelowane: podzapytywanie niezależne od zapytania zewnętrznego. Można go uruchomić samodzielnie bez polegania na zapytaniu zewnętrznym.
Uwaga
Usługa Azure Cosmos DB obsługuje tylko skorelowane podzapytania.
Podzapytania można dalej klasyfikować na podstawie liczby zwracanych wierszy i kolumn. Dostępne są trzy typy kosztów:
- Tabela: zwraca wiele wierszy i wiele kolumn.
- Wiele wartości: zwraca wiele wierszy i jedną kolumnę.
- Skalarny: zwraca pojedynczy wiersz i jedną kolumnę.
Zapytania w usłudze Azure Cosmos DB dla NoSQL zawsze zwracają jedną kolumnę (prostą wartość lub złożony element). W związku z tym mają zastosowanie tylko podzapytania wielowartego i skalarne. Podzapytywanie wielowartość można użyć tylko w klauzuli FROM
jako wyrażenia relacyjnego. Można użyć podzapytania skalarnego jako wyrażenia skalarnego w klauzuli SELECT
or WHERE
lub jako wyrażenia relacyjnego w klauzuli FROM
.
Podzapytania wielu wartości
Podzapytania wielowartościowe zwracają zestaw elementów i są zawsze używane w klauzuli FROM
. Są one używane do:
JOIN
Optymalizowanie wyrażeń (samosprzężenia).- Ocenianie kosztownych wyrażeń raz i odwoływanie się do wielu razy.
Optymalizowanie wyrażeń samosprzężenia
Podzapytania wielu wartości mogą optymalizować JOIN
wyrażenia, wypychając predykaty po każdym wyrażeniu select-many , a nie po wszystkich sprzężeniach krzyżowych w klauzuli WHERE
.
Rozważ następujące zapytanie:
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
W przypadku tego zapytania indeks pasuje do dowolnego elementu, który ma tag z name
"zimą" lub "jesienią", co najmniej jeden z przedziału od zera do dziesięciu, a co najmniej jeden quantity
magazyn, w którym backstock
znajduje się false
. Wyrażenie JOIN
w tym miejscu wykonuje krzyżowy produkt wszystkich elementów tags
, onHandQuantities
i warehouseStock
tablic dla każdego pasującego elementu przed zastosowaniem dowolnego filtru.
Klauzula WHERE
następnie stosuje predykat filtru dla każdej <c, t, n, s>
krotki. Na przykład jeśli pasujący element miał dziesięć elementów w każdej z trzech tablic, rozszerza się na 1 x 10 x 10 x 10
(czyli 1000) krotek. Użycie podzapytania może pomóc w odfiltrowaniu sprzężonych elementów tablicy przed dołączeniem do następnego wyrażenia.
To zapytanie jest równoważne poprzedniemu, ale używa podzapytania:
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)
Załóżmy, że tylko jeden element w tablicy tagów jest zgodny z filtrem i istnieje pięć elementów zarówno dla liczby, jak i tablic magazynowych. Wyrażenia JOIN
następnie rozszerzają się na 1 x 1 x 5 x 5
(25) elementy, w przeciwieństwie do 1000 elementów w pierwszym zapytaniu.
Ocenianie raz i odwołuje się wiele razy
Podzapytania mogą ułatwić optymalizowanie zapytań za pomocą kosztownych wyrażeń, takich jak funkcje zdefiniowane przez użytkownika (UDF), złożone ciągi lub wyrażenia arytmetyczne. Możesz użyć podzapytania wraz z wyrażeniem JOIN
, aby obliczyć wyrażenie raz, ale odwoływać się do niego wiele razy.
Załóżmy, że zdefiniowano następującą funkcję zdefiniowaną przez użytkownika (getTotalWithTax
).
function getTotalWithTax(subTotal){
return subTotal * 1.25;
}
Następujące zapytanie uruchamia funkcję zdefiniowanej getTotalWithTax
przez użytkownika wiele razy:
SELECT VALUE {
subtotal: p.price,
total: udf.getTotalWithTax(p.price)
}
FROM
products p
WHERE
udf.getTotalWithTax(p.price) < 22.25
Oto równoważne zapytanie, które uruchamia funkcję UDF tylko raz:
SELECT VALUE {
subtotal: p.price,
total: totalPrice
}
FROM
products p
JOIN
(SELECT VALUE udf.getTotalWithTax(p.price)) totalPrice
WHERE
totalPrice < 22.25
Napiwek
Należy pamiętać o zachowaniu JOIN
wyrażeń między produktami. Jeśli wyrażenie UDF może ocenić wartość undefined
, upewnij się, że JOIN
wyrażenie zawsze tworzy pojedynczy wiersz, zwracając obiekt z podzapytania, a nie bezpośrednio.
Naśladować sprzężenia z zewnętrznymi danymi referencyjnymi
Często może być konieczne odwołanie się do danych statycznych, które rzadko się zmieniają, takich jak jednostki miary. Jest to idealne rozwiązanie, aby nie duplikować danych statycznych dla każdego elementu w zapytaniu. Unikanie tego duplikowania pozwala zaoszczędzić na magazynie i zwiększyć wydajność zapisu, zachowując mniejszy rozmiar pojedynczego elementu. Za pomocą podzapytania można naśladować semantykę sprzężenia wewnętrznego z kolekcją statycznych danych referencyjnych.
Rozważmy na przykład ten zestaw pomiarów:
Nazwa/nazwisko | Mnożnik | Jednostka bazowa | |
---|---|---|---|
ng |
Nanogram | 1.00E-09 |
Gram |
µg |
Mikrogramów | 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 |
Następujące zapytanie naśladuje dołączenie do tych danych, aby dodać nazwę lekcji do danych wyjściowych:
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
Podzapytania skalarne
Wyrażenie podzapytania skalarnego to podzapytywanie, które daje w wyniku pojedynczą wartość. Wartość wyrażenia podzapytania skalarnego jest wartością projekcji (SELECT
klauzuli) podzapytania. W wielu miejscach można użyć wyrażenia podrzędnego skalarnego, w którym wyrażenie skalarne jest prawidłowe. Można na przykład użyć podzapytania skalarnego w dowolnym wyrażeniu SELECT
w klauzulach i WHERE
.
Użycie podzapytania skalarnego nie zawsze pomaga zoptymalizować zapytanie. Na przykład przekazanie podzapytania skalarnego jako argumentu do funkcji zdefiniowanych przez system lub użytkownika nie zapewnia korzyści w zmniejszeniu zużycia jednostek zasobów (RU) ani opóźnienia.
Podzapytania skalarne mogą być dalej klasyfikowane jako:
- Podzapytania skalarne wyrażeń prostych
- Agregowanie podzapytania skalarnych
Podzapytania skalarne wyrażeń prostych
Podzapytywanie skalarne wyrażeń prostych jest skorelowanym podzapytaniem zawierającym klauzulę SELECT
, która nie zawiera żadnych wyrażeń agregujących. Te podzapytania nie zapewniają żadnych korzyści optymalizacji, ponieważ kompilator konwertuje je na jedno większe proste wyrażenie. Nie ma skorelowanego kontekstu między zapytaniami wewnętrznymi i zewnętrznymi.
Jako pierwszy przykład rozważ to proste zapytanie.
SELECT
1 AS a,
2 AS b
Możesz ponownie napisać to zapytanie, używając podzapytania skalarnego prostego wyrażenia.
SELECT
(SELECT VALUE 1) AS a,
(SELECT VALUE 2) AS b
Oba zapytania generują te same dane wyjściowe.
[
{
"a": 1,
"b": 2
}
]
To następne przykładowe zapytanie łączy unikatowy identyfikator z prefiksem jako podzapytywaniem skalarnym prostego wyrażenia.
SELECT
(SELECT VALUE Concat('ID-', p.id)) AS internalId
FROM
products p
W tym przykładzie użyto podzapytania skalarnego prostego wyrażenia, aby zwrócić tylko odpowiednie pola dla każdego elementu. Zapytanie zwraca coś dla każdego elementu, ale zawiera tylko pole przewidywane, jeśli spełnia filtr w podzapytaniu.
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"
}
]
Agregowanie podzapytania skalarnych
Podzapytywanie skalarne agregacji to podzapytywanie, które ma funkcję agregacji w projekcji lub filtrze, który daje jedną wartość.
Jako pierwszy przykład rozważ element z następującymi polami.
{
"name": "Snow coat",
"inventory": [
{
"location": "Redmond, WA",
"quantity": 50
},
{
"location": "Seattle, WA",
"quantity": 30
},
{
"location": "Washington, DC",
"quantity": 25
}
]
}
Oto podzapytywanie z pojedynczym wyrażeniem funkcji agregującej w projekcji. To zapytanie zlicza wszystkie tagi dla każdego elementu.
SELECT
p.name,
(SELECT VALUE COUNT(1) FROM i IN p.inventory) AS locationCount
FROM
products p
[
{
"name": "Snow coat",
"locationCount": 3
}
]
Oto ta sama podzapytywanie z filtrem.
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
}
]
Oto kolejna podzapytywanie z wieloma wyrażeniami funkcji agregacji:
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
}
}
]
Na koniec oto zapytanie z zagregowanym podzapytaniem zarówno w projekcji, jak i w filtrze:
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
}
]
Bardziej optymalnym sposobem na napisanie tego zapytania jest sprzężenie w podzapytaniu i odwołanie do aliasu podzapytania zarówno w klauzulach SELECT, jak i WHERE. To zapytanie jest bardziej wydajne, ponieważ należy wykonać podzapytywanie tylko w instrukcji join, a nie w projekcji i filtrze.
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
Wyrażenie EXISTS
Aparat zapytań usługi Azure Cosmos DB for NoSQL obsługuje EXISTS
wyrażenia. To wyrażenie jest agregowanym podzapytaniem skalarnym wbudowanym w usługę Azure Cosmos DB for NoSQL. EXISTS
przyjmuje wyrażenie podzapytania i zwraca true
wartość , jeśli podzapytywanie zwraca dowolne wiersze. W przeciwnym razie zwraca wartość false
.
Ponieważ aparat zapytań nie rozróżnia wyrażeń logicznych i innych wyrażeń skalarnych, można użyć EXISTS
w klauzulach SELECT
i WHERE
. To zachowanie jest w przeciwieństwie do języka T-SQL, gdzie wyrażenie logiczne jest ograniczone tylko do filtrów.
EXISTS
Jeśli podzapytywanie zwraca pojedynczą wartość undefined
, EXISTS
zwraca wartość false. Rozważmy na przykład następujące zapytanie, które nie zwraca niczego.
SELECT VALUE
undefined
Jeśli używasz EXISTS
wyrażenia i poprzedniego zapytania jako podzapytania, wyrażenie zwraca wartość false
.
SELECT
EXISTS (SELECT VALUE undefined)
[
{
"$1": false
}
]
Jeśli pominięto słowo kluczowe VALUE w poprzednim podzapytaniu, podzapytywanie jest obliczane na tablicę z pojedynczym pustym obiektem.
SELECT
undefined
[
{}
]
W tym momencie EXISTS
wyrażenie daje wartość , true
ponieważ obiekt ({}
) kończy się technicznie.
SELECT
EXISTS (SELECT undefined)
[
{
"$1": true
}
]
Typowym przypadkiem ARRAY_CONTAINS
użycia elementu jest filtrowanie elementu przez istnienie elementu w tablicy. W tym przypadku sprawdzamy, czy tablica tags
zawiera element o nazwie "odzież zewnętrzna".
SELECT
p.name,
p.tags
FROM
products p
WHERE
ARRAY_CONTAINS(p.tags, "outerwear")
To samo zapytanie może być używane EXISTS
jako alternatywna opcja.
SELECT
p.name,
p.tags
FROM
products p
WHERE
EXISTS (SELECT VALUE t FROM t IN p.tags WHERE t = "outerwear")
Ponadto można sprawdzić tylko, ARRAY_CONTAINS
czy wartość jest równa dowolnemu elementowi w tablicy. Jeśli potrzebujesz bardziej złożonych filtrów we właściwościach tablicy, użyj JOIN
zamiast tego.
Rozważmy ten przykładowy element w zestawie z wieloma elementami zawierającymi tablicę accessories
.
{
"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
}
]
}
Teraz rozważ następujące zapytanie, które filtruje na type
podstawie właściwości i quantityOnHand
w tablicy w ramach każdego elementu.
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"
}
]
Dla każdego elementu w kolekcji element między produktami jest wykonywany z elementami tablicy. Ta JOIN
operacja umożliwia filtrowanie właściwości w tablicy. Jednak użycie jednostek RU tego zapytania jest znaczące. Jeśli na przykład 1000 elementów miało 100 elementów w każdej tablicy, rozszerza się na 1,000 x 100
(czyli 100 000) krotek.
Użycie EXISTS
może pomóc uniknąć tego kosztownego produktu krzyżowego. W następnym przykładzie zapytanie filtruje elementy tablicy w podzapytaniu EXISTS
. Jeśli element tablicy jest zgodny z filtrem, projektujesz go i EXISTS
obliczasz wartość true.
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"
]
Zapytania mogą również aliasować EXISTS
i odwoływać się do aliasu w projekcji:
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
}
]
Wyrażenie ARRAY
Możesz użyć ARRAY
wyrażenia do projekcji wyników zapytania jako tablicy. To wyrażenie można użyć tylko w SELECT
klauzuli zapytania.
W tych przykładach załóżmy, że istnieje kontener z co najmniej tym elementem.
{
"name": "Radimer mountain bike",
"tags": [
{
"name": "road"
},
{
"name": "bike"
},
{
"name": "competitive"
}
]
}
W tym pierwszym przykładzie wyrażenie jest używane w klauzuli SELECT
.
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"
]
}
]
Podobnie jak w przypadku innych podzapytania, możliwe są filtry z wyrażeniem ARRAY
.
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"
]
}
]
Wyrażenia tablicowe mogą również pochodzić po klauzuli w podzapytaniach FROM
.
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"
}
]