Разработка SQL-кода для запроса таблицы очередей приоритетов

StackOverflow https://stackoverflow.com/questions/465692

Вопрос

Я реализую небольшую очередь, чтобы определить, какой процесс запускается первым.Для этого я использую таблицу в базе данных.Вот структура таблицы (я макетирую ее в 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).

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

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

Редактировать

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

Это было полезно?

Решение

Посмотрим, сработает ли это:

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)

Другие советы

select * from queue_manager order by priority_number, timestamp LIMIT 1;

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

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

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

2) Отключите автоматическую фиксацию

3) ВЫБЕРИТЕ * ИЗ queue_manager, где in_progress = 0 порядок по priority_number, ОГРАНИЧЕНИЕ по временной метке 1 ДЛЯ UDPATE;

4) ОБНОВИТЬ queue_manager, УСТАНОВИТЬ in_progress = 1, где id = X;

5) СОВЕРШИТЬ

6) Выполняйте свою работу.Затем удалите строку, когда она будет выполнена удовлетворительно.Попросите "главный процесс" обработать / переопределить / очистить старые задания "in_progress".

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

Выбор ограниченного количества строк выполняется по-разному в разных версиях 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 и попросить интерфейс получить только первую строку.

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top