الأبحاث · جولةُ البرمجة الأولى

CB-3-05 — قواعد بيانات وSQL · SQL · متوسّط

جولةُ البرمجة الأولى · 2026-10-17 · GAIBRA v2.31.03 · واجبُ التشغيل

نتيجةُ الجولة

الترتيبالمتنافسالرمز أمام المحكّمالكلماتالاختباراتOpusGrokالمجموعالصدق (Opus · Grok)
1GAIBRAهـ1,45815/1597.0100.098.512 · 12
2Claude Sonnetج1,16715/1593.091.092.012 · 10
3ChatGPTأ1,14315/1585.094.089.57 · 11
4DeepSeekد1,15415/1578.090.084.05 · 10
5Geminiب84515/1571.081.076.03 · 8

نصُّ السيناريو كما كتبه المولّد

عندنا 3 فروع لمطعم برجر، وكل فرع يرسل مبيعاته كصفوف orders وorder\_items إلى قاعدة مركزية SQLite. المالية تريد استعلاماً واحداً يخرج تقريراً يومياً لكل فرع بالشكل التالي: | branch | day | gross\_sales | discounts | refunds | net\_sales | vat\_15 | top\_item | top\_item\_qty | القواعد: gross\_sales: مجموع unit\_price \* qty لكل البنود (السعر يُخزن شامل الضريبة في order\_items). discounts: مجموع عمود discount\_amount من رأس الطلب. refunds: البنود المرتبطة بمرتجع (order\_items.is\_return = 1) تُطرح من المبيعات الإجمالية وتظهر سطراً مستقلاً في عمود refunds بقيمتها المطلقة. net\_sales = gross\_sales - discounts - refunds، ثم vat\_15 = net\_sales - net\_sales/1.15 مقرّباً إلى رقمين عشريين. top\_item: الصنف الأعلى عدداً (مجموع qty، مع طرح الكميات المرتجعة) في ذلك اليوم والفرع؛ عند التعادل يُختار الاسم الأبجدي أولاً. الطلبات بحالة 'cancelled' تُستبعد كلياً. نريد النتائج حتى يوم معيّن يمرّر كمعامل في الاستعلام، ولكل فرع فقط أيام له فيها مبيعات فعلية. هذا تعريف الجداول والبيانات الأولية (SQLite، أرقام عشرية REAL أو NUMERIC كما تراه مناسباً): sql CREATE TABLE branches (id INTEGER PRIMARY KEY, name TEXT NOT NULL); CREATE TABLE orders (   id INTEGER PRIMARY KEY,   branch\_id INTEGER NOT NULL REFERENCES branches(id),   ordered\_at TEXT NOT NULL, -- 'YYYY-MM-DD HH:MM'   status TEXT NOT NULL, -- 'completed' | 'cancelled'   discount\_amount REAL NOT NULL DEFAULT 0 ); CREATE TABLE order\_items (   id INTEGER PRIMARY KEY,   order\_id INTEGER NOT NULL REFERENCES orders(id),   item\_name TEXT NOT NULL,   unit\_price REAL NOT NULL, -- شامل الضريبة   qty INTEGER NOT NULL,   is\_return INTEGER NOT NULL DEFAULT 0 -- 1 = مرتجع ); البيانات (ألصقها في ملف واحد مع الاستعلام): sql INSERT INTO branches (id, name) VALUES (1,'العليا'), (2,'الروضة'), (3,'النرجس'); \-- العليا 2026-10-01: طلبان مكتملان، أحدهما فيه مرتجع 1 برجر كلاسيك، وخصم 5 \-- الروضة 2026-10-01: طلب مكتمل + طلب ملغى يجب أن لا يظهر \-- النرجس 2026-10-01: طلب واحد مكتمل، تعادل في الكمية بين صنفين \-- (اكتب INSERTs تفصيلية كافية لإبراز كل قاعدة أعلاه: بيانات 8-12 بنود كافية) المطلوب: ملف report.sql واحد قابل للتشغيل بـ sqlite3 report.sql من البداية للنهاية (بناء الجداول + البيانات + الاستعلام النهائي المُعلّم بـ -- REPORT QUERY، مع قيمة يوم المعيّن مثبّتة داخله). لا دوال معرّفة منك خارج SQL القياسي لـ SQLite (أي CTEs و window functions مسموحة).

الردودُ الخمسة كما وصلت المحكّمين

الرمزُ هو ما رآه المحكّمان؛ والاسمُ كُشف بعد التحكيم.

هـGAIBRA98.5 · 1,458 كلمة
\-- report.sql \-- تقرير المبيعات اليومي لكل فرع — SQLite 3.25 فأحدث (دوال النوافذ) \-- التشغيل: sqlite3 :memory: < report.sql .bail on .headers on .mode column PRAGMA foreign\_keys = ON; \-- ───────── المخطّط كما ورد في المواصفة ───────── CREATE TABLE branches (id INTEGER PRIMARY KEY, name TEXT NOT NULL); CREATE TABLE orders ( &#x20; id INTEGER PRIMARY KEY, &#x20; branch\_id INTEGER NOT NULL REFERENCES branches(id), &#x20; ordered\_at TEXT NOT NULL, -- 'YYYY-MM-DD HH:MM' &#x20; status TEXT NOT NULL, -- 'completed' | 'cancelled' &#x20; discount\_amount REAL NOT NULL DEFAULT 0 ); CREATE TABLE order\_items ( &#x20; id INTEGER PRIMARY KEY, &#x20; order\_id INTEGER NOT NULL REFERENCES orders(id), &#x20; item\_name TEXT NOT NULL, &#x20; unit\_price REAL NOT NULL, -- شامل الضريبة &#x20; qty INTEGER NOT NULL, &#x20; is\_return INTEGER NOT NULL DEFAULT 0 -- 1 = مرتجع ); \-- فهارس لمسار التقرير: نطاق التاريخ أوّلاً ثمّ ربط البنود بطلباتها CREATE INDEX idx\_orders\_ordered\_at ON orders (ordered\_at); CREATE INDEX idx\_order\_items\_order\_id ON order\_items (order\_id); \-- ───────── معاملات التقرير (مثبّتة داخل الملف) ───────── \-- صفّ واحد فقط؛ التاريخ غير الصالح (مثل 2026-02-30) يكسر CHECK فيتوقّف الملف بخطأ صريح CREATE TEMP TABLE report\_params ( &#x20; id INTEGER PRIMARY KEY CHECK (id = 1), &#x20; report\_end\_day TEXT NOT NULL CHECK (report\_end\_day = date(report\_end\_day)), &#x20; cancelled\_status TEXT NOT NULL, &#x20; vat\_inclusive\_divisor REAL NOT NULL CHECK (vat\_inclusive\_divisor > 1) ); INSERT INTO report\_params (id, report\_end\_day, cancelled\_status, vat\_inclusive\_divisor) VALUES (1, '2026-10-02', 'cancelled', 1.15); \-- ───────── البيانات ───────── BEGIN TRANSACTION; INSERT INTO branches (id, name) VALUES (1,'العليا'), (2,'الروضة'), (3,'النرجس'); INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES &#x20; -- العليا 2026-10-01: طلبان مكتملان؛ الخصم 5 على طلب فيه بندان (يكشف تكرار الخصم عند الربط) &#x20; (1, 1, '2026-10-01 12:15', 'completed', 5), &#x20; (2, 1, '2026-10-01 19:40', 'completed', 0), &#x20; -- الروضة 2026-10-01: طلب مكتمل + طلب ملغى بخصم وكمية كبيرة يجب ألّا يؤثّرا &#x20; (3, 2, '2026-10-01 13:05', 'completed', 0), &#x20; (4, 2, '2026-10-01 20:30', 'cancelled', 3), &#x20; -- النرجس 2026-10-01: طلب واحد فيه تعادل كمّيّ بين صنفين &#x20; (5, 3, '2026-10-01 14:10', 'completed', 0), &#x20; -- العليا 2026-10-02: يوم ثانٍ داخل الحدّ &#x20; (6, 1, '2026-10-02 13:00', 'completed', 0), &#x20; -- النرجس 2026-10-03: بعد يوم التقرير، يجب ألّا يظهر &#x20; (7, 3, '2026-10-03 12:00', 'completed', 0); INSERT INTO order\_items (id, order\_id, item\_name, unit\_price, qty, is\_return) VALUES &#x20; (1, 1, 'برجر كلاسيك', 25.00, 2, 0), &#x20; (2, 1, 'بطاطس', 9.00, 2, 0), &#x20; (3, 2, 'برجر كلاسيك', 25.00, 1, 0), &#x20; (4, 2, 'برجر دجاج', 22.00, 3, 0), &#x20; (5, 2, 'برجر كلاسيك', 25.00, 1, 1), -- مرتجع: لولا طرحه لتصدّر الكلاسيك بدل الدجاج &#x20; (6, 3, 'برجر كلاسيك', 25.00, 1, 0), &#x20; (7, 3, 'مشروب غازي', 6.00, 2, 0), &#x20; (8, 4, 'برجر دجاج', 22.00, 4, 0), -- داخل طلب ملغى &#x20; (9, 5, 'بطاطس', 9.00, 2, 0), &#x20; (10, 5, 'برجر كلاسيك', 25.00, 2, 0), -- تعادل مع البطاطس عند 2 &#x20; (11, 6, 'برجر كلاسيك', 25.00, 1, 0), &#x20; (12, 7, 'برجر دجاج', 22.00, 1, 0); -- بعد يوم التقرير COMMIT; \-- REPORT QUERY WITH params AS ( &#x20; SELECT report\_end\_day, &#x20; date(report\_end\_day, '+1 day') AS exclusive\_upper\_bound, &#x20; cancelled\_status, &#x20; vat\_inclusive\_divisor &#x20; FROM report\_params &#x20; WHERE id = 1 ), eligible\_orders AS ( &#x20; -- الطلبات غير الملغاة حتى نهاية يوم التقرير شاملاً &#x20; SELECT o.id AS order\_id, &#x20; o.branch\_id, &#x20; date(o.ordered\_at) AS sales\_day, &#x20; o.discount\_amount &#x20; FROM orders AS o &#x20; CROSS JOIN params AS p &#x20; WHERE o.status <> p.cancelled\_status &#x20; AND o.ordered\_at < p.exclusive\_upper\_bound &#x20; AND date(o.ordered\_at) IS NOT NULL ), order\_discounts AS ( &#x20; -- الخصم من رأس الطلب، مجموعاً قبل أيّ ربط بالبنود &#x20; SELECT branch\_id, sales\_day, SUM(discount\_amount) AS discounts &#x20; FROM eligible\_orders &#x20; GROUP BY branch\_id, sales\_day ), item\_lines AS ( &#x20; SELECT eo.branch\_id, &#x20; eo.sales\_day, &#x20; oi.item\_name, &#x20; CASE WHEN oi.is\_return = 1 THEN 0 ELSE oi.unit\_price \* oi.qty END AS sale\_amount, &#x20; CASE WHEN oi.is\_return = 1 THEN ABS(oi.unit\_price \* oi.qty) ELSE 0 END AS refund\_amount, &#x20; CASE WHEN oi.is\_return = 1 THEN -ABS(oi.qty) ELSE oi.qty END AS signed\_qty &#x20; FROM eligible\_orders AS eo &#x20; JOIN order\_items AS oi ON oi.order\_id = eo.order\_id ), item\_totals AS ( &#x20; SELECT branch\_id, sales\_day, &#x20; SUM(sale\_amount) AS gross\_sales, &#x20; SUM(refund\_amount) AS refunds &#x20; FROM item\_lines &#x20; GROUP BY branch\_id, sales\_day ), item\_quantities AS ( &#x20; SELECT branch\_id, sales\_day, item\_name, SUM(signed\_qty) AS net\_qty &#x20; FROM item\_lines &#x20; GROUP BY branch\_id, sales\_day, item\_name ), ranked\_items AS ( &#x20; SELECT branch\_id, sales\_day, item\_name, net\_qty, &#x20; ROW\_NUMBER() OVER ( &#x20; PARTITION BY branch\_id, sales\_day &#x20; ORDER BY net\_qty DESC, item\_name COLLATE BINARY ASC &#x20; ) AS item\_rank &#x20; FROM item\_quantities ), daily\_totals AS ( &#x20; SELECT it.branch\_id, &#x20; it.sales\_day, &#x20; ROUND(it.gross\_sales, 2) AS gross\_sales, &#x20; ROUND(COALESCE(od.discounts, 0), 2) AS discounts, &#x20; ROUND(it.refunds, 2) AS refunds, &#x20; ROUND(it.gross\_sales - COALESCE(od.discounts, 0) - it.refunds, 2) AS net\_sales &#x20; FROM item\_totals AS it &#x20; LEFT JOIN order\_discounts AS od &#x20; ON od.branch\_id = it.branch\_id AND od.sales\_day = it.sales\_day &#x20; WHERE it.gross\_sales > 0 -- أيام فيها مبيعات فعليّة فقط ) SELECT b.name AS branch, &#x20; dt.sales\_day AS day, &#x20; dt.gross\_sales, &#x20; dt.discounts, &#x20; dt.refunds, &#x20; dt.net\_sales, &#x20; ROUND(dt.net\_sales - dt.net\_sales / p.vat\_inclusive\_divisor, 2) AS vat\_15, &#x20; ri.item\_name AS top\_item, &#x20; ri.net\_qty AS top\_item\_qty FROM daily\_totals AS dt JOIN branches AS b ON b.id = dt.branch\_id JOIN ranked\_items AS ri ON ri.branch\_id = dt.branch\_id &#x20; AND ri.sales\_day = dt.sales\_day &#x20; AND ri.item\_rank = 1 CROSS JOIN params AS p ORDER BY b.id, dt.sales\_day; الشرح الاستعلام كلّه مبنيٌّ على أنّ الخصم يعيش في رأس الطلب والمبيعات تعيش في البنود، فلا يصحّ جمعهما من ربطٍ واحد. لو ربطتَ orders بـ order\_items ثمّ جمعتَ discount\_amount لتكرّر خصم الطلب بعدد بنوده: الطلب 1 في العليا عليه خصم 5 وفيه بندان، فيظهر الخصم 10 ويهبط الصافي دون سبب. لهذا يُجمع الخصم في order\_discounts على مستوى الطلب، وتُجمع البنود في item\_totals مستقلّة، ثمّ يلتقيان على الفرع واليوم. هذا أشيع عيبٍ في تقارير من هذا النوع، والبيانات مصمَّمةٌ لتكشفه. قاعدة المرتجعات تحتمل قراءتين، والفارق بينهما في سؤالٍ واحد: هل تدخل بنود المرتجع في gross\_sales أصلاً؟ إن دخلت ثمّ طُرحت في net عادت القيمة كأنّها بيعٌ أُلغي فقط، أو طُرحت مرّتين إن خُزّنت سالبة. القراءة التي تُرضي نصّك وصيغتك معاً هي أن يخرج المرتجع من gross ويظهر في refunds بقيمته المطلقة، ثمّ يُطرح مرّةً واحدة في net\_sales؛ وهي المعتمدة. واستعمال ABS يجعل النتيجة واحدةً سواء أرسل الفرع كمّيّة المرتجع موجبةً أو سالبة. top\_item يُحسب من الكمّيّة الصافية: بنود المرتجع تدخل بإشارة سالبة. في العليا يوم 2026-10-01 كان الكلاسيك سيتصدّر بـ 4 لو أُهمل المرتجع، لكنّ صافيه 2 فيتقدّم برجر دجاج بـ 3. واخترتُ ROW\_NUMBER بدل RANK لأنّ الثانية تُعيد صفّين عند التعادل فتتضاعف صفوف التقرير. في النرجس يتعادل الكلاسيك والبطاطس عند 2، فيُحسم أبجديّاً لصالح «برجر كلاسيك» لأنّ الراء تسبق الطاء. المقارنة BINARY تتبع ترتيب Unicode، وهو يطابق الترتيب الهجائيّ للحروف الأساسيّة لكنّه لا يُراعي صور الهمزة والتاء المربوطة والتشكيل. المعامل محفوظٌ في جدولٍ مؤقّتٍ بصفٍّ واحد، وعليه CHECK يقارن القيمة بـ date() لها. لذلك يُرفض تاريخٌ مثل 2026-02-30 بخطأٍ يوقف الملف عبر .bail بدل أن يُخرج تقريراً فارغاً يُقرأ خطأً على أنّه يومٌ بلا مبيعات. وكلمة «حتى» قُرئت شاملةً لليوم نفسه. أمّا الفلتر ordered\_at < date(day, '+1 day') فهو مقارنة نصّيّة تستفيد من الفهرس وتلتقط اليوم كاملاً حتى 23:59، والتجميع اليوميّ يتمّ عبر date(ordered\_at) الذي يستبعد أيّ طابعٍ زمنيٍّ مشوّه. «أيام لها مبيعات فعليّة» نُفّذت بشرط gross\_sales > 0 دون توليد تقويم. فاليوم الذي ليس فيه إلّا طلبٌ ملغى أو مرتجعٌ وحده لا يظهر، والروضة لا صفّ لها في 2026-10-02. المبالغ تُقرَّب إلى رقمين للعرض، وvat\_15 تُحسب من net\_sales المقرَّب بالصيغة التي حدّدتها حرفيّاً. أبقيتُ REAL كما سمحتَ، والأسعار في البيانات قيمٌ تُمثَّل في الثنائيّ بدقّة. لم أشغّل الملف. ما يلي تتبّعٌ يدويٌّ للبيانات أعلاه عبر الاستعلام: branch day gross\_sales discounts refunds net\_sales vat\_15 top\_item top\_item\_qty العليا 2026-10-01 159 5 25 129 16.83 برجر دجاج 3 العليا 2026-10-02 25 0 0 25 3.26 برجر كلاسيك 1 الروضة 2026-10-01 37 0 0 37 4.83 مشروب غازي 2 النرجس 2026-10-01 68 0 0 68 8.87 برجر كلاسيك 2 لو تسرّب الطلب الملغى لصار gross الروضة 125 ولتصدّرها برجر دجاج بـ 4، ولو تجاوز الفلترُ يومَ التقرير لظهر للنرجس صفٌّ في 2026-10-03. هذان هما الصفّان اللذان يفضحان أيّ خللٍ في الاستبعاد. طريقة الاستخدام الأمر الذي ذكرتَه لن يشغّل الملف كما تتوقّع. مهمّ الأمر sqlite3 report.sql يفتح report.sql بوصفه ملفّ قاعدة بيانات لا سكربتاً، فيفشل برسالة "file is not a database". الأمر الصحيح: sqlite3 :memory: < report.sql التشغيل على :memory: مقصود. لو وُجّه الملف إلى قاعدتكم المركزيّة لفشل عند أوّل CREATE TABLE وتوقّف بفضل .bail، وهذا أسلم من DROP يمسح بياناتٍ حقيقيّة. لتقرير يومٍ آخر غيّر '2026-10-02' في سطر INSERT INTO report\_params وحده. حين تنقل الاستعلام إلى التطبيق خذ ما بعد -- REPORT QUERY فقط، واحذف أوامر النقطة لأنّها خاصّةٌ بواجهة sqlite3. ثمّ استبدل جسم params بـ SELECT :report\_end\_day AS report\_end\_day, date(:report\_end\_day, '+1 day') AS exclusive\_upper\_bound, 'cancelled' AS cancelled\_status, 1.15 AS vat\_inclusive\_divisor مع ربط التاريخ معاملاً، لا بلصقه في النصّ.
جClaude Sonnet92.0 · 1,167 كلمة
سأكتب الملف وأشغّله فعلياً للتأكد من النتائج. الملف report.sql جاهز: يبني الجداول والبيانات ثم ينفّذ الاستعلام المعلَّم بـ -- REPORT QUERY، وقيمة اليوم المعيّن مثبّتة داخله في params (حالياً 2026-10-02). طريقة التشغيل: الأمر sqlite3 report.sql سيفتح الملف كقاعدة بيانات وليس كسكربت. استخدم بدلاً منه: sqlite3 :memory: < report.sql لم يكن sqlite3 CLI متاحاً عندي، فاختبرت الملف عبر مكتبة sqlite3 في بايثون (الإصدار 3.45.1) بعد تجاهل سطرَي .headers و.mode. النتيجة: branch day gross\_sales discounts refunds net\_sales vat\_15 top\_item top\_item\_qty الروضة 2026-10-01 65.0 0.0 0.0 65.0 8.48 دجاج مقرمش 2 الروضة 2026-10-02 97.0 2.5 25.0 69.5 9.07 كولا 3 العليا 2026-10-01 136.0 5.0 23.0 108.0 14.09 برجر كلاسيك 4 العليا 2026-10-02 41.0 0.0 0.0 41.0 5.35 برجر دبل 1 النرجس 2026-10-01 67.0 0.0 0.0 67.0 8.74 برجر كلاسيك 2 ما تُبرزه البيانات: الطلب الملغى (الروضة 10-01، خصمه 10 وبنوده 5 برجر دبل) لا يظهر في أي رقم. مرتجع العليا: طُرحت قيمته (23) من المبيعات الإجمالية وظهرت في refunds، وطُرحت كميته من برجر كلاسيك (2+3−1=4). التعادل: في النرجس تعادل كولا وبرجر كلاسيك بكمية 2، فاختير «برجر كلاسيك» أبجدياً مع أن «كولا» أُدخل أولاً. الخصم لا يتكرر: الروضة 10-02 فيها طلبان، والخصم 2.5 يُحسب مرة واحدة لأنه يُجمع من orders مباشرة وليس عبر الربط مع البنود. الأيام المستبعدة: النرجس 10-02 فيه طلب ملغى فقط فلا صف له، وطلب 10-03 يتجاوز اليوم المعيّن. افتراضات اتخذتها، وأرجو مراجعتها مع المالية: gross\_sales يحسب البنود غير المرتجعة فقط. نصّ القاعدة يقول «لكل البنود»، لكن إدخال بنود المرتجع فيه ثم طرح refunds منه يُلغي أثر المرتجع بدل أن يخصمه. إن أرادوا الجمع على كل البنود حرفياً، فاحذف شرط is\_return = 0 من amounts. ظهور اليوم في التقرير: يظهر اليوم إذا كان فيه طلب غير ملغى له بنود، حتى لو كانت كلها مرتجعات، كي لا يضيع مرتجع يوم بلا مبيعات. التقريب: يُقرَّب كل عمود إلى رقمين، وتُحسب vat\_15 من net\_sales المقرّب. الترتيب الأبجدي: يعتمد على المقارنة الثنائية الافتراضية في SQLite، وهي كافية للأسماء العربية البسيطة. صافي كمية سالب: إذا كان صافي كمية الصنف الأعلى سالباً أو صفراً فسيظهر كما هو في top\_item\_qty. لتمرير اليوم كمعامل بدل القيمة الثابتة، استبدل '2026-10-02' في params بـ :as\_of واربطه من التطبيق. Report Code·SQL  -- ============================================================ \-- تقرير المبيعات اليومي لكل فرع (SQLite) \-- التشغيل: sqlite3 :memory: < report.sql \-- ============================================================ PRAGMA foreign\_keys = ON; DROP TABLE IF EXISTS order\_items; DROP TABLE IF EXISTS orders; DROP TABLE IF EXISTS branches; CREATE TABLE branches (id INTEGER PRIMARY KEY, name TEXT NOT NULL); CREATE TABLE orders ( &#x20; id INTEGER PRIMARY KEY, &#x20; branch\_id INTEGER NOT NULL REFERENCES branches(id), &#x20; ordered\_at TEXT NOT NULL, -- 'YYYY-MM-DD HH:MM' &#x20; status TEXT NOT NULL, -- 'completed' | 'cancelled' &#x20; discount\_amount REAL NOT NULL DEFAULT 0 ); CREATE TABLE order\_items ( &#x20; id INTEGER PRIMARY KEY, &#x20; order\_id INTEGER NOT NULL REFERENCES orders(id), &#x20; item\_name TEXT NOT NULL, &#x20; unit\_price REAL NOT NULL, -- شامل الضريبة &#x20; qty INTEGER NOT NULL, &#x20; is\_return INTEGER NOT NULL DEFAULT 0 -- 1 = مرتجع ); CREATE INDEX idx\_orders\_branch\_day ON orders(branch\_id, ordered\_at); CREATE INDEX idx\_items\_order ON order\_items(order\_id); INSERT INTO branches (id, name) VALUES (1,'العليا'), (2,'الروضة'), (3,'النرجس'); INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES &#x20;(1, 1, '2026-10-01 12:10', 'completed', 5), -- العليا: خصم 5 &#x20;(2, 1, '2026-10-01 19:30', 'completed', 0), -- العليا: فيه مرتجع &#x20;(3, 2, '2026-10-01 13:00', 'completed', 0), &#x20;(4, 2, '2026-10-01 14:00', 'cancelled', 10), -- ملغى: يجب ألا يظهر (بنوده وخصمه) &#x20;(5, 3, '2026-10-01 20:00', 'completed', 0), -- النرجس: تعادل في الكمية &#x20;(6, 1, '2026-10-02 13:00', 'completed', 0), &#x20;(7, 2, '2026-10-02 21:45', 'completed', 2.5), -- الروضة: خصم + مرتجع &#x20;(8, 2, '2026-10-02 23:30', 'completed', 0), -- طلب ثانٍ لنفس اليوم (فحص عدم تكرار الخصم) &#x20;(9, 3, '2026-10-03 11:00', 'completed', 0), -- بعد اليوم المعيّن: يُستبعد &#x20;(10,3, '2026-10-02 18:00', 'cancelled', 0); -- النرجس 10-02 ملغى فقط: لا صف لهذا اليوم INSERT INTO order\_items (id, order\_id, item\_name, unit\_price, qty, is\_return) VALUES &#x20;-- طلب 1 (العليا) &#x20;(1, 1, 'برجر كلاسيك', 23, 2, 0), &#x20;(2, 1, 'بطاطس', 9, 1, 0), &#x20;-- طلب 2 (العليا) مع مرتجع 1 برجر كلاسيك &#x20;(3, 2, 'برجر كلاسيك', 23, 3, 0), &#x20;(4, 2, 'كولا', 6, 2, 0), &#x20;(5, 2, 'برجر كلاسيك', 23, 1, 1), &#x20;-- طلب 3 (الروضة) &#x20;(6, 3, 'دجاج مقرمش', 25, 2, 0), &#x20;(7, 3, 'بطاطس', 9, 1, 0), &#x20;(8, 3, 'كولا', 6, 1, 0), &#x20;-- طلب 4 (ملغى) &#x20;(9, 4, 'برجر دبل', 35, 5, 0), &#x20;-- طلب 5 (النرجس): تعادل كولا = برجر كلاسيك = 2 (كولا أولاً في الإدخال) &#x20;(10, 5, 'كولا', 6, 2, 0), &#x20;(11, 5, 'برجر كلاسيك', 23, 2, 0), &#x20;(12, 5, 'بطاطس', 9, 1, 0), &#x20;-- طلب 6 (العليا 10-02) &#x20;(13, 6, 'برجر دبل', 35, 1, 0), &#x20;(14, 6, 'كولا', 6, 1, 0), &#x20;-- طلب 7 (الروضة 10-02) مع مرتجع دجاج &#x20;(15, 7, 'برجر دبل', 35, 2, 0), &#x20;(16, 7, 'بطاطس', 9, 1, 0), &#x20;(17, 7, 'دجاج مقرمش', 25, 1, 1), &#x20;-- طلب 8 (الروضة 10-02) &#x20;(18, 8, 'كولا', 6, 3, 0), &#x20;-- طلب 9 (بعد اليوم المعيّن) &#x20;(19, 9, 'برجر دبل', 35, 1, 0), &#x20;-- طلب 10 (ملغى) &#x20;(20,10, 'كولا', 6, 4, 0); .headers on .mode column \-- REPORT QUERY WITH params AS ( &#x20; SELECT '2026-10-02' AS as\_of -- اليوم المعيّن (شاملاً) ), valid\_orders AS ( -- استبعاد الملغى كلياً &#x20; SELECT o.id, o.branch\_id, substr(o.ordered\_at, 1, 10) AS day, o.discount\_amount &#x20; FROM orders o, params p &#x20; WHERE o.status <> 'cancelled' &#x20; AND substr(o.ordered\_at, 1, 10) <= p.as\_of ), \-- الخصومات من رأس الطلب: تُجمَّع من orders مباشرة (بدون JOIN على البنود) لتجنّب تكرارها disc AS ( &#x20; SELECT branch\_id, day, SUM(discount\_amount) AS discounts &#x20; FROM valid\_orders &#x20; GROUP BY branch\_id, day ), \-- المبيعات والمرتجعات من البنود amounts AS ( &#x20; SELECT vo.branch\_id, vo.day, &#x20; SUM(CASE WHEN oi.is\_return = 0 THEN oi.unit\_price \* oi.qty ELSE 0 END) AS gross\_sales, &#x20; SUM(CASE WHEN oi.is\_return = 1 THEN ABS(oi.unit\_price \* oi.qty) ELSE 0 END) AS refunds &#x20; FROM valid\_orders vo &#x20; JOIN order\_items oi ON oi.order\_id = vo.id &#x20; GROUP BY vo.branch\_id, vo.day ), \-- صافي كمية كل صنف (المرتجع يُطرح) item\_qty AS ( &#x20; SELECT vo.branch\_id, vo.day, oi.item\_name, &#x20; SUM(CASE WHEN oi.is\_return = 1 THEN -oi.qty ELSE oi.qty END) AS net\_qty &#x20; FROM valid\_orders vo &#x20; JOIN order\_items oi ON oi.order\_id = vo.id &#x20; GROUP BY vo.branch\_id, vo.day, oi.item\_name ), ranked AS ( &#x20; SELECT branch\_id, day, item\_name, net\_qty, &#x20; ROW\_NUMBER() OVER (PARTITION BY branch\_id, day &#x20; ORDER BY net\_qty DESC, item\_name ASC) AS rn &#x20; FROM item\_qty ), calc AS ( &#x20; SELECT a.branch\_id, a.day, &#x20; ROUND(a.gross\_sales, 2) AS gross\_sales, &#x20; ROUND(d.discounts, 2) AS discounts, &#x20; ROUND(a.refunds, 2) AS refunds, &#x20; ROUND(a.gross\_sales - d.discounts - a.refunds, 2) AS net\_sales &#x20; FROM amounts a &#x20; JOIN disc d ON d.branch\_id = a.branch\_id AND d.day = a.day ) SELECT b.name AS branch, &#x20; c.day AS day, &#x20; c.gross\_sales AS gross\_sales, &#x20; c.discounts AS discounts, &#x20; c.refunds AS refunds, &#x20; c.net\_sales AS net\_sales, &#x20; ROUND(c.net\_sales - c.net\_sales / 1.15, 2) AS vat\_15, &#x20; r.item\_name AS top\_item, &#x20; r.net\_qty AS top\_item\_qty FROM calc c JOIN branches b ON b.id = c.branch\_id JOIN ranked r ON r.branch\_id = c.branch\_id AND r.day = c.day AND r.rn = 1 ORDER BY b.name, c.day;
أChatGPT89.5 · 1,143 كلمة
سأبني التقرير باستخدام CTEs وROW\_NUMBER() في SQLite، مع تجميع المبيعات والمرتجعات والخصومات كلٌّ على حدة قبل ربطها؛ لتجنّب مضاعفة الخصومات عند وجود عدة بنود في الطلب. هناك نقطة مهمة في الحساب: بما أن unit\_price شامل ضريبة القيمة المضافة، فسنستخدمه كما هو في المبيعات، ونطرح قيمة المرتجعات مرة واحدة، ثم نحسب صافي المبيعات والضريبة وفق الصيغة المطلوبة. وسأستبعد الطلبات الملغاة من جميع الحسابات، بما فيها الخصومات وترتيب الأصناف. هذا محتوى ملف report.sql كاملاً، جاهز للنسخ والتشغيل. كتابة \-- ========================================================= \-- report.sql \-- Daily branch sales report (SQLite) \-- Fixed report cutoff date: 2026-10-01 \-- ========================================================= PRAGMA foreign\_keys = ON; DROP TABLE IF EXISTS order\_items; DROP TABLE IF EXISTS orders; DROP TABLE IF EXISTS branches; CREATE TABLE branches ( &#x20; id INTEGER PRIMARY KEY, &#x20; name TEXT NOT NULL ); CREATE TABLE orders ( &#x20; id INTEGER PRIMARY KEY, &#x20; branch\_id INTEGER NOT NULL REFERENCES branches(id), &#x20; ordered\_at TEXT NOT NULL, &#x20; status TEXT NOT NULL, &#x20; discount\_amount REAL NOT NULL DEFAULT 0 ); CREATE TABLE order\_items ( &#x20; id INTEGER PRIMARY KEY, &#x20; order\_id INTEGER NOT NULL REFERENCES orders(id), &#x20; item\_name TEXT NOT NULL, &#x20; unit\_price REAL NOT NULL, &#x20; qty INTEGER NOT NULL, &#x20; is\_return INTEGER NOT NULL DEFAULT 0 ); \-- --------------------------------------------------------- \-- Sample data \-- --------------------------------------------------------- INSERT INTO branches (id, name) VALUES &#x20; (1, 'العليا'), &#x20; (2, 'الروضة'), &#x20; (3, 'النرجس'); INSERT INTO orders &#x20; (id, branch\_id, ordered\_at, status, discount\_amount) VALUES &#x20; -- العليا: two completed orders, total discount = 5 &#x20; (101, 1, '2026-10-01 12:00', 'completed', 5), &#x20; (102, 1, '2026-10-01 18:30', 'completed', 0), &#x20; -- الروضة: completed order and cancelled order &#x20; (201, 2, '2026-10-01 13:15', 'completed', 2), &#x20; (202, 2, '2026-10-01 19:00', 'cancelled', 100), &#x20; -- النرجس: two items tied in net quantity &#x20; (301, 3, '2026-10-01 14:00', 'completed', 0), &#x20; -- A later order, excluded by the fixed cutoff date &#x20; (401, 1, '2026-10-02 12:00', 'completed', 0); INSERT INTO order\_items &#x20; (id, order\_id, item\_name, unit\_price, qty, is\_return) VALUES &#x20; -- العليا: order 101 &#x20; (1, 101, 'Classic Burger', 30, 2, 0), &#x20; (2, 101, 'French Fries', 10, 1, 0), &#x20; (3, 101, 'Classic Burger', 30, 1, 1), &#x20; -- العليا: order 102 &#x20; (4, 102, 'Classic Burger', 30, 1, 0), &#x20; (5, 102, 'Cola', 5, 2, 0), &#x20; -- الروضة: completed order &#x20; (6, 201, 'Classic Burger', 25, 2, 0), &#x20; (7, 201, 'Cola', 5, 1, 0), &#x20; -- الروضة: cancelled order; all its data must be ignored &#x20; (8, 202, 'Classic Burger', 25, 10, 0), &#x20; -- النرجس: tie in quantity; alphabetically first wins &#x20; (9, 301, 'Chicken Burger', 20, 2, 0), &#x20; (10, 301, 'Classic Burger', 25, 2, 0), &#x20; -- Later sale: excluded &#x20; (11, 401, 'Classic Burger', 30, 3, 0); \-- ========================================================= \-- REPORT QUERY \-- ========================================================= WITH params AS ( &#x20; SELECT '2026-10-01' AS cutoff\_day ), \-- Include completed orders only, up to the cutoff date. valid\_orders AS ( &#x20; SELECT &#x20; o.id, &#x20; o.branch\_id, &#x20; date(o.ordered\_at) AS sale\_day, &#x20; o.discount\_amount &#x20; FROM orders AS o &#x20; CROSS JOIN params AS p &#x20; WHERE o.status <> 'cancelled' &#x20; AND date(o.ordered\_at) <= p.cutoff\_day ), \-- Aggregate item amounts and quantities by branch and day. daily\_items AS ( &#x20; SELECT &#x20; vo.branch\_id, &#x20; vo.sale\_day, &#x20; -- Gross sales include sold items, excluding returns. &#x20; SUM( &#x20; CASE &#x20; WHEN oi.is\_return = 0 &#x20; THEN oi.unit\_price \* oi.qty &#x20; ELSE 0 &#x20; END &#x20; ) AS gross\_sales, &#x20; -- Refunds are reported as positive amounts. &#x20; SUM( &#x20; CASE &#x20; WHEN oi.is\_return = 1 &#x20; THEN ABS(oi.unit\_price \* oi.qty) &#x20; ELSE 0 &#x20; END &#x20; ) AS refunds &#x20; FROM valid\_orders AS vo &#x20; JOIN order\_items AS oi &#x20; ON oi.order\_id = vo.id &#x20; GROUP BY &#x20; vo.branch\_id, &#x20; vo.sale\_day ), \-- Discounts are aggregated at order level, not item level. daily\_discounts AS ( &#x20; SELECT &#x20; branch\_id, &#x20; sale\_day, &#x20; SUM(discount\_amount) AS discounts &#x20; FROM valid\_orders &#x20; GROUP BY &#x20; branch\_id, &#x20; sale\_day ), \-- Net quantity per item; returned quantities are subtracted. item\_quantities AS ( &#x20; SELECT &#x20; vo.branch\_id, &#x20; vo.sale\_day, &#x20; oi.item\_name, &#x20; SUM( &#x20; CASE &#x20; WHEN oi.is\_return = 1 THEN -oi.qty &#x20; ELSE oi.qty &#x20; END &#x20; ) AS net\_qty &#x20; FROM valid\_orders AS vo &#x20; JOIN order\_items AS oi &#x20; ON oi.order\_id = vo.id &#x20; GROUP BY &#x20; vo.branch\_id, &#x20; vo.sale\_day, &#x20; oi.item\_name ), \-- Highest net quantity first; alphabetical name breaks ties. ranked\_items AS ( &#x20; SELECT &#x20; branch\_id, &#x20; sale\_day, &#x20; item\_name, &#x20; net\_qty, &#x20; ROW\_NUMBER() OVER ( &#x20; PARTITION BY branch\_id, sale\_day &#x20; ORDER BY net\_qty DESC, item\_name COLLATE BINARY ASC &#x20; ) AS rn &#x20; FROM item\_quantities &#x20; WHERE net\_qty > 0 ), \-- Calculate daily totals and the requested VAT amount. daily\_totals AS ( &#x20; SELECT &#x20; di.branch\_id, &#x20; di.sale\_day, &#x20; di.gross\_sales, &#x20; dd.discounts, &#x20; di.refunds, &#x20; di.gross\_sales - dd.discounts - di.refunds AS net\_sales &#x20; FROM daily\_items AS di &#x20; JOIN daily\_discounts AS dd &#x20; ON dd.branch\_id = di.branch\_id &#x20; AND dd.sale\_day = di.sale\_day &#x20; -- Only days with actual positive sales are reported. &#x20; WHERE di.gross\_sales > 0 ) SELECT &#x20; b.name AS branch, &#x20; dt.sale\_day AS day, &#x20; ROUND(dt.gross\_sales, 2) AS gross\_sales, &#x20; ROUND(dt.discounts, 2) AS discounts, &#x20; ROUND(dt.refunds, 2) AS refunds, &#x20; ROUND(dt.net\_sales, 2) AS net\_sales, &#x20; ROUND(dt.net\_sales - dt.net\_sales / 1.15, 2) AS vat\_15, &#x20; ri.item\_name AS top\_item, &#x20; ri.net\_qty AS top\_item\_qty FROM daily\_totals AS dt JOIN branches AS b &#x20; ON b.id = dt.branch\_id LEFT JOIN ranked\_items AS ri &#x20; ON ri.branch\_id = dt.branch\_id &#x20;AND ri.sale\_day = dt.sale\_day &#x20;AND ri.rn = 1 ORDER BY &#x20; dt.sale\_day, &#x20; b.id; التشغيل احفظ المحتوى باسم report.sql، ثم نفّذه باستخدام: sqlite3 < report.sql أو إذا أردت حفظ النتائج في قاعدة بيانات مستقلة: sqlite3 restaurant.db < report.sql ملاحظة: الأمر sqlite3 report.sql وحده يفتح ملفاً باسم report.sql باعتباره قاعدة بيانات، ولا ينفّذ محتواه كسكربت SQL. لذلك استخدم إعادة توجيه الإدخال كما في الأمرين أعلاه. النتيجة المتوقعة branch &#x09; day &#x09; gross\_sales &#x09; discounts &#x09; refunds &#x09; net\_sales &#x09; vat\_15 &#x09; top\_item &#x09; top\_item\_qty العليا &#x09; 2026-10-01 &#x09; 105.00 &#x09; 5.00 &#x09; 30.00 &#x09; 70.00 &#x09; 9.13 &#x09; Classic Burger &#x09; 2 الروضة &#x09; 2026-10-01 &#x09; 55.00 &#x09; 2.00 &#x09; 0.00 &#x09; 53.00 &#x09; 6.91 &#x09; Classic Burger &#x09; 2 النرجس &#x09; 2026-10-01 &#x09; 90.00 &#x09; 0.00 &#x09; 0.00 &#x09; 90.00 &#x09; 11.74 &#x09; Chicken Burger &#x09; 2 نقاط التحقق المرتجعات: تظهر بقيمة موجبة في refunds، وتُطرح مرة واحدة من المبيعات في حساب net\_sales. الخصومات: تُجمع من رأس الطلب مرة واحدة لكل طلب، ولا تتضاعف بعدد البنود. الطلبات الملغاة: لا تؤثر في أي عمود أو في اختيار الصنف الأعلى. الصنف الأعلى: تُطرح الكميات المرتجعة، ثم يُحسم التعادل بترتيب أسماء الأصناف. تاريخ التقرير: القيمة المثبتة هي 2026-10-01، وتُستبعد المبيعات اللاحقة. الأيام دون مبيعات: لا تظهر إذا لم توجد مبيعات فعلية موجبة. تنبيه بشأن تعريف gross\_sales: استبعدت بنود المرتجعات من gross\_sales حتى لا تُحتسب قيمتها مرتين عند طرح refunds. وهذا يجعل الأعمدة متوافقة مع معادلة صافي المبيعات المطلوبة.
دDeepSeek84.0 · 1,154 كلمة
سأبني لك ملف report.sql كاملاً وقابلاً للتشغيل. الفكرة الأساسية: نستبعد الطلبات الملغاة. نحسب gross\_sales من البنود غير المرتجعة. نحسب refunds من البنود المرتجعة (قيمة مطلقة). نطرح المرتجعات من الكميات عند حساب top\_item. نستخدم CTEs وwindow functions فقط. sql \-- report.sql PRAGMA foreign\_keys = ON; DROP TABLE IF EXISTS order\_items; DROP TABLE IF EXISTS orders; DROP TABLE IF EXISTS branches; CREATE TABLE branches (id INTEGER PRIMARY KEY, name TEXT NOT NULL); CREATE TABLE orders ( &#x20; id INTEGER PRIMARY KEY, &#x20; branch\_id INTEGER NOT NULL REFERENCES branches(id), &#x20; ordered\_at TEXT NOT NULL, &#x20; status TEXT NOT NULL, &#x20; discount\_amount REAL NOT NULL DEFAULT 0 ); CREATE TABLE order\_items ( &#x20; id INTEGER PRIMARY KEY, &#x20; order\_id INTEGER NOT NULL REFERENCES orders(id), &#x20; item\_name TEXT NOT NULL, &#x20; unit\_price REAL NOT NULL, &#x20; qty INTEGER NOT NULL, &#x20; is\_return INTEGER NOT NULL DEFAULT 0 ); INSERT INTO branches (id, name) VALUES &#x20; (1,'العليا'), (2,'الروضة'), (3,'النرجس'); \-- ===== العليا 2026-10-01 ===== \-- طلب 1 مكتمل: برجر كلاسيك x2، بطاطس x1 INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES &#x20; (1, 1, '2026-10-01 12:00', 'completed', 5); INSERT INTO order\_items (id, order\_id, item\_name, unit\_price, qty, is\_return) VALUES &#x20; (1, 1, 'برجر كلاسيك', 20.0, 2, 0), &#x20; (2, 1, 'بطاطس', 10.0, 1, 0); \-- طلب 2 مكتمل: برجر كلاسيك x1، مرتجع برجر كلاسيك x1 INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES &#x20; (2, 1, '2026-10-01 14:00', 'completed', 0); INSERT INTO order\_items (id, order\_id, item\_name, unit\_price, qty, is\_return) VALUES &#x20; (3, 2, 'برجر كلاسيك', 20.0, 1, 0), &#x20; (4, 2, 'برجر كلاسيك', 20.0, 1, 1); -- مرتجع \-- ===== الروضة 2026-10-01 ===== \-- طلب 3 مكتمل: برجر دبل x1، مشروب x2 INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES &#x20; (3, 2, '2026-10-01 11:00', 'completed', 3); INSERT INTO order\_items (id, order\_id, item\_name, unit\_price, qty, is\_return) VALUES &#x20; (5, 3, 'برجر دبل', 25.0, 1, 0), &#x20; (6, 3, 'مشروب', 5.0, 2, 0); \-- طلب 4 ملغى (يجب استبعاده كلياً) INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES &#x20; (4, 2, '2026-10-01 13:00', 'cancelled', 10); INSERT INTO order\_items (id, order\_id, item\_name, unit\_price, qty, is\_return) VALUES &#x20; (7, 4, 'برجر دبل', 25.0, 5, 0); \-- ===== النرجس 2026-10-01 ===== \-- طلب 5 مكتمل: تعادل بالكمية بين 'برجر كلاسيك' و 'برجر دبل' (كلاهما x2) \-- الأبجدي أولاً: 'برجر دبل' < 'برجر كلاسيك' حسب ترتيب النص INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES &#x20; (5, 3, '2026-10-01 10:00', 'completed', 2); INSERT INTO order\_items (id, order\_id, item\_name, unit\_price, qty, is\_return) VALUES &#x20; (8, 5, 'برجر كلاسيك', 20.0, 2, 0), &#x20; (9, 5, 'برجر دبل', 25.0, 2, 0), &#x20; (10,5, 'بطاطس', 10.0, 1, 0); \-- ===== REPORT QUERY ===== \-- يوم التقرير (غيّره كما تريد) WITH params AS ( &#x20; SELECT '2026-10-01' AS day ), \-- الطلبات الصالحة (غير ملغاة) حتى اليوم المطلوب valid\_orders AS ( &#x20; SELECT o.id, o.branch\_id, o.ordered\_at, o.discount\_amount &#x20; FROM orders o, params p &#x20; WHERE o.status <> 'cancelled' &#x20; AND date(o.ordered\_at) <= date(p.day) ), \-- المبيعات الإجمالية (بنود غير مرتجعة) gross AS ( &#x20; SELECT vo.branch\_id, date(vo.ordered\_at) AS day, &#x20; SUM(oi.unit\_price \* oi.qty) AS gross\_sales &#x20; FROM valid\_orders vo &#x20; JOIN order\_items oi ON oi.order\_id = vo.id AND oi.is\_return = 0 &#x20; GROUP BY vo.branch\_id, date(vo.ordered\_at) ), \-- المرتجعات (قيمة مطلقة) refunds AS ( &#x20; SELECT vo.branch\_id, date(vo.ordered\_at) AS day, &#x20; SUM(oi.unit\_price \* oi.qty) AS refunds &#x20; FROM valid\_orders vo &#x20; JOIN order\_items oi ON oi.order\_id = vo.id AND oi.is\_return = 1 &#x20; GROUP BY vo.branch\_id, date(vo.ordered\_at) ), \-- الخصومات على مستوى اليوم/الفرع discounts AS ( &#x20; SELECT vo.branch\_id, date(vo.ordered\_at) AS day, &#x20; SUM(vo.discount\_amount) AS discounts &#x20; FROM valid\_orders vo &#x20; GROUP BY vo.branch\_id, date(vo.ordered\_at) ), \-- صافي كميات كل صنف (مع طرح المرتجعات) لحساب top\_item item\_qty AS ( &#x20; SELECT vo.branch\_id, date(vo.ordered\_at) AS day, &#x20; oi.item\_name, &#x20; SUM(CASE WHEN oi.is\_return = 0 THEN oi.qty ELSE -oi.qty END) AS net\_qty &#x20; FROM valid\_orders vo &#x20; JOIN order\_items oi ON oi.order\_id = vo.id &#x20; GROUP BY vo.branch\_id, date(vo.ordered\_at), oi.item\_name ), \-- ترتيب الأصناف: الأعلى كمية ثم الأبجدي ranked\_items AS ( &#x20; SELECT iq.\*, &#x20; ROW\_NUMBER() OVER ( &#x20; PARTITION BY branch\_id, day &#x20; ORDER BY net\_qty DESC, item\_name ASC &#x20; ) AS rn &#x20; FROM item\_qty iq &#x20; WHERE net\_qty > 0 ), top\_items AS ( &#x20; SELECT branch\_id, day, item\_name AS top\_item, net\_qty AS top\_item\_qty &#x20; FROM ranked\_items &#x20; WHERE rn = 1 ), \-- تجميع كل القيم combined AS ( &#x20; SELECT &#x20; b.name AS branch, &#x20; g.day AS day, &#x20; g.gross\_sales, &#x20; COALESCE(d.discounts, 0) AS discounts, &#x20; COALESCE(r.refunds, 0) AS refunds, &#x20; g.gross\_sales - COALESCE(d.discounts, 0) - COALESCE(r.refunds, 0) AS net\_sales &#x20; FROM gross g &#x20; JOIN branches b ON b.id = g.branch\_id &#x20; LEFT JOIN discounts d ON d.branch\_id = g.branch\_id AND d.day = g.day &#x20; LEFT JOIN refunds r ON r.branch\_id = g.branch\_id AND r.day = g.day ) SELECT &#x20; c.branch, &#x20; c.day, &#x20; ROUND(c.gross\_sales, 2) AS gross\_sales, &#x20; ROUND(c.discounts, 2) AS discounts, &#x20; ROUND(c.refunds, 2) AS refunds, &#x20; ROUND(c.net\_sales, 2) AS net\_sales, &#x20; ROUND(c.net\_sales - c.net\_sales / 1.15, 2) AS vat\_15, &#x20; t.top\_item, &#x20; t.top\_item\_qty FROM combined c LEFT JOIN top\_items t ON t.branch\_id = ( &#x20; SELECT id FROM branches WHERE name = c.branch ) AND t.day = c.day ORDER BY c.branch, c.day; ملاحظات مهمة على التصميم: gross\_sales يُحسب فقط من البنود غير المرتجعة (is\_return = 0)، والمرتجع لا يُخصم من gross\_sales بل يظهر منفصلاً في refunds كما طلبت القواعد. refunds هي القيمة المطلقة لبنود المرتجع (مجموع unit\_price \* qty للبنود ذات is\_return = 1). net\_sales = gross\_sales - discounts - refunds تماماً كما في القواعد. vat\_15 = net\_sales - net\_sales/1.15 مقرّباً لرقمين عشريين (لأن الأسعار شاملة الضريبة). top\_item: نحسب صافي الكمية لكل صنف (مبيعات − مرتجعات)، نستبعد الأصناف التي صافيها ≤ 0، ثم نرتب بـ ROW\_NUMBER() حسب net\_qty DESC, item\_name ASC لضمان اختيار الاسم الأبجدي عند التعادل. استبعاد الملغاة يتم في valid\_orders قبل كل التجميعات. حتى يوم معيّن: نستخدم date(ordered\_at) <= date(params.day)، والدالة date() على النص 'YYYY-MM-DD HH:MM' تُرجع التاريخ فقط. كل فرع فقط أيام له فيها مبيعات فعلية: لأن gross مبنية على JOIN مع البنود غير المرتجعة، فالفرع/اليوم بلا مبيعات فعلية لن يظهر. لاحظ أن يوماً فيه مرتجعات فقط (بلا مبيعات) لن يظهر أيضاً — وهذا متسق مع "أيام له فيها مبيعات فعلية". التعادل في النرجس: 'برجر دبل' و 'برجر كلاسيك' كلاهما بكمية 2؛ الترتيب الأبجدي حسب item\_name ASC سيختار 'برجر دبل' (لأن مقارنة النصوص تتم حسب ترميز Unicode، و'د' < 'ك' في هذه الحالة). إن أردت ترتيباً عربياً مخصصاً يمكن تعديله، لكن القاعدة المطلوبة هي "الاسم الأبجدي أولاً" وهذا ما يطبقه ORDER BY item\_name ASC. للتشغيل: bash sqlite3 :memory: < report.sql \# أو sqlite3 test.db < report.sql الناتج المتوقع (مبسّط): branch day gross\_sales discounts refunds net\_sales vat\_15 top\_item top\_item\_qty الروضة 2026-10-01 35 3 0 32 4.17 مشروب 2 العليا 2026-10-01 50 5 20 25 3.26 برجر كلاسيك 2 النرجس 2026-10-01 100 2 0 98 12.78 برجر دبل 2 ملاحظة: ترتيب الصفوف أبجدي حسب اسم الفرع بسبب ORDER BY c.branch. لو أردت ترتيباً حسب branch\_id يمكن تعديله بسهولة.
بGemini76.0 · 845 كلمة
إليك ملف report.sql كاملاً يحتوي على إنشاء الجداول، بيانات الاختبار الشاملة للحالات، والاستعلام النهائي باستخدام Common Table Expressions (CTEs) و Window Functions الخاصة بـ SQLite:SQL-- ========================================== \-- 1. إنشاء الجداول \-- ========================================== DROP TABLE IF EXISTS order\_items; DROP TABLE IF EXISTS orders; DROP TABLE IF EXISTS branches; CREATE TABLE branches ( &#x20; id INTEGER PRIMARY KEY, &#x20; name TEXT NOT NULL ); CREATE TABLE orders ( &#x20; id INTEGER PRIMARY KEY, &#x20; branch\_id INTEGER NOT NULL REFERENCES branches(id), &#x20; ordered\_at TEXT NOT NULL, -- 'YYYY-MM-DD HH:MM' &#x20; status TEXT NOT NULL, -- 'completed' | 'cancelled' &#x20; discount\_amount REAL NOT NULL DEFAULT 0 ); CREATE TABLE order\_items ( &#x20; id INTEGER PRIMARY KEY, &#x20; order\_id INTEGER NOT NULL REFERENCES orders(id), &#x20; item\_name TEXT NOT NULL, &#x20; unit\_price REAL NOT NULL, -- شامل الضريبة &#x20; qty INTEGER NOT NULL, &#x20; is\_return INTEGER NOT NULL DEFAULT 0 -- 1 = مرتجع ); \-- ========================================== \-- 2. إدراج البيانات التوضيحية \-- ========================================== INSERT INTO branches (id, name) VALUES &#x20; (1, 'العليا'), &#x20; (2, 'الروضة'), &#x20; (3, 'النرجس'); \-- ----- فرع العليا (2026-10-01) ----- \-- طلب 1: مكتمل، خصم 5 ريال، فيه مرتجع برجر كلاسيك (1) INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES (1, 1, '2026-10-01 12:30', 'completed', 5.00); INSERT INTO order\_items (order\_id, item\_name, unit\_price, qty, is\_return) VALUES &#x20; (1, 'برجر كلاسيك', 30.00, 3, 0), -- مباع: 3 &#x20; (1, 'برجر كلاسيك', 30.00, 1, 1), -- مرتجع: 1 (الصافي = 2) &#x20; (1, 'بطاطس', 10.00, 2, 0); -- مباع: 2 \-- طلب 2: مكتمل بدون خصم INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES (2, 1, '2026-10-01 14:15', 'completed', 0.00); INSERT INTO order\_items (order\_id, item\_name, unit\_price, qty, is\_return) VALUES &#x20; (2, 'برجر دبل', 40.00, 1, 0); \-- ----- فرع الروضة (2026-10-01) ----- \-- طلب 3: مكتمل INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES (3, 2, '2026-10-01 13:00', 'completed', 0.00); INSERT INTO order\_items (order\_id, item\_name, unit\_price, qty, is\_return) VALUES &#x20; (3, 'برجر كلاسيك', 30.00, 2, 0); \-- طلب 4: ملغى (يجب استبعاده كلياً وعدم احتساب مبيعاته) INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES (4, 2, '2026-10-01 15:00', 'cancelled', 10.00); INSERT INTO order\_items (order\_id, item\_name, unit\_price, qty, is\_return) VALUES &#x20; (4, 'برجر دبل', 40.00, 5, 0); \-- ----- فرع النرجس (2026-10-01) ----- \-- طلب 5: مكتمل، تعادل في صافي الكمية (2 لكل من 'برجر دبل' و 'بطاطس') \-- يجب أن يختار الاستعلام 'بطاطس' أبجدياً INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES (5, 3, '2026-10-01 18:00', 'completed', 0.00); INSERT INTO order\_items (order\_id, item\_name, unit\_price, qty, is\_return) VALUES &#x20; (5, 'برجر دبل', 40.00, 2, 0), &#x20; (5, 'بطاطس', 10.00, 2, 0); \-- ----- يوم لاحق خارج تاريخ المعامل (2026-10-02) ----- INSERT INTO orders (id, branch\_id, ordered\_at, status, discount\_amount) VALUES (6, 1, '2026-10-02 20:00', 'completed', 0.00); INSERT INTO order\_items (order\_id, item\_name, unit\_price, qty, is\_return) VALUES &#x20; (6, 'برجر كلاسيك', 30.00, 1, 0); \-- ========================================== \-- REPORT QUERY \-- ========================================== WITH target\_date AS ( &#x20; -- تغيير تاريخ المعامل المراد تصفية التقرير عنده &#x20; SELECT '2026-10-01' AS cutoff\_day ), valid\_orders AS ( &#x20; SELECT &#x20; o.id AS order\_id, &#x20; o.branch\_id, &#x20; DATE(o.ordered\_at) AS day, &#x20; o.discount\_amount &#x20; FROM orders o &#x20; JOIN target\_date td ON DATE(o.ordered\_at) <= td.cutoff\_day &#x20; WHERE o.status != 'cancelled' ), daily\_order\_totals AS ( &#x20; SELECT &#x20; vo.branch\_id, &#x20; vo.day, &#x20; SUM(vo.discount\_amount) AS total\_discounts &#x20; FROM ( &#x20; SELECT DISTINCT order\_id, branch\_id, day, discount\_amount &#x20; FROM valid\_orders &#x20; ) vo &#x20; GROUP BY vo.branch\_id, vo.day ), daily\_item\_sales AS ( &#x20; SELECT &#x20; vo.branch\_id, &#x20; vo.day, &#x20; oi.item\_name, &#x20; SUM(CASE WHEN oi.is\_return = 0 THEN oi.unit\_price \* oi.qty ELSE 0 END) AS gross\_sales, &#x20; SUM(CASE WHEN oi.is\_return = 1 THEN oi.unit\_price \* oi.qty ELSE 0 END) AS refunds, &#x20; SUM(CASE WHEN oi.is\_return = 0 THEN oi.qty ELSE -oi.qty END) AS net\_qty &#x20; FROM valid\_orders vo &#x20; JOIN order\_items oi ON vo.order\_id = oi.order\_id &#x20; GROUP BY vo.branch\_id, vo.day, oi.item\_name ), ranked\_items AS ( &#x20; SELECT &#x20; dis.branch\_id, &#x20; dis.day, &#x20; dis.item\_name, &#x20; dis.net\_qty, &#x20; ROW\_NUMBER() OVER ( &#x20; PARTITION BY dis.branch\_id, dis.day &#x20; ORDER BY dis.net\_qty DESC, dis.item\_name ASC &#x20; ) AS rn &#x20; FROM daily\_item\_sales dis ), branch\_daily\_summary AS ( &#x20; SELECT &#x20; b.name AS branch, &#x20; dis.day, &#x20; SUM(dis.gross\_sales) AS gross\_sales, &#x20; dot.total\_discounts AS discounts, &#x20; SUM(dis.refunds) AS refunds &#x20; FROM daily\_item\_sales dis &#x20; JOIN branches b ON dis.branch\_id = b.id &#x20; JOIN daily\_order\_totals dot ON dis.branch\_id = dot.branch\_id AND dis.day = dot.day &#x20; GROUP BY b.name, dis.day, dot.total\_discounts ) SELECT &#x20; s.branch, &#x20; s.day, &#x20; ROUND(s.gross\_sales, 2) AS gross\_sales, &#x20; ROUND(s.discounts, 2) AS discounts, &#x20; ROUND(s.refunds, 2) AS refunds, &#x20; ROUND(s.gross\_sales - s.discounts - s.refunds, 2) AS net\_sales, &#x20; ROUND( &#x20; (s.gross\_sales - s.discounts - s.refunds) - &#x20; ((s.gross\_sales - s.discounts - s.refunds) / 1.15), &#x20; 2 &#x20; ) AS vat\_15, &#x20; r.item\_name AS top\_item, &#x20; r.net\_qty AS top\_item\_qty FROM branch\_daily\_summary s JOIN ranked\_items r ON s.branch = (SELECT name FROM branches WHERE id = r.branch\_id) &#x20; AND s.day = r.day &#x20; AND r.rn = 1 ORDER BY s.day ASC, s.branch ASC; طريقة التشغيل عبر السطر البرمجي:Bashsqlite3 report.sql نتيجة الاستعلام بالتمرير على البيانات المرفقة:branchdaygross\_salesdiscountsrefundsnet\_salesvat\_15top\_itemtop\_item\_qtyالعليا2026-10-01160.05.030.0125.016.3 AS (125 - 125/1.15 = 16.304...)برجر كلاسيك2النرجس2026-10-01100.00.00.0100.013.04بطاطس2الروضة2026-10-0160.00.00.060.07.83برجر كلاسيك2