How would you get the Largest / MAX COUNT of Sequential / Contiguous records meeting a Criteria using T-SQL
-
12-11-2019 - |
Question
In a nutshell, I wish to create a query that will help me find the best available seats for a concert type venue much like Ticketmaster.com's "Find Best Available Seats" where the requirements are to find the requested number of seats closest to the stage and the seats must be in sequential order.
Given this example table:
DECLARE @Seats TABLE
(
SectionId INT NOT NULL,
RowId VARCHAR(2) NOT NULL,
SeatId INT NOT NULL,
Priority INT NOT NULL, /* Used to determine closeness to the stage and/or order to search in */
StatusCd CHAR(1) NOT NULL, /* A for Available, H for Held, P for Purchased, etc. */
Cost MONEY NOT NULL
)
And given this script to populate the table:
DECLARE @SectionCounter INT
DECLARE @RowCounter INT
DECLARE @SeatCounter INT
SET @SectionCounter = 1
SET @RowCounter = 1
WHILE @SectionCounter <= 10
BEGIN
WHILE @RowCounter <= 26
BEGIN
SET @SeatCounter = 1
WHILE @SeatCounter <= 26
BEGIN
INSERT INTO @Seats
( SectionId ,
RowId ,
SeatId ,
Priority ,
StatusCd ,
Cost
)
VALUES
( @SectionCounter ,
CHAR(64 + @RowCounter) ,
@SeatCounter ,
1 ,
(
/* Randomly setting certain seats as purchased */
SELECT CASE
WHEN @SeatCounter IN
(
1,2,9,10,
(ROUND(((26 - 1 -1) * RAND() + 1), 0)),
(ROUND(((26 - 1 -1) * RAND() + 1), 0)),
(ROUND(((26 - 1 -1) * RAND() + 1), 0)),
(ROUND(((26 - 1 -1) * RAND() + 1), 0)),
(ROUND(((26 - 1 -1) * RAND() + 1), 0)),
(ROUND(((26 - 1 -1) * RAND() + 1), 0)),
(ROUND(((26 - 1 -1) * RAND() + 1), 0))
)
THEN 'P' ELSE 'A' END) ,
(
SELECT CASE
WHEN @SectionCounter IN (1,2)
THEN 75.00 ELSE 25.00 END
)
)
SET @SeatCounter = @SeatCounter + 1
END
SET @RowCounter = @RowCounter + 1
END
SET @RowCounter = 1
SET @SectionCounter = @SectionCounter + 1
END
What is the best query to find an x number of sequential / contiguous seats?
Below is my current solution, which requires at minimum 3 queries by my application.
For example, if a customer requested 8 of the next best available seats, I would run this query:
/* Get each sections available seat count */
SELECT SectionId,
Priority,
COUNT(SeatId) AS 'Seat Count'
FROM @Seats
WHERE StatusCd = 'A' /* A = Available. */
GROUP BY SectionId, Priority
ORDER BY Priority
Which would produce a result set such as this:
| SectionId | Priority | SeatCount |
|-----------|----------|-----------|
| 1 | 1 | 544 |
| 2 | 2 | 554 |
| 3 | 3 | 552 |
The application would say "Are there 8 seats available with a Priority of 1?" and with the above result set, the answer would be yes, so let's get the available row counts for the corresponding section, which is Section 1. Here's the query for that:
SELECT RowId,
COUNT(SeatId) AS 'Seat Count'
FROM @Seats
WHERE SectionId = 1
AND StatusCd = 'A'
GROUP BY RowId
Which would produce a result set such as this:
| RowId | SeatCount |
|-------|-----------|
| A | 21 |
| B | 18 |
| C | 22 |
The application would look at these results and ask the same question starting with the first row, "Are there 8 seats available in Row A?" With the above results, the answer would be yes, so at that time the application would query for all seats in Row A with this query:
SELECT *
FROM @Seats
WHERE SectionId = 1
AND RowId = 'A'
Which would produce a result set such as this:
| SectionId | RowId | SeatId | Priority | StatusCd | Cost |
|-----------|-------|--------|----------|----------|-------|
| 1 | A | 1 | 1 | P | 75.00 |
| 1 | A | 2 | 1 | P | 75.00 |
| 1 | A | 3 | 1 | A | 75.00 |
| 1 | A | 4 | 1 | A | 75.00 |
| 1 | A | 5 | 1 | A | 75.00 |
At that time, the application would iterate through the results attempting to find 8 seats in a row with a StatusCd of 'A' for available.
I'm sure there is a much more efficient method of querying for sequential records in the database without having to load entire rows and doing it in code.
My best guess for an optimal solution would be to do a self join on the table and doing some kind of incrementing of the SeatId or something along those lines.
Any help or suggestions is greatly appreciated.
No correct solution