اليوم، وذلك باستخدام أمثلة بسيطة للغاية، دعونا نرى ما يمكن أن يؤدي إليه في سياق الاستخدام
GROUP/DISTINCT
و LIMIT
معهم.
الآن ، إذا كتبت في الطلب "قم أولاً بتوصيل هذه اللوحات ، ثم تجاهل جميع التكرارات ، يجب أن يكون هناك نسخة واحدة فقط لكل مفتاح" - هذه هي الطريقة التي ستعمل بها بالضبط ، حتى لو لم تكن هناك حاجة للاتصال على الإطلاق.
وأحيانًا تكون محظوظًا وهو "يعمل فقط" ، وأحيانًا يكون له تأثير غير سار على الأداء ، وأحيانًا يعطي تأثيرات غير متوقعة على الإطلاق من وجهة نظر المطور.
حسنًا ، ربما ليس مذهلاً جدًا ، لكن ...
"ثنائي جميل": JOIN + DISTINCT
SELECT DISTINCT
X.*
FROM
X
JOIN
Y
ON Y.fk = X.pk
WHERE
Y.bool_condition;
كيف يكون واضحًا أننا أردنا تحديد مثل هذه السجلات X ، والتي يرتبط Y بها بشرط مستوفى . لقد كتبنا طلبًا من خلال
JOIN
- حصلنا على بعض قيم pk عدة مرات (بالضبط عدد السجلات المطابقة في Y تبين أنها). كيفية إزالة؟ بالطبع DISTINCT
!
إنه أمر "سعيد" بشكل خاص عندما يكون لكل سجل X عدة مئات من سجلات Y المرتبطة ، ثم تتم إزالة التكرارات بشكل بطولي ...
كيف يتم إصلاحها؟ بادئ ذي بدء ، أدرك أنه يمكن تعديل المهمة "لتحديد مثل هذه السجلات X التي يمتلك Y لها واحدًا على الأقل مرتبطًا بحالة تشغيل" - بعد كل شيء ، لا نحتاج إلى أي شيء من السجل Y نفسه.
متداخلة EXISTS
SELECT
*
FROM
X
WHERE
EXISTS(
SELECT
NULL
FROM
Y
WHERE
fk = X.pk AND
bool_condition
LIMIT 1
);
تدرك بعض إصدارات PostgreSQL أنه يكفي العثور على أول سجل متاح في EXISTS ، بينما لا تفعل الإصدارات القديمة. لذلك ، أفضل تحديد
LIMIT 1
الداخل دائمًا EXISTS
.
انضمام LATERAL
SELECT
X.*
FROM
X
, LATERAL (
SELECT
Y.*
FROM
Y
WHERE
fk = X.pk AND
bool_condition
LIMIT 1
) Y
WHERE
Y IS DISTINCT FROM NULL;
يسمح نفس الخيار ، إذا لزم الأمر ، في نفس الوقت بإرجاع بعض البيانات على الفور من سجل Y المرتبط الموجود. تمت مناقشة خيار مشابه في المقالة "PostgreSQL Antipatterns: سجل نادر ينتقل إلى منتصف JOIN" .
"لماذا تدفع أكثر": DISTINCT [ON] + LIMIT 1
ميزة إضافية لتحويلات الاستعلام هذه هي القدرة على تقييد التكرار بسهولة على السجلات إذا كانت هناك حاجة إلى واحد / عدة منها ، كما في الحالة التالية:
SELECT DISTINCT ON(X.pk)
*
FROM
X
JOIN
Y
ON Y.fk = X.pk
LIMIT 1;
نقرأ الآن الطلب ونحاول فهم ما يقترح DBMS القيام به:
- نقوم بتوصيل اللوحات
- فريد من نوعه بواسطة X.pk
- اختر واحدة من السجلات المتبقية
هذا هو ، هل حصلت على ماذا؟ "سجل واحد" من السجلات الفريدة - وإذا أخذت هذا السجل غير المميز ، فهل ستتغير النتيجة بطريقة ما؟ .. "وإذا لم يكن هناك فرق ، فلماذا تدفع أكثر؟"
SELECT
*
FROM
(
SELECT
*
FROM
X
--
LIMIT 1 -- +1 Limit
) X
JOIN
Y
ON Y.fk = X.pk
LIMIT 1;
ونفس الموضوع بالضبط مع
GROUP BY + LIMIT 1
.
"أنا فقط أسأل": مجموعة ضمنية + حد
تمت مصادفة أشياء مماثلة أثناء عمليات التحقق المختلفة من عدم إفراغ اللوحة أو CTE أثناء تنفيذ الطلب:
...
CASE
WHEN (
SELECT
count(*)
FROM
X
LIMIT 1
) = 0 THEN ...
يتم
count/min/max/sum/...
تنفيذ وظائف التجميع ( ) بنجاح على المجموعة بأكملها ، حتى بدون إشارة صريحة GROUP BY
. فقط LIMIT
هم ليسوا ودودين معهم.
قد يفكر المطور "إذا كانت هناك سجلات هناك ، فلن أحتاج إلى مزيد من LIMIT" . لكن لا تفعل! لأن القاعدة هي:
- عد ما تريد في جميع السجلات
- أعط العديد من الأسطر كما تطلب
اعتمادًا على الشروط المستهدفة ، من المناسب إجراء أحد البدائل هنا:
(count + LIMIT 1) = 0
علىNOT EXISTS(LIMIT 1)
(count + LIMIT 1) > 0
علىEXISTS(LIMIT 1)
count >= N
على(SELECT count(*) FROM (... LIMIT N))
"كم يمكن تعليقها بالجرام": DISTINCT + LIMIT
SELECT DISTINCT
pk
FROM
X
LIMIT $1
يمكن للمطور الساذج أن يعتقد بصدق أن الاستعلام سيتوقف بمجرد أن نجد أول قيم مختلفة بقيمة دولار واحد تظهر .
في وقت ما في المستقبل ، قد يعمل هذا وسيعمل بفضل عقدة Index Skip Scan الجديدة ، والتي يجري العمل على تنفيذها حاليًا ، ولكن ليس بعد.
حتى الآن ، في البداية ، سيتم استرداد جميع السجلات ، وجعلها فريدة من نوعها ، وسيتم إرجاع عدد السجلات المطلوبة منها فقط. إنه لأمر محزن بشكل خاص إذا أردنا شيئًا مثل $ 1 = 4 ، وهناك مئات الآلاف من السجلات في الجدول ...
لكي لا نحزن عبثًا ، سنستخدم الاستعلام التعاودي "DISTINCT for the poor" من PostgreSQL Wiki :