الاستعلامات الفرعية في Azure Cosmos DB ل NoSQL
ينطبق على: NoSQL
الاستعلام الفرعي هو استعلام متداخل داخل استعلام آخر داخل Azure Cosmos DB ل NoSQL. يسمى الاستعلام الفرعي أيضا استعلام داخلي أو داخلي SELECT
. عادة ما تسمى العبارة التي تحتوي على استعلام فرعي استعلام خارجي.
أنواع الاستعلامات الفرعية
يوجد نوعان رئيسيان من الاستعلامات الفرعية:
- مترابط: استعلام فرعي يُشير إلى قيم من الاستعلام الخارجي. يتم تقييم الاستعلام الفرعي مرة واحدة لكل صف يعالجه الاستعلام الخارجي.
- غير مترابط:استعلام فرعي مستقل عن الاستعلام الخارجي. يمكن تشغيله من تلقائيًا دون الاعتماد على الاستعلام الخارجي.
إشعار
تدعم قاعدة بيانات Azure Cosmos الاستعلامات الفرعية المترابطة فقط.
يمكن أيضًا تصنيف الاستعلامات الفرعية استنادًا إلى عدد الصفوف والأعمدة التي ترجعها. توجد ثلاثة أنواع:
- الجدول: يُرجع صفوف متعددة وأعمدة متعددة.
- متعدد القيم: يُرجع صفوف متعددة وعمود واحد.
- عددي: يُرجع صف واحد وعمود واحد.
دائما ما ترجع الاستعلامات في Azure Cosmos DB ل NoSQL عمودا واحدا (إما قيمة بسيطة أو عنصر معقد). لذلك، يمكن تطبيق الاستعلامات الفرعية متعددة القيم والعددية فقط. يمكنك استخدام استعلام فرعي متعدد القيم فقط في العبارة FROM
كتعبير علائقي. يمكنك استخدام استعلام فرعي عددي كتعبير عددي في SELECT
عبارة أو WHERE
، أو كتعبير علائقي في العبارة FROM
.
الاستعلامات الفرعية متعددة القيم
ترجع الاستعلامات الفرعية متعددة القيم مجموعة من العناصر وتستخدم دائما ضمن FROM
عبارة . تُستخدم من أجل:
JOIN
تحسين تعبيرات (الانضمام الذاتي).- تقييم التعبيرات باهظة الثمن مرة واحدة، والرجوع عدة مرات.
تحسين تعبيرات الانضمام الذاتي
يمكن أن تحسن JOIN
الاستعلامات الفرعية متعددة القيم التعبيرات عن طريق دفع دالات التقييم بعد كل تعبير select-many بدلا من بعد جميع الصلات المتقاطعة في العبارة WHERE
.
خذ بعين الاعتبار الاستعلام التالي:
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
بالنسبة لهذا الاستعلام، يطابق الفهرس أي عنصر يحتوي على علامة إما name
"الشتاء" أو "الخريف"، واحد quantity
على الأقل بين صفر وعشرة، ومستودع واحد على الأقل حيث backstock
يكون .false
JOIN
يقوم التعبير هنا بتنفيذ المنتج المتقاطع لكافة عناصر tags
الصفائف onHandQuantities
و و warehouseStock
لكل عنصر مطابق قبل تطبيق أي عامل تصفية.
WHERE
ثم تطبق العبارة دالة تقييم عامل التصفية على كل <c, t, n, s>
مجموعة. على سبيل المثال، إذا كان العنصر المطابق يحتوي على عشرة عناصر في كل صفيف من الصفائف الثلاثة، فإنه يتوسع إلى 1 x 10 x 10 x 10
(أي 1000) مجموعة. يُمكن أن يُساعد استخدام الاستعلامات الفرعية هنا على تصفية عناصر الصفيف المرتبطة قبل الانضمام إلى التعبير التالي.
هذا الاستعلام مكافئ إلى الاستعلام السابق ولكنه يستخدم الاستعلامات الفرعية:
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)
افترض أن عنصرا واحدا فقط في صفيف العلامات يطابق عامل التصفية، وهناك خمسة عناصر لكل من صفائف الكمية والمخزون. ثم يتم JOIN
توسيع التعبيرات إلى 1 x 1 x 5 x 5
(25) عنصر، مقابل 1000 عنصر في الاستعلام الأول.
تقييم مرة واحدة، والرجوع عدة مرات
يمكن أن تساعد الاستعلامات الفرعية في تحسين الاستعلامات باستخدام تعبيرات باهظة الثمن؛ مثل: الوظائف المُعرفة من قبل المستخدم (UDFs)، أو سلاسل مُعقدة، أو تعبيرات حسابية. يمكنك استخدام استعلام فرعي مع تعبير JOIN
لتقييم التعبير مرة واحدة ولكن الرجوع إليه عدة مرات.
لنفترض أن لديك UDF التالي (getTotalWithTax
) معرفا.
function getTotalWithTax(subTotal){
return subTotal * 1.25;
}
يقوم الاستعلام التالي بتشغيل UDF getTotalWithTax
عدة مرات:
SELECT VALUE {
subtotal: p.price,
total: udf.getTotalWithTax(p.price)
}
FROM
products p
WHERE
udf.getTotalWithTax(p.price) < 22.25
فيما يلي استعلام مكافئ يقوم بتشغيل تناسق القرص الشامل مرة واحدة فقط:
SELECT VALUE {
subtotal: p.price,
total: totalPrice
}
FROM
products p
JOIN
(SELECT VALUE udf.getTotalWithTax(p.price)) totalPrice
WHERE
totalPrice < 22.25
تلميح
ضع في اعتبارك سلوك التعبيرات عبر JOIN
المنتجات. إذا كان يمكن تقييم تعبير UDF إلى undefined
، يجب التأكد من أن JOIN
التعبير ينتج دائما صفا واحدا عن طريق إرجاع كائن من الاستعلام الفرعي بدلا من القيمة مباشرة.
تقليد الضم إلى البيانات المرجعية الخارجية
قد تحتاج غالبا إلى الرجوع إلى البيانات الثابتة التي نادرا ما تتغير، مثل وحدات القياس. من المثالي عدم تكرار البيانات الثابتة لكل عنصر في استعلام. يؤدي تجنب هذا التكرار إلى حفظ التخزين وتحسين أداء الكتابة عن طريق الاحتفاظ بحجم العنصر الفردي أصغر. يمكنك استخدام استعلام فرعي لمحاكاة دلالات الصلة الداخلية بمجموعة من البيانات المرجعية الثابتة.
على سبيل المثال، ضع في اعتبارك هذه المجموعة من القياسات:
الاسم | مضاعف | الوحدة الأساسية | |
---|---|---|---|
ng |
نانوغرام | 1.00E-09 |
غرام |
µg |
ميكروغرام | 1.00E-06 |
غرام |
mg |
مليغرام | 1.00E-03 |
غرام |
g |
غرام | 1.00E+00 |
غرام |
kg |
كيلوغرام | 1.00E+03 |
غرام |
Mg |
ميجاغرام | 1.00E+06 |
غرام |
Gg |
جيجاغرام | 1.00E+09 |
غرام |
يحاكي الاستعلام التالي الانضمام مع هذه البيانات؛ بحيث يمكنك إضافة اسم الوحدة إلى الإخراج:
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
الاستعلام الفرعي الرقمي
تعبير الاستعلام الفرعي العددي هو استعلام فرعي الذي يقيم قيمة مفردة. قيمة تعبير الاستعلام الفرعي العددي هي قيمة الإسقاط (SELECT
عبارة) الاستعلام الفرعي. يمكنك استخدام تعبير الاستعلام الفرعي التحجيمي في العديد من الأماكن؛ حيث يكون التعبير المقياسي صالحًا. على سبيل المثال، يمكنك استخدام استعلام فرعي عددي في أي تعبير في كل من SELECT
العبارتين و WHERE
.
لا يساعد استخدام استعلام فرعي عددي دائما في تحسين استعلامك. على سبيل المثال، تمرير استعلام فرعي عددي كوسيطة إما إلى نظام أو وظائف معرفة من قبل المستخدم لا يوفر أي فائدة في تقليل استهلاك وحدة الموارد (RU) أو زمن الانتقال.
يمكن أيضًا تصنيف الاستعلامات الفرعية العددية على النحو:
- الاستعلامات الفرعية العددية بسيطة التعبير
- الاستعلامات الفرعية العددية المجمعة
الاستعلامات الفرعية العددية بسيطة التعبير
الاستعلام الفرعي العددي للتعبير البسيط هو استعلام فرعي مرتبط يحتوي SELECT
على عبارة لا تحتوي على أي تعبيرات تجميعية. لا توفر تلك الاستعلامات الفرعية أي فوائد التحسين؛ لأن المحول البرمجي يحولها إلى تعبير واحد بسيط وأكبر. لا يوجد سياق مترابط بين الاستعلامات الداخلية والخارجية.
كمثال أول، ضع في اعتبارك هذا الاستعلام التافه.
SELECT
1 AS a,
2 AS b
يمكنك إعادة كتابة هذا الاستعلام، باستخدام استعلام فرعي عددي بسيط التعبير.
SELECT
(SELECT VALUE 1) AS a,
(SELECT VALUE 2) AS b
ينتج عن كلا الاستعلامين نفس الإخراج.
[
{
"a": 1,
"b": 2
}
]
يقوم استعلام المثال التالي هذا بتسلسل المعرف الفريد ببادئة كتعليق فرعي عددي بسيط التعبير.
SELECT
(SELECT VALUE Concat('ID-', p.id)) AS internalId
FROM
products p
يستخدم هذا المثال استعلام فرعي عددي بسيط التعبير لإرجاع الحقول ذات الصلة لكل عنصر فقط. يقوم الاستعلام بإخراج شيء ما لكل عنصر، ولكنه يتضمن الحقل المتوقع فقط إذا كان يفي بعامل التصفية داخل الاستعلام الفرعي.
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"
}
]
الاستعلامات الفرعية العددية المجمعة
يعد الطلب الفرعي للتحجيم الكلي هو استعلام فرعي له دالة تجميعية في الإسقاط، أو عامل التصفية الذي يقيم إلى قيمة واحدة.
كمثال أول، ضع في اعتبارك عنصرا يحتوي على الحقول التالية.
{
"name": "Snow coat",
"inventory": [
{
"location": "Redmond, WA",
"quantity": 50
},
{
"location": "Seattle, WA",
"quantity": 30
},
{
"location": "Washington, DC",
"quantity": 25
}
]
}
فيما يلي استعلام فرعي مع تعبير دالة تجميعية واحدة في إسقاطه. يحسب هذا الاستعلام كافة العلامات لكل عنصر.
SELECT
p.name,
(SELECT VALUE COUNT(1) FROM i IN p.inventory) AS locationCount
FROM
products p
[
{
"name": "Snow coat",
"locationCount": 3
}
]
فيما يلي نفس الاستعلام الفرعي مع عامل تصفية.
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
}
]
فيما يلي استعلام فرعي آخر مع تعبيرات دالة تجميعية متعددة:
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
}
}
]
وأخيرا، إليك استعلام مع استعلام فرعي مجمع في كل من الإسقاط وعامل التصفية:
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
}
]
طريقة أكثر مثالية لكتابة هذا الاستعلام هو الانضمام إلى الاستعلام الفرعي والإشارة إلى الاسم المستعار للاستعلام الفرعي في كل من جمل SELECT و WHERE. هذا الاستعلام أكثر فعالية؛ لأنك تحتاج إلى تنفيذ الاستعلام الفرعي فقط ضمن عبارة الضم، وليس في كل من الإسقاط والتصفية.
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
يدعم EXISTS
Azure Cosmos DB لمحرك استعلام NoSQL التعبيرات. هذا التعبير هو استعلام فرعي عددي مجمع مضمن في Azure Cosmos DB ل NoSQL. EXISTS
يأخذ تعبير الاستعلام الفرعي ويرجع true
إذا كان الاستعلام الفرعي يرجع أي صفوف. وإلا، فإنه يرجع false
.
نظرا لأن محرك الاستعلام لا يميز بين التعبيرات المنطقية وأي تعبيرات رقمية أخرى، يمكنك استخدام EXISTS
في كل من العبارتين SELECT
و WHERE
. يختلف هذا السلوك عن T-SQL، حيث يقتصر التعبير المنطقي على عوامل التصفية فقط.
EXISTS
إذا كان الاستعلام الفرعي يرجع قيمة واحدة، يتم undefined
EXISTS
تقييمها إلى false. على سبيل المثال، ضع في اعتبارك الاستعلام التالي الذي لا يرجع شيئا.
SELECT VALUE
undefined
إذا كنت تستخدم EXISTS
التعبير والاستعلام السابق كتعلام فرعي، فإن التعبير يرجع false
.
SELECT
EXISTS (SELECT VALUE undefined)
[
{
"$1": false
}
]
إذا تم حذف الكلمة الأساسية VALUE في الاستعلام الفرعي السابق، يتم تقييم الاستعلام الفرعي إلى صفيف مع كائن فارغ واحد.
SELECT
undefined
[
{}
]
عند هذه النقطة، EXISTS
يتم تقييم التعبير إلى true
منذ خروج الكائن ({}
) تقنيا.
SELECT
EXISTS (SELECT undefined)
[
{
"$1": true
}
]
حالة الاستخدام الشائعة ل ARRAY_CONTAINS
هي تصفية عنصر حسب وجود عنصر في صفيف. في هذه الحالة، نتحقق لمعرفة ما إذا كان tags
الصفيف يحتوي على عنصر يسمى "ملابس خارجية".
SELECT
p.name,
p.tags
FROM
products p
WHERE
ARRAY_CONTAINS(p.tags, "outerwear")
يمكن استخدام EXISTS
نفس الاستعلام كخيار بديل.
SELECT
p.name,
p.tags
FROM
products p
WHERE
EXISTS (SELECT VALUE t FROM t IN p.tags WHERE t = "outerwear")
بالإضافة إلى ذلك، ARRAY_CONTAINS
يمكن التحقق فقط مما إذا كانت القيمة مساوية لأي عنصر داخل صفيف. إذا كنت بحاجة إلى عوامل تصفية أكثر تعقيدا على خصائص الصفيف، فاستخدم JOIN
بدلا من ذلك.
ضع في اعتبارك هذا المثال العنصر في مجموعة مع عناصر متعددة يحتوي كل منها على 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
}
]
}
الآن، ضع في اعتبارك الاستعلام التالي الذي يقوم بالتصفية استنادا type
إلى خصائص و quantityOnHand
في الصفيف داخل كل عنصر.
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"
}
]
لكل عنصر من العناصر في المجموعة، يتم تنفيذ منتج متقاطع مع عناصر الصفيف الخاصة به. تجعل هذه JOIN
العملية من الممكن التصفية على الخصائص داخل الصفيف. ومع ذلك، فإن استهلاك RU لهذا الاستعلام مهم. على سبيل المثال، إذا كان هناك 1000 عنصر يحتوي على 100 عنصر في كل صفيف، فإنه يتوسع إلى 1,000 x 100
(أي 100,000) مجموعة.
يمكن أن يساعد استخدام EXISTS
لتجنب هذا المنتج المشترك باهظ الثمن. في هذا المثال التالي، يقوم الاستعلام بتصفية عناصر الصفيف داخل EXISTS
الاستعلام الفرعي. إذا تطابق عنصر صفيف مع عامل التصفية، فعندئذ يمكنك عرضه وتقييمه EXISTS
إلى صحيح.
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"
]
يمكن للاستعلامات أيضا الاسم المستعار EXISTS
والإشارة إلى الاسم المستعار في الإسقاط:
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
يمكنك استخدام ARRAY
التعبير لعرض نتائج استعلام كصفيف. يمكنك استخدام هذا التعبير فقط ضمن SELECT
عبارة الاستعلام.
لهذه الأمثلة، لنفترض أن هناك حاوية مع هذا العنصر على الأقل.
{
"name": "Radimer mountain bike",
"tags": [
{
"name": "road"
},
{
"name": "bike"
},
{
"name": "competitive"
}
]
}
في هذا المثال الأول، يتم استخدام التعبير داخل 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"
]
}
]
كما هو الحال مع الاستعلامات الفرعية الأخرى، تكون عوامل التصفية مع 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"
]
}
]
يمكن أن تأتي تعبيرات الصفيف أيضا بعد العبارة 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"
}
]