Как бы вы получили наибольшее / максимальное количество последовательных / смежных записей, отвечающих критериям с использованием T-SQL - PullRequest
1 голос
/ 24 февраля 2012

Короче говоря, я хочу создать запрос, который поможет мне найти наилучшие доступные места для концертного типа, очень похожего на «Найти лучшие доступные места» на Ticketmaster.com, где необходимо найти запрошенное количество мест, ближайших на сцену и места должны быть в последовательном порядке.

Учитывая этот пример таблицы:

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
)

И учитывая этот скрипт для заполнения таблицы:

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

Каков наилучший запрос для поиска x числа последовательных / смежных мест?

Ниже приведено мое текущее решение, которое требует как минимум 3 запроса от моего приложения.

Например, если клиент запросил 8 из следующих лучших доступных мест, я бы запустил этот запрос:

/* 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

Что даст такой набор результатов, как этот:

| SectionId | Priority | SeatCount |
|-----------|----------|-----------|
| 1         | 1        | 544       |
| 2         | 2        | 554       |
| 3         | 3        | 552       |

В приложении будет указано: «Есть ли 8 мест с приоритетом 1?» и с указанным выше набором результатов ответ будет положительным, так что давайте посчитаем количество доступных строк для соответствующего раздела, который является Разделом 1. Вот запрос для этого:

SELECT  RowId,
        COUNT(SeatId) AS 'Seat Count'
FROM    @Seats
WHERE   SectionId = 1
        AND StatusCd = 'A'
GROUP BY RowId

Что даст такой набор результатов, как этот:

| RowId | SeatCount |
|-------|-----------|
| A     | 21        |
| B     | 18        |
| C     | 22        |

Приложение будет смотреть на эти результаты и задавать тот же вопрос, начиная с первого ряда: «Есть ли 8 мест в строке A?» При указанных выше результатах ответ будет положительным, поэтому в это время приложение будет запрашивать все места в строке A с помощью этого запроса:

SELECT  *
FROM    @Seats
WHERE   SectionId = 1
AND     RowId = 'A'

Что даст такой набор результатов, как этот:

| 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 |

В это время приложение будет перебирать результаты, пытаясь найти 8 мест подряд с StatusCd 'A' для доступных.

Я уверен, что существует гораздо более эффективный метод запроса последовательных записей в базе данных без необходимости загружать целые строки и делать это в коде.

Моим лучшим предположением для оптимального решения было бы сделать самостоятельное соединение на столе и выполнить какое-то приращение SeatId или что-то в этом роде.

Любая помощь или предложения приветствуются.

1 Ответ

1 голос
/ 24 февраля 2012

Это должно помочь вам начать. Вы были на правильном пути, насколько я присоединился, это другой путь к этому.

Это даст вам первые 8 доступных мест с тем же приоритетом, раздел, строка со статусом «А» в порядке приоритета по приоритету, разделу, строке. Поправь меня, если я что-то не так понял.

DECLARE @number_seats AS INTEGER = 8;

WITH T1 AS (
    SELECT S.*,
           SeatId - ROW_NUMBER() OVER(PARTITION BY Priority, SectionId, RowId, StatusCd ORDER BY SeatId) AS grp
    FROM #seats AS S
),

T2 AS (
SELECT Priority    AS Priority,
       SectionId   AS Section,
       RowId       AS RowId,
       StatusCd    AS StatusCd, 
       MIN(SeatId) AS StartingSeat,
       MAX(SeatId) AS EndingSeat,
       COUNT(*)    AS Seats       
FROM T1
GROUP BY Priority, SectionId, RowId, StatusCd, grp
),

T3 AS (
    SELECT TOP 1 *
    FROM T2
    WHERE T2.Seats >= @number_seats and StatusCd = 'A'
    ORDER BY Priority, Section, RowId, StartingSeat
)
SELECT S.*
FROM T3 JOIN #seats AS S ON 
(
    T3.Priority = S.Priority AND
    T3.Section  = S.SectionId AND
    T3.RowId    = S.RowId AND
    S.SeatId BETWEEN T3.StartingSeat AND T3.StartingSeat + @number_seats - 1
)
ORDER BY Priority, Section, RowId, StartingSeat

Результаты:

SectionId   RowId SeatId      Priority    StatusCd Cost
----------- ----- ----------- ----------- -------- ---------------------
1           A     11          1           A        75.00
1           A     12          1           A        75.00
1           A     13          1           A        75.00
1           A     14          1           A        75.00
1           A     15          1           A        75.00
1           A     16          1           A        75.00
1           A     17          1           A        75.00
1           A     18          1           A        75.00
...