Разработка SQL для запроса таблицы очередей с приоритетом - PullRequest
6 голосов
/ 21 января 2009

Я реализую небольшую очередь, чтобы определить, какой процесс будет запущен первым. Я использую таблицу в базе данных, чтобы сделать это. Вот структура таблицы (я ее макетирую в SQLite):

        "id" INTEGER PRIMARY KEY  AUTOINCREMENT  NOT NULL ,
        "identifier" VARCHAR NOT NULL ,
        "priority_number" INTEGER DEFAULT 15,
        "timestamp" DATETIME DEFAULT CURRENT_TIMESTAMP,
        "description" VARCHAR

Я пытаюсь написать SQL, чтобы указать, какой процесс может быть запущен следующим. Вот некоторые примеры данных:

id  identifier  priority_number timestamp   description
1   test1   15  2009-01-20 17:14:49 NULL
2   test2   15  2009-01-20 17:14:56 NULL
3   test3   10  2009-01-20 17:15:03 NULL
4   test4   15  2009-01-20 17:15:08 NULL
5   test5   15  2009-01-20 17:32:23 NULL
6   test6   14  2009-01-20 17:32:30 NULL
7   test7   7   2009-01-20 17:32:38 NULL
8   test8   20  2009-01-20 17:32:57 NULL
9   test9   7   2009-01-21 13:47:30 NULL
10  test10  15  2009-01-21 13:50:52 NULL

Если я использую этот SQL, я могу получить данные в правильном порядке:

select * from queue_manager order by priority_number, timestamp;

Это даст мне элемент с самым низким номером приоритета (самый важный) в верхней части, а в этих номерах приоритетов самый ранний в очереди (по отметке времени) вверху.

Я мог бы выполнить этот запрос и взять только первую строку, но я бы предпочел сделать это с SQL-запросом, который дал бы мне одну строку процесса, которая находится в верхней части очереди (в примере выше) , строка с id = 7).

Я пытался делать самостоятельные объединения и подзапросы, но у меня должен быть ментальный блок - я просто не могу понять, как это правильно.

Заранее спасибо!

EDIT

Я забыл упомянуть, что я ищу независимый от базы данных запрос. Я делаю это в SQlite, но есть хорошая возможность реализовать это в DB2 или Oracle. Я думал использовать оператор типа «предел 1» в моем запросе, но он отличается в разных движках базы данных.

Ответы [ 7 ]

8 голосов
/ 21 января 2009

Проверьте, работает ли это:

select * from queue_manager where priority_number = 
(select min(priority_number) from queue_manager) and  
timestamp = (select min(timestamp) 
from queue_manager qm2 
where qm2.priority_number = queue_manager.priority_number)
3 голосов
/ 21 января 2009
select * from queue_manager order by priority_number, timestamp LIMIT 1;

Что касается так называемой «независимости базы данных», то это миф для большинства реальных задач. Как правило, вы даже не можете создать схему независимо от базы данных.

2 голосов
/ 21 января 2009

Если вы хотите, чтобы он был «одновременно безопасным» для чего-то вроде InnoDB, сделайте:

1) Добавьте поле in_progress.

2) Отключить AUTOCommit

3) SELECT * FROM queue_manager, где in_progress = 0 порядок по номеру приоритета, отметка времени LIMIT 1 FOR UDPATE;

4) ОБНОВЛЕНИЕ queue_manager SET in_progress = 1, где id = X;

5) COMMIT

6) Сделай работу. Затем удалите строку, когда это сделано к удовлетворению. Иметь «основной процесс» для обработки / повторного делегирования / очистки старых заданий in_progress.

1 голос
/ 21 января 2009

Выбор ограниченного числа строк выполняется по-разному в разных разновидностях SQL, поэтому в зависимости от того, что вы используете, может быть встроенный способ сделать это. Например, в MS SQL Server:

SELECT TOP 1
     identifier,
     priority_number,
     timestamp,
     description
FROM
     dbo.Queue_Manager
ORDER BY
     priority_number,
     timestamp

Для этого в ANSI-совместимом SQL должны работать следующие методы:

    SELECT
         QM1.identifier,
         QM1.priority_number,
         QM1.timestamp,
         QM1.description
    FROM
         Queue_Manager QM1
    LEFT OUTER JOIN Queue_Manager QM2 ON
         QM2.priority_number < QM1.priority_number OR
         (QM2.priority_number = QM1.priority_number AND QM2.timestamp < QM1.timestamp)
    /* If you're concerned that there might be an exact match by priority_number
and timestamp then you might want to add a bit more to the join */
    WHERE
         QM2.identifier IS NULL

Или вы можете попробовать:

SELECT
     QM1.identifier,
     QM1.priority_number,
     QM1.timestamp,
     QM1.description
FROM
     Queue_Manager QM1
INNER JOIN
     (
          SELECT
               priority_number
               MIN(timestamp) AS timestamp,
          FROM
               Queue_Manager
          WHERE
               priority_number = 
                    (
                         SELECT
                              MIN(priority_number)
                         FROM
                              Queue_Manager
                    )
          GROUP BY
               priority_number
     ) SQ1 ON
          SQ1.priority_number = QM1.priority_number AND
          SQ1.timestamp = QM1.timestamp

Ни один из методов не учитывает точные совпадения в обоих значениях priority_number и timestamp, поэтому, если вы считаете, что это возможно (и, возможно, даже если вы этого не сделаете), вам нужно добавить одну или две строки, чтобы перейти на еще один уровень с использованием идентификатора или что-то еще, что гарантирует уникальность. Или просто напишите свой интерфейс, чтобы обработать случайный случай возврата двух строк (возможно, просто проигнорируйте второй - вы получите его в следующий раз).

Протестируйте каждый метод и посмотрите, какой из них лучше для вас.

Кроме того, какого размера вы ожидаете получить очередь? Было бы разумно просто запросить с помощью ORDER BY, и только внешний интерфейс получит первую строку.

1 голос
/ 21 января 2009

Лучший способ сделать это зависит от базы данных; гораздо проще иметь разные процедуры поиска для разных целевых СУБД по сравнению со всеми издержками курсоров или других конструкций.

0 голосов
/ 21 января 2009

Реляционные базы данных не очень хороши в управлении очередями.

Попробуйте взглянуть на MSMQ в мире Windows, ActiveMQ в мире Java или Websphere MQ в мире бизнеса.

Эти продукты делают одну вещь, управляют очередями, но делают это хорошо.

0 голосов
/ 21 января 2009

Прочитайте этот раздел и выберите вариант, который обеспечивает наиболее подходящую совместимость. Вероятно, использование курсоров является единственным более или менее универсально совместимым способом, но имеет некоторое снижение производительности, которое может не оправдать его (профиль!).

...