Trabalhar fora do SQL para consultar uma tabela fila de prioridade
-
19-08-2019 - |
Pergunta
Estou implementando uma pequena fila de punho que processo começa a correr pela primeira vez. Eu estou usando uma tabela em um banco de dados para fazer isso. Aqui está a estrutura da tabela (estou zombando-lo em SQLite):
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL ,
"identifier" VARCHAR NOT NULL ,
"priority_number" INTEGER DEFAULT 15,
"timestamp" DATETIME DEFAULT CURRENT_TIMESTAMP,
"description" VARCHAR
Eu estou tentando escrever SQL para me dar a linha de que processo pode ser executado em seguida. Aqui está alguns dados de exemplo:
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
Se eu usar este SQL, eu posso obter os dados na ordem correta:
select * from queue_manager order by priority_number, timestamp;
Isso vai me dar o item com o número de prioridade mais baixa (mais importante), na parte superior, e nesses números de prioridade, o mais antigo na fila (por timestamp) no topo.
Eu poderia executar essa consulta, e só tomar a primeira linha, mas eu preferiria fazer isso com uma consulta SQL que me daria a uma linha do processo que está no topo da fila (no exemplo dados acima , a linha com id = 7).
Eu tentei fazer auto junções e subconsultas, mas devo estar tendo um bloqueio mental -. Eu simplesmente não consigo fazer direito
Agradecemos antecipadamente!
Editar
Eu esqueci de mencionar que eu estou olhando para uma consulta independente de banco de dados. Eu estou zombando isso no SQLite, mas há uma boa possibilidade de que irá implementar isso no DB2 ou Oracle. Eu tinha pensado em usar um "limite 1" operador tipo na minha consulta, mas isso é diferente entre diferentes bancos de dados.
Solução
Veja se isso funciona:
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)
Outras dicas
select * from queue_manager order by priority_number, timestamp LIMIT 1;
Como para tal chamada "independência de banco de dados", que é um mito para a maioria das tarefas do mundo real. Como regra geral, você não pode mesmo criar o esquema de forma independente do banco de dados.
Se você quer que seja 'seguro concorrente' em algo como InnoDB fazer:
1) Adicionar um campo 'IN_PROGRESS'.
2) Desligue AutoCommit
3) SELECT * FROM queue_manager onde IN_PROGRESS = 0 ordem por priority_number, timestamp LIMITE PARA UDPATE 1;
4) ACTUALIZAÇÃO queue_manager SET IN_PROGRESS = 1 onde ID = X;
5) COMMIT
6) fazer o trabalho. Em seguida, exclua a linha quando o seu feito à satisfação. Tenha um 'processo principal' alavanca / redelegate / limpeza antigos empregos 'IN_PROGRESS'.
A melhor maneira de fazer isso é banco de dados dependentes; é uma coisa muito mais simples ter diferentes procs de recuperação para o destino diferente SGBDs contra todos a sobrecarga de cursores ou outras construções.
Selecionar um número limitado de linhas é feito de forma diferente em diferentes sabores de SQL, então dependendo do que você está usando pode haver um construído em forma de fazê-lo. Por exemplo, em MS SQL Server:
SELECT TOP 1
identifier,
priority_number,
timestamp,
description
FROM
dbo.Queue_Manager
ORDER BY
priority_number,
timestamp
Para fazer isso no SQL ANSI compatível, os seguintes métodos deve funcionar:
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
Ou você pode tentar:
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
Nem contas de método para correspondências exatas em ambos os priority_number e timestamp, então se você acha que isso é possível (e talvez até mesmo se você não), você vai precisar adicionar uma linha ou duas para ir mais um nível usando o identificador ou outra coisa que garante a exclusividade. Ou simplesmente escrever seu front-end para lidar com o caso ocasional de ficar para trás duas linhas (talvez simplesmente ignorar o segundo - você vai obtê-lo na próxima vez through)
.Test cada método e ver o que funciona melhor para você.
Além disso, o quão grande você espera na fila para obter? Poderia ser razoável apenas consulta com seu ORDER BY e só tem a extremidade dianteira recuperar a primeira linha.
Leia nesta seção e selecione a variante que lhe proporciona a mais compatibilidade adequada. Provavelmente o uso de cursores é a única forma mais ou menos universalmente compatível, mas tem alguma penalidade de desempenho que pode não fazer valer a pena (perfil!).
Bancos de dados relacionais não são grandes em filas de gestão.
Tente olhar para MSMQ no mundo Windows, ActiveMQ no mundo java ou Websphere MQ no mundo dos negócios.
Estes produtos fazem uma única coisa, gerenciar filas, mas eles fazê-lo bem.