Puzzle de consulta MySQL - Encontrando o que teria sido a data mais recente
Pergunta
Eu olhei por toda parte e ainda não encontrei uma maneira inteligente de lidar com isso, embora tenha certeza de que é possível:
Uma tabela de dados históricos possui informações trimestrais:
CREATE TABLE Quarterly (
unique_ID INT UNSIGNED NOT NULL,
date_posted DATE NOT NULL,
datasource TINYINT UNSIGNED NOT NULL,
data FLOAT NOT NULL,
PRIMARY KEY (unique_ID));
Outra tabela de dados históricos (que é muito grande) contém informações diárias:
CREATE TABLE Daily (
unique_ID INT UNSIGNED NOT NULL,
date_posted DATE NOT NULL,
datasource TINYINT UNSIGNED NOT NULL,
data FLOAT NOT NULL,
qtr_ID INT UNSIGNED,
PRIMARY KEY (unique_ID));
O campo QTR_ID não faz parte da alimentação dos dados diários que preencheram o banco de dados - em vez disso, preciso preencher retroativamente o campo QTR_ID na tabela diária com o trimestralmente.unique_id linha ID, usando o que teria sido os dados trimestrais mais recentes sobre Isso diariamente.date_posted para essa fonte de dados.
Por exemplo, se os dados trimestrais forem
101 2009-03-31 1 4.5
102 2009-06-30 1 4.4
103 2009-03-31 2 7.6
104 2009-06-30 2 7.7
105 2009-09-30 1 4.7
e os dados diários são
1001 2009-07-14 1 3.5 ??
1002 2009-07-15 1 3.4 &&
1003 2009-07-14 2 2.3 ^^
Então gostaríamos do? O campo QTR_ID a ser atribuído '102' como o trimestre mais recente para essa fonte de dados nessa data, e && também seria '102' e ^^ seria '104'.
Os desafios incluem que ambas as tabelas (particularmente a tabela diária) são realmente muito grandes, elas não podem ser normalizadas para se livrar das datas repetitivas ou otimizadas de outra forma e, para certas entradas diárias, não há entrada trimestral anterior.
Eu tentei uma variedade de junções, usando o datediff (onde o desafio é encontrar o valor mínimo do datediff maior que o zero) e outras tentativas, mas nada está funcionando para mim - geralmente minha sintaxe está quebrando em algum lugar. Quaisquer idéias são bem -vindas - vou executar quaisquer idéias ou conceitos básicos e relatar de volta.
Solução
Basta subconsciar o trimestre ID usando algo como:
(
SELECT unique_ID
FROM Quarterly
WHERE
datasource = ?
AND date_posted >= ?
ORDER BY
unique_ID ASC
LIMIT 1
)
Obviamente, isso provavelmente não lhe dará o melhor desempenho e pressupõe que as datas sejam adicionadas a sequencialmente trimestralmente (caso contrário order by date_posted
). No entanto, deve resolver seu problema.
Você usaria esta subconsulta em seu INSERT
ou UPDATE
declarações como o valor do seu qtr_ID
campo para o seu Daily
tabela.
Outras dicas
O seguinte parece funcionar exatamente como pretendido, mas certamente é feio (com três chamadas para o mesmo datediff !!), talvez vendo uma consulta de trabalho que alguém possa reduzi -lo ou melhorá -lo:
UPDATE Daily SET qtr_ID = (select unique_ID from Quarterly
WHERE Quarterly.datasource = Daily.datasource AND
DATEDIFF(Daily.date_posted, Quarterly.date_posted) =
(SELECT MIN(DATEDIFF(Daily.date_posted, Quarterly.date_posted)) from Quarterly
WHERE Quarterly.datasource = Daily.datasource AND
DATEDIFF(Daily.date_posted, Quarterly.date_posted) > 0));
Depois de mais trabalho nessa consulta, acabei com enormes melhorias de desempenho em relação ao conceito original. A melhoria mais importante foi criar índices nas tabelas diárias e trimestrais - diariamente, criei índices (DataSource, date_posted) e (date_posted, DataSource) usando o Btree e ON (DataSource) usando hash e, trimestralmente, fiz o mesmo coisa. Isso é um exagero, mas garantiu que eu tivesse a opção de que o mecanismo de consulta pudesse usar. Isso reduziu o tempo de consulta para menos de 1% do que tinha sido. (!!)
Em seguida, aprendi que, dadas minhas circunstâncias específicas, eu poderia usar max () em vez de ordenar e limitar, por isso uso uma chamada para max () para obter o apropriado exclusivo_id. Isso reduziu o tempo de consulta em cerca de 20%.
Finalmente, aprendi que, com o mecanismo de armazenamento Innodb, eu poderia segmentar o pedaço da tabela diária que estava atualizando com qualquer consulta, o que me permitiu multi-lida nas consultas com um pouco de gravação e scripts. O processamento paralelo funcionou bem e cada thread reduziu o tempo de consulta linearmente.
Portanto, a consulta básica que está realizando literalmente 1000 vezes melhor do que minha primeira tentativa é:
UPDATE Daily
SET qtr_ID =
(
SELECT MAX(unique_ID)
FROM Quarterly
WHERE Daily.datasource = Quarterly.datasource AND
Daily.date_posted > Quarterly.dateposted
)
WHERE unique_ID > ScriptVarLowerBound AND
unique_ID <= ScriptVarHigherBound
;