مقدمه
"فاکتوری که برای انتخاب برنده مورد استفاده قرار خواهد گرفت عملکرد و کوتاهی نقشه ای اجرا کوئری است."
یک شرکت اتوبوس رانی نیاز به یک گزارش برای رزرم صندلیهای اتوبوس ها دارد.
هدف دادن تعداد مسافر به کوئری و دریافت شماره صندلی های مناسب است. صندلی های خالی باید کنار هم و در یک دریف باشند. تعداد مسافرینی که قصد دارند کنار هم مسافرت کنند حد اکثر در گروههای 4 نفری دسته بندی می شوند.
اپراتور ممکن است اعداد متغیری بین 1 تا 4 به کوئری بدهد و انتظار دارد اولین جای خالی مناسب را به عنوان نتیجه دریافت کند.
جدول و داده ها
داده هایی که ما احتیاج داریم به قرار زیر است:
1. شماره صندلی -nbr
2.شماره ردیف- row_nbr
3.مقداری مبنی بر پر یا خالی بودن صندلی- reserve_mark
اتوبوس های این شرکت دارای 10 ردیف چهارتایی هست که مجموعا 40 نفر ظرفیت دارد.
CREATE TABLE BusBooking
(nbr INTEGER NOT NULL PRIMARY KEY
CONSTRAINT possible_nbrs
CHECK (nbr BETWEEN 1 AND 40),
row_nbr INTEGER NOT NULL
CONSTRAINT possible_row_nbrs
CHECK (row_nbr BETWEEN 1 AND 10),
reserve_mark BIT NOT NULL DEFAULT 0);
/*every row has exactly 4 chairs
NOT EXISTS
(SELECT 1
FROM BusBooking
GROUP BY row_nbr
HAVING COUNT(*) <> 4)
*/
INSERT INTO BusBooking VALUES
(1, 1, 1),
(2, 1, 1),
(3, 1, 0),
(4, 1, 0),
(5, 2, 1),
(6, 2, 1),
(7, 2, 1),
(8, 2, 1),
(09, 3, 1),
(10, 3, 0),
(11, 3, 0),
(12, 3, 0),
(13, 4, 0),
(14, 4, 0),
(15, 4, 0),
(16, 4, 0);
نتیجه مورد نظر با فرض داده های آزمایشی، اگر کاربر مقدار 1 را منظور کرد نتیجه ی مطلوب و خواسته این است:
3
برای مقدار 2:
3
4
برای 3:
10
11
12
برای 4:
13
14
15
16
یعنی اولین صندلی های خالی کنار هم که در یک دریف قرار دارند و به تعداد مورد نیاز نیز هستند به عنوان جواب بایستی انتخاب شود.
بطور پیشفرض امکان کنسل کردن رزور وجود نداره. در غیر اینصورت ممکنه صندلی های خالی در یک دریف موجود باشه ولی مجاور نباشن. شما میتونید دو راه حل ارائه بدین یکی برای برای حالتی که امکان کنسل کردن وجود نداره و یکی که این امکان وجود داره. بدیهی هست که مساله دوم به مراتب پیچیده تر از مساله 1 هست.
راه حل نباید شامل Dynamic SQL باشد
راهنمایی
شما میتونید از راه حل های مناسبی که در چالش اول مطرح شد برای حل مساله ایده بگیرد. همچنین روشی را در زیر شرح می دهم تا بتوانید ایده هایی را از آن برداشت کنید. ولی آن را به هیچ وجه به عنوان پاسخ پیشنهاد نمی کنم.
اگر ما برای هر درخواست یک کوئری جدا گانه تولید کنیم و سپس آنها را با همدیگر اجتماع کنیم و سطرهایی را انتخاب کنیم که مناسب به تعداد درخواست کاربر باشد مساله حل میشه.
با این فرض که کاربر 3 صندلی کنار هم را درخواست کرده است ما می توانیم سطرجاری را با 2 سطر بعدی مرتبط به ردیف فعلی به شرط خالی بودن هر سه صندلی اتصال دهیم و بعد از بدست آوردن کوچکترین شماره صندلی توسط تکنیکی فوق العاده (unpivoting by cross apply) مقادیر بین minimum و minimum + 2 را بدست می آوریم.
طبق توضیحاتی که داده شد راه حل آموزشی همراه با نقشه متنی آن به این شکل در میاید (در اینجا متغیر N به عنوان تعداد درخواستی هست که من با 4 ستش کردم):
DECLARE @N TINYINT = 1;
;WITH N1(m) AS
(SELECT MIN(nbr)
FROM BusBooking
WHERE reserve_mark = 0),
N2(m) AS
(SELECT MIN(A.nbr)
FROM BusBooking A, BusBooking B
WHERE A.nbr + 1 = B.nbr
AND A.row_nbr = B.row_nbr
AND A.reserve_mark = 0
AND B.reserve_mark = 0),
N3(m) AS
(SELECT MIN(A.nbr)
FROM BusBooking A, BusBooking B, BusBooking C
WHERE A.nbr + 1 = B.nbr
AND B.nbr + 1 = C.nbr
AND A.row_nbr = B.row_nbr
AND B.row_nbr = C.row_nbr
AND A.reserve_mark = 0
AND B.reserve_mark = 0
AND C.reserve_mark = 0),
N4(m) AS
(SELECT MIN(A.nbr)
FROM BusBooking A, BusBooking B, BusBooking C, BusBooking D
WHERE A.nbr + 1 = B.nbr
AND B.nbr + 1 = C.nbr
AND C.nbr + 1 = D.nbr
AND A.row_nbr = B.row_nbr
AND B.row_nbr = C.row_nbr
AND C.row_nbr = D.row_nbr
AND A.reserve_mark = 0
AND B.reserve_mark = 0
AND C.reserve_mark = 0
AND D.reserve_mark = 0)
SELECT n
FROM (
SELECT m, 1
FROM N1
UNION ALL
SELECT m, 2
FROM N2
UNION ALL
SELECT m, 3
FROM N3
UNION ALL
SELECT m, 4
FROM N4
)T(m, k)
CROSS APPLY
(
VALUES
(m),(m + 1),(m + 2),(m + 3)
) D(n)
WHERE n < m + @N
AND k = @N;