تعمل النافذة مع "نافذة" أو كيفية استخدام إطار

تحية للجميع. في 26 فبراير ، بدأت الفصول الدراسية في OTUS في مجموعة جديدة في الدورة التدريبية "MS SQL Server Developer" . في هذا الصدد ، أود أن أشارككم منشوري حول وظائف النافذة. بالمناسبة ، في الأسبوع المقبل لا يزال بإمكانك الانضمام إلى المجموعة ؛-).





وظائف النافذة راسخة في ممارستنا ، لكن قلة من الناس يعرفون كيف تعمل إطارات RANGE و ROWS.

ربما لهذا السبب هم أقل شيوعًا إلى حد ما. الغرض من هذه المقالة هو تقديم أمثلة للاستخدام بحيث لا يكون لديك بالتأكيد أسئلة "من هو؟" و "كيفية تطبيقه؟". السؤال "لماذا؟" ستبقى المقالة غير مضاءة.

دعونا نلقي نظرة على ما هو الإطار وكيفية تحقيق تأثير مماثل باستخدام ORDER By في جملة OVER ().

للتوضيح ، سنستخدم جدولًا بسيطًا حتى تتمكن من حساب الأمثلة دون استخدام مترجم. بشكل عام ، أوصي به بشدة - انظر وفكر في ما سيكون نتيجة التنفيذ ، ثم تحقق من نفسك - لذلك ستجد بقع بيضاء في تصور وظائف النافذة ، والتي قد لا تكون واضحة عند قراءة النتائج النهائية.

الطاولة

Create table sales 
(sales_id INT PRIMARY KEY, sales_dt DATETIME2 DEFAULT GETUTCDATE(),  customer_id INT, item_id INT, cnt INT, price_per_item DECIMAL(19,4));

INSERT INTO sales
(sales_id, sales_dt, customer_id, item_id, cnt, price_per_item)
VALUES
(1, '2020-01-10T10:00:00', 100, 200, 2, 30.15),
(2, '2020-01-11T11:00:00', 100, 311, 1, 5.00),
(3, '2020-01-12T14:00:00', 100, 400, 1, 50.00),
(4, '2020-01-12T20:00:00', 100, 311, 5, 5.00),
(5, '2020-01-13T10:00:00', 150, 311, 1, 5.00),
(6, '2020-01-13T11:00:00', 100, 315, 1, 17.00),
(7, '2020-01-14T10:00:00', 150, 200, 2, 30.15),
(8, '2020-01-14T15:00:00', 100, 380, 1, 8.00),
(9, '2020-01-14T18:00:00', 170, 380, 3, 8.00),
(10, '2020-01-15T09:30:00', 100, 311, 1, 5.00),
(11, '2020-01-15T12:45:00', 150, 311, 5, 5.00),
(12, '2020-01-15T21:30:00', 170, 200, 1, 30.15);

لنبدأ بلحظة بسيطة - الاختلافات في دالة SUM مع الفرز أو بدونه

SELECT sales_id, customer_id, count, 
SUM(count) OVER () as total,
SUM(count) OVER (ORDER BY customer_id) AS cum,
SUM(count) OVER (ORDER BY customer_id, sales_id) AS cum_uniq
FROM sales
ORDER BY customer_id, sales_id;



دعونا نلقي نظرة على أشعل النار الأول غير الواضح ، كيف تعتقد كم عدد المطورين سيعتقدون أن نائب الرئيس و cum_uniq متماثلان عند قراءة الكود؟ فكر قليلا؟ ربما ، ولكن لأنه هنا واضح ، وهو واضح جدًا عند قراءة الكود في التطبيق ، وحتى مع عدم التفرد غير الواضح في حقل الفرز.

الآن افتح نافذتنا الرائعة.

النافذة ، أو بالأحرى الإطار نوعان من ROWS و RANGE ، تعرف على ROWS أولاً.
خيارات تقييد الإطار:

  1. كل شيء قبل الصف / النطاق الحالي والقيمة الفعلية للصف الحالي
    بين سابقة غير مسبوقة
    بين سابقة غير مقيدة وحالية
  2. الصف / النطاق الحالي وكل شيء بعد ذلك
    بين الصفوف الحالية والمتابعة غير المقيدة
  3. تحديد عدد الأسطر قبل وبعد تضمين (غير مدعوم لـ RANGE)
    بين N و N التالية
    بين ROWWEN الحاليين و N التالية
    بين N السابقة والسابقة ROW

لكن دعنا نرى الإطار في العمل.

نفتح "النافذة" على الخط الحالي وكل ما سبق ، من أجل وظيفة SUM ، كما ترى ، يتزامن هذا مع تصنيف ASC

SELECT sales_id, customer_id, cnt, 
SUM(cnt) OVER (ORDER BY customer_id, sales_id) AS cum_uniq,
SUM(cnt) OVER (ORDER BY customer_id, sales_id ROWS UNBOUNDED PRECEDING) AS current_and_all_before,
SUM(cnt) OVER (ORDER BY customer_id, sales_id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS current_and_all_before2
FROM sales
ORDER BY customer_id, sales_id;



في الوقت نفسه ، أود أن أذكرك بأن ترتيب الفرز في النافذة (في بند OVER ()) لا يرتبط بترتيب الفرز في الاستعلام نفسه ، في المثال هو نفسه من أجل تبسيط الحساب إذا قررت التحقق من الحساب وفهمك لكيفية عمل الوظيفة

SELECT sales_id, customer_id, cnt, 
SUM(cnt) OVER (ORDER BY customer_id, sales_id) AS cum_uniq,
SUM(cnt) OVER (ORDER BY customer_id, sales_id ROWS UNBOUNDED PRECEDING) AS current_and_all_before,
SUM(cnt) OVER (ORDER BY customer_id, sales_id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS current_and_all_before2
FROM sales
ORDER BY cnt;



الآن دعونا نلقي نظرة على وظيفة الإطار عندما نقوم بتضمين جميع الأسطر اللاحقة.

SELECT sales_id, customer_id, cnt, 
SUM(cnt) OVER (ORDER BY customer_id, sales_id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS current_and_all_frame,
SUM(cnt) OVER (ORDER BY customer_id DESC, sales_id DESC) AS current_and_all_order_desc
FROM sales
ORDER BY customer_id, sales_id;



كما ترى هنا ، يمكنك أيضًا الحصول على نفس النتيجة لوظيفة مجمعة ، إذا كنت تستخدم الفرز العكسي - لكن ROWS أكثر استقرارًا بعض الشيء ، لأنه إذا لم تكن حقول الفرز الخاصة بك فريدة ، فيمكنك الحصول على مفاجأة في شكل نفس القيم.

وأخيرًا ، خيار لم يعد من الممكن تقليده من خلال الأنواع ، عندما نحدد عددًا معينًا من الخطوط التي يجب تضمينها في الإطار

SELECT sales_id, customer_id, cnt, 
SUM(cnt) OVER (ORDER BY customer_id, sales_id ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS before_and_current,
cnt,
SUM(cnt) OVER (ORDER BY customer_id, sales_id ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) AS current_and_1_next,
cnt,
SUM(cnt) OVER (ORDER BY customer_id, sales_id ROWS BETWEEN 2 PRECEDING AND 2 FOLLOWING) AS before2_and_2_next
FROM sales
ORDER BY customer_id, sales_id;



في هذا الخيار ، أشرت بالفعل على وجه التحديد إلى الخطوط الموجودة في النطاق ، وفي رأيي ، أنها أكثر وضوحًا من النتائج.

الفرق بين ROWS و RANGE


الفرق هو أن ROWS تعمل على التوالي و RANGE على نطاق. صحيح ، هذا واضح من الاسم ، لكنه يفسر القليل في الممارسة؟

لنلق نظرة على الصورة (المصدر في أسفل المقالة)





الآن ، إذا نظرنا بعناية ، سيصبح من الواضح أن الصفوف التي لها نفس قيمة معلمة الفرز تسمى النطاق.

كما ذكرنا سابقًا ، يقتصر ROWS على سلسلة ، بينما يلتقط RANGE النطاق الكامل للقيم المطابقة التي تحددها في وظيفة النافذة ORDER BY.

SELECT sales_id, customer_id, cnt, 
SUM(cnt) OVER (ORDER BY customer_id) AS cum_uniq,
cnt,
SUM(cnt) OVER (ORDER BY customer_id ROWS UNBOUNDED PRECEDING) AS current_and_all_before,
customer_id,
cnt,
SUM(cnt) OVER (ORDER BY customer_id RANGE UNBOUNDED PRECEDING) AS current_and_all_before2
FROM sales
ORDER BY 2, sales_id;



ROWS - تعمل دائمًا بخط معين ، حتى لو لم يكن الفرز فريدًا ، لكن RANGE يجمع فقط النقاط في نطاقات مع القيم المطابقة لحقول الفرز. وبهذا المعنى ، فإن الوظيفة تشبه إلى حد كبير سلوك دالة SUM () مع الفرز حسب حقل غير فريد. دعونا نرى مثالاً آخر.

SELECT sales_id, customer_id, price_per_item, cnt, 
SUM(cnt) OVER (ORDER BY customer_id, price_per_item) AS cum_uniq,
cnt,
SUM(cnt) OVER (ORDER BY customer_id, price_per_item ROWS UNBOUNDED PRECEDING) AS current_and_all_before,
customer_id,
cnt,
SUM(cnt) OVER (ORDER BY customer_id, price_per_item RANGE UNBOUNDED PRECEDING) AS current_and_all_before2
FROM sales
ORDER BY 2, price_per_item;




يوجد بالفعل حقلين ونطاق يتم تحديدهما من خلال نطاق بقيم متطابقة لكلا الحقلين.

والخيار هو عندما ندرج في الحساب جميع الأسطر اللاحقة من السطر الحالي ، والتي في حالة دالة SUM تتزامن مع القيمة التي يمكن الحصول عليها باستخدام الفرز العكسي:

SELECT sales_id, customer_id, price_per_item, cnt, 
SUM(cnt) OVER (ORDER BY customer_id DESC, price_per_item DESC) AS cum_uniq,
cnt,
SUM(cnt) OVER (ORDER BY customer_id, price_per_item ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS current_and_all_before,
customer_id,
cnt,
SUM(cnt) OVER (ORDER BY customer_id, price_per_item RANGE BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS current_and_all_before2
FROM sales
ORDER BY 2, price_per_item, sales_id desc;



كما ترى ، تلتقط RANGE مرة أخرى النطاق الكامل للأزواج المتطابقة.

على الرغم من حقيقة أن وظيفة ROWS و RANGE ليست جديدة في كل مرة ، إلا أن هناك تساؤلات حول كيفية استخدامها. آمل أن تكون هذه المقالة قد أضافت فهمًا لكيفية اختلاف ROWS و RANGE ، والآن لن تشك في هذه الحالة التي تحتاج إلى هذا الإطار أو ذاك.

مصدر توضيحي RANGE حول الاختلاف
ووظائف ROWS في النافذة مع Mark SQL Server 2016 ، يكون Mark Tabladillo

في الوقت المناسب للدورة

All Articles