Albekérdezések az Azure Cosmos DB for NoSQL-ben

A KÖVETKEZŐRE VONATKOZIK: NoSQL

Az al lekérdezés egy másik lekérdezésbe ágyazott lekérdezés az Azure Cosmos DB for NoSQL-ben. Az alkérdezéseket belső lekérdezésnek vagy belső SELECTlekérdezésnek is nevezik. Az al lekérdezést tartalmazó utasítást általában külső lekérdezésnek nevezzük.

Az albekérdezések típusai

Az albekérdezéseknek két fő típusa van:

  • Korrelált: A külső lekérdezésből származó értékekre hivatkozó al lekérdezés. Az alkérelmek kiértékelése egyszer történik minden olyan sornál, amelyet a külső lekérdezés feldolgoz.
  • Nem korrelált: A külső lekérdezéstől független alkérdezés. Önállóan is futtatható anélkül, hogy a külső lekérdezésre támaszkodna.

Megjegyzés

Az Azure Cosmos DB csak a korrelált albekérdezéseket támogatja.

Az albekérdezések tovább besorolhatók a visszaadott sorok és oszlopok száma alapján. Három típus létezik:

  • Táblázat: Több sort és több oszlopot ad vissza.
  • Többértékű: Több sort és egyetlen oszlopot ad vissza.
  • Skaláris: Egyetlen sort és egyetlen oszlopot ad vissza.

Az Azure Cosmos DB for NoSQL lekérdezései mindig egyetlen oszlopot adnak vissza (egy egyszerű értéket vagy egy összetett elemet). Ezért csak a többértékű és a skaláris al lekérdezések alkalmazhatók. A többértékű alkérdezéseket csak a FROM záradékban használhatja relációs kifejezésként. A skaláris alkérdezéseket használhatja skaláris kifejezésként a vagy WHERE záradékbanSELECT, vagy relációs kifejezésként a FROM záradékban.

Többértékű albekérdezések

A többértékű albekérdezések egy elemkészletet adnak vissza, és mindig a FROM záradékon belül használják. Ezeket a következő célokra használják:

  • Kifejezések optimalizálása JOIN (önillesztés).
  • Költséges kifejezések egyszer történő kiértékelése és többszöri hivatkozás.

Önillesztésű kifejezések optimalizálása

A többértékű albekérdezések úgy optimalizálhatják JOIN a kifejezéseket, hogy predikátumokat nyomnak le minden select-many kifejezés után, nem pedig a WHERE záradék összes keresztcsatlakozása után.

Tekintse meg a következő lekérdezést:

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

Ebben a lekérdezésben az index megfelel minden olyan elemnek, amely "téli" vagy "őszi" címkével rendelkezikname, legalább egy quantitynullátóltízig, és legalább egy raktárhoz, ahol a backstock .false Az JOIN itt található kifejezés az egyes egyező elemekhez tartozó , és onHandQuantitieswarehouseStock tömbök összes elemének kereszttermékéttagshajtja végre a szűrő alkalmazása előtt.

A WHERE záradék ezután alkalmazza a szűrő predikátumot minden <c, t, n, s> egyes rekordra. Ha például egy egyező elemet mind a három tömbben tíz elem tartalmaz, az 1000-ra bővül 1 x 10 x 10 x 10 . Az itt található albekérdezések segíthetnek a csatlakoztatott tömbelemek szűrésében, mielőtt csatlakozna a következő kifejezéshez.

Ez a lekérdezés egyenértékű az előzővel, de al lekérdezéseket használ:

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)

Tegyük fel, hogy a címketömbben csak egy elem felel meg a szűrőnek, és öt elem van mind a tápanyagokhoz, mind a kiszolgálótömbökhöz. A JOIN kifejezések ezután (25) elemre bontanak, 1 x 1 x 5 x 5 szemben az első lekérdezés 1000 elemével.

Kiértékelés egyszer és hivatkozás sokszor

Az alkérések segíthetnek optimalizálni a lekérdezéseket olyan költséges kifejezésekkel, mint a felhasználó által definiált függvények (UDF-ek), az összetett sztringek vagy az aritmetikai kifejezések. A kifejezés egyszer kiértékelhető egy kifejezéssel JOIN együtt, de sokszor hivatkozhat rá.

Tegyük fel, hogy a következő UDF -t (getTotalWithTax) definiálta.

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

A következő lekérdezés többször futtatja az UDF-et getTotalWithTax :

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

Íme egy egyenértékű lekérdezés, amely csak egyszer futtatja az UDF-et:

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

Tipp

Tartsa szem előtt a kifejezések termékközi viselkedését JOIN . Ha az UDF-kifejezés kiértékelhető a értékre undefined, győződjön meg arról, hogy a JOIN kifejezés mindig egyetlen sort állít elő úgy, hogy egy objektumot ad vissza az al lekérdezésből, nem pedig közvetlenül az értéket.

Mimikai illesztés külső referenciaadatokkal

Előfordulhat, hogy gyakran olyan statikus adatokra kell hivatkoznia, amelyek ritkán változnak, például mértékegységek. Ideális, ha nem duplikálja a statikus adatokat a lekérdezés minden eleméhez. A duplikációk elkerülése a tárterületen takarítható meg, és az egyes elemek méretének kisebbre adásával javíthatja az írási teljesítményt. Az albekérdezésekkel statikus referenciaadatok gyűjteményével utánozhatja a belső illesztésű szemantikát.

Vegyük például a következő méréseket:

Név Szorzó Alapegység
ng Nanogramm 1.00E-09 Gramm
µg Mikrogramm 1.00E-06 Gramm
mg Milligramm 1.00E-03 Gramm
g Gramm 1.00E+00 Gramm
kg Kilogramm 1.00E+03 Gramm
Mg Megagram 1.00E+06 Gramm
Gg Gigagram 1.00E+09 Gramm

A következő lekérdezés az adatokkal való összekapcsolás után adja hozzá az egység nevét a kimenethez:

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

Skaláris albekérdezések

A skaláris subquery kifejezés egy olyan részkikérdezés, amely egyetlen értékre van kiértékelve. A skaláris subquery kifejezés értéke az alquery vetületének (SELECT záradékának) értéke. Skaláris subquery kifejezést számos olyan helyen használhat, ahol a skaláris kifejezés érvényes. Használhat például skaláris alkérdezéseket a és WHERE a SELECT záradékok bármely kifejezésében.

A skaláris alkérdezés használata nem mindig segít a lekérdezés optimalizálásában. Ha például egy skaláris alqueryt argumentumként ad át egy rendszernek vagy felhasználó által definiált függvénynek, az nem jár előnyökkel az erőforrásegységek (RU) használatának vagy késésének csökkentésében.

A skaláris albekérdezések további besorolása:

  • Egyszerű kifejezés skaláris alkérdezések
  • Skaláris al lekérdezések összesítése

Egyszerű kifejezés skaláris alkérdezések

Az egyszerű kifejezések skaláris alqueryje egy korrelált alquery, amely olyan SELECT záradékkal rendelkezik, amely nem tartalmaz összesítő kifejezéseket. Ezek az al lekérdezések nem biztosítanak optimalizálási előnyöket, mivel a fordító egyetlen nagyobb egyszerű kifejezéssé alakítja őket. A belső és a külső lekérdezések között nincs korreláció.

Első példaként tekintse meg ezt a triviális lekérdezést.

SELECT
    1 AS a,
    2 AS b

Ezt a lekérdezést átírhatja egy egyszerű kifejezéssel rendelkező skaláris alkérdezés használatával.

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

Mindkét lekérdezés ugyanazt a kimenetet hozza létre.

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

Ez a következő példában szereplő lekérdezés összefűzi az egyedi azonosítót egy előtaggal egyszerű kifejezés skaláris alkérdezésként.

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

Ez a példa egy egyszerű kifejezéssel rendelkező skaláris allekérdezés használatával csak az egyes elemek megfelelő mezőit adja vissza. A lekérdezés minden egyes elemhez kimenetet ad ki, de csak akkor tartalmazza a kivetített mezőt, ha megfelel az al lekérdezés szűrőjének.

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

Skaláris al lekérdezések összesítése

Az aggregátum skaláris alkikérdezések olyan al lekérdezések, amelyek a vetületében vagy szűrőjében egy összesítő függvényt is tartalmazó függvényt ad vissza, amely egyetlen értékre kiértékelhető.

Első példaként vegye figyelembe az alábbi mezőkkel rendelkező elemet.

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

Íme egy al lekérdezés egyetlen aggregátumfüggvény-kifejezéssel a vetületében. Ez a lekérdezés az egyes elemekhez tartozó összes címkét megszámolja.

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

Itt ugyanaz az alkérdezés egy szűrővel.

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

Íme egy másik, több aggregátumfüggvény-kifejezéssel rendelkező al lekérdezés:

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

Végül íme egy lekérdezés, amely a leképezésben és a szűrőben is aggregátumalkotó lekérdezéssel rendelkezik:

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

A lekérdezés írásának optimálisabb módja, ha csatlakozik az al lekérdezéshez, és hivatkozik a subquery aliasra a SELECT és a WHERE záradékban is. Ez a lekérdezés hatékonyabb, mert az al lekérdezést csak a join utasításon belül kell végrehajtania, a leképezésben és a szűrőben nem.

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 kifejezés

Az Azure Cosmos DB for NoSQL lekérdezési motorja támogatja EXISTS a kifejezéseket. Ez a kifejezés egy a NoSQL-hez készült Azure Cosmos DB-be beépített összesítő skaláris al lekérdezés. EXISTS egy subquery kifejezést vesz fel, és visszaadja true , ha az al lekérdezés bármilyen sort visszaad. Ellenkező esetben a értéket adja falsevissza.

Mivel a lekérdezési motor nem tesz különbséget a logikai kifejezések és más skaláris kifejezések között, mindkét SELECTWHERE és záradékban használhatóEXISTS. Ez a viselkedés nem hasonlít a T-SQL-hez, ahol egy logikai kifejezés csak szűrőkre korlátozódik.

Ha az EXISTS al lekérdezés egyetlen értéket ad vissza, akkor a undefinedEXISTS kiértékelése hamis lesz. Vegyük például az alábbi lekérdezést, amely nem ad vissza semmit.

SELECT VALUE
    undefined

Ha a EXISTS kifejezést és az előző lekérdezést használja segéd lekérdezésként, a kifejezés a következőt adja falsevissza: .

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

Ha az előző részkikérdezés ÉRTÉK kulcsszóját nem adja meg, a részkikérdezés egyetlen üres objektummal rendelkező tömbre lesz kiértékelve.

SELECT
    undefined
[
  {}
]

Ekkor a EXISTS kifejezés kiértékeli true a értéket, mivel az objektum ({}) technikailag kilép.

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

A gyakori használati eset egy elem szűrése ARRAY_CONTAINS egy tömbben lévő elem megléte alapján. Ebben az esetben ellenőrizzük, hogy a tags tömb tartalmaz-e "külsőruházat" nevű elemet.

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

Ugyanez a lekérdezés alternatív lehetőségként is használható EXISTS .

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

Emellett csak azt tudja ellenőrizni, ARRAY_CONTAINS hogy egy érték egyenlő-e egy tömb bármely eleméhez. Ha összetettebb szűrőkre van szüksége a tömbtulajdonságokon, használja helyette a parancsot JOIN .

Vegyük ezt a példaelemet egy olyan készletben, amelyben több elem is szerepel, amelyek mindegyike tartalmaz egy tömböt 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
    }
  ]
}

Most vegye figyelembe a következő lekérdezést, amely az type egyes elemek tömbjének és quantityOnHand tulajdonságainak alapján szűr.

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

A gyűjtemény minden eleme esetében a rendszer a tömbelemekkel együtt hajtja végre a keresztterméket. Ez a JOIN művelet lehetővé teszi a tömbben lévő tulajdonságok szűrését. A lekérdezés kérelemegység-felhasználása azonban jelentős. Ha például 1000 elem minden tömbben 100 elemet tartalmazott, akkor a (100 000) értékre 1,000 x 100 bővül.

A használatával EXISTS elkerülheti ezt a költséges termékközi terméket. Ebben a következő példában a lekérdezés az alkérdezés tömbelemeire EXISTS szűr. Ha egy tömbelem megfelel a szűrőnek, akkor kivetíti, és EXISTS igaz értékre értékeli ki.

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

A lekérdezések aliast EXISTS is használhatnak, és hivatkozhatnak az aliasra a leképezésben:

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

TÖMB kifejezés

A ARRAY kifejezéssel tömbként vetítheti ki a lekérdezések eredményeit. Ezt a kifejezést csak a SELECT lekérdezés záradékában használhatja.

Ezekhez a példákhoz tegyük fel, hogy van egy tároló, amely legalább ezt az elemet tartalmazza.

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

Ebben az első példában a kifejezést a záradékban SELECT használjuk.

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

Más albekérdezésekhez hasonlóan a ARRAY kifejezéssel rendelkező szűrők is lehetségesek.

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

A tömbkifejezések a FROM záradék után is jöhetnek a segéd lekérdezésekben.

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