Como posso melhorar minha instrução SQL com resultados semanais com a semana iniciando na quinta-feira ou em qualquer outro dia da semana?

8

Eu sou um novato total e não consegui encontrar uma boa maneira de fazer isso em qualquer lugar. Eu tenho uma tabela de banco de dados que contém estatísticas que são registradas em vários momentos da semana. A semana do relatório começa na quinta-feira. A tabela contém uma coluna de data e hora (data) que armazena quando os dados foram registrados.

Preciso extrair os dados para uma determinada semana (as semanas de relatório começam na quinta-feira). Eu escrevi a seguinte consulta:

   SELECT * 
FROM `table` 
WHERE 1 = CASE 
  WHEN WEEKDAY(NOW()) = 0 THEN DATEDIFF(NOW(),`date`) BETWEEN -2 AND 4
  WHEN WEEKDAY(NOW()) = 1 THEN DATEDIFF(NOW(),`date`) BETWEEN -1 AND 5
  WHEN WEEKDAY(NOW()) = 2 THEN DATEDIFF(NOW(),`date`) BETWEEN -0 AND 6
  WHEN WEEKDAY(NOW()) = 3 THEN DATEDIFF(NOW(),`date`) BETWEEN -6 AND 0
  WHEN WEEKDAY(NOW()) = 4 THEN DATEDIFF(NOW(),`date`) BETWEEN -5 AND 1
  WHEN WEEKDAY(NOW()) = 5 THEN DATEDIFF(NOW(),`date`) BETWEEN -4 AND 2
  WHEN WEEKDAY(NOW()) = 6 THEN DATEDIFF(NOW(),`date`) BETWEEN -3 AND 3
END

Isso parece funcionar no teste inicial. Mas não tenho certeza de que é a melhor maneira de fazer isso. Não sei muito sobre o desempenho do MySQL, mas haverá mais de cem mil registros para filtrar. Essa consulta será muito lenta devido ao número de condições verificadas?

A função NOW () é usada ao extrair o relatório mais atual - no entanto, em alguns casos, precisarei fazer relatórios por outras semanas - para substituir outra data no local.

Além disso, fazê-lo dessa maneira exige reescrever a consulta se a semana do relatório for alterada - digamos que o dia inicial mude para quarta-feira.

Não posso usar a função WEEK () porque você só pode iniciar uma semana no Sun ou Mon com ela.

Todas as idéias para melhorar esta consulta são muito apreciadas!

Outras notas: Atualmente usando o MariaDB 5.3.

James Lee Vann
fonte

Respostas:

5

Aqui está uma consulta que escrevi para fornecer a quinta-feira mais recente e o final de quarta-feira

SELECT thuwk_beg + INTERVAL 0 second thu_beg,
thuwk_beg + INTERVAL 604799 second wed_end
FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A;

Aqui está um exemplo de hoje, 2011-09-21

mysql> SELECT
    -> thuwk_beg + INTERVAL 0 second thu_beg,
    -> thuwk_beg + INTERVAL 604799 second wed_end
    -> FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
    -> FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
    -> FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A;
+---------------------+---------------------+
| thu_beg             | wed_end             |
+---------------------+---------------------+
| 2011-09-15 00:00:00 | 2011-09-21 23:59:59 |
+---------------------+---------------------+
1 row in set (0.00 sec)

Apenas substitua as chamadas da função NOW () pelo horário que desejar e você terá a semana começando na quinta-feira o tempo todo pelo horário especificado.

Aqui está outro exemplo usando a data específica '2011-01-01'

mysql> SELECT
    -> thuwk_beg + INTERVAL 0 second thu_beg,
    -> thuwk_beg + INTERVAL 604799 second wed_end
    -> FROM (SELECT (DATE('2011-01-01') - INTERVAL daysbacktothursday DAY) thuwk_beg
    -> FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
    -> FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE('2011-01-01') dt) AAAA) AAA) AA) A;
+---------------------+---------------------+
| thu_beg             | wed_end             |
+---------------------+---------------------+
| 2010-12-30 00:00:00 | 2011-01-05 23:59:59 |
+---------------------+---------------------+
1 row in set (0.00 sec)

Sua consulta de tablereferência hoje seria semelhante a isto:

SELECT * from `table`,
(SELECT thuwk_beg + INTERVAL 0 second thu_beg,
thuwk_beg + INTERVAL 604799 second wed_end
FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A) M
WHERE `date` >= thu_beg
AND `date` <= wed_end;

De uma chance !!!

ATUALIZAÇÃO 22-09-2011 16:27 EDT

Esta foi a minha consulta proposta para marcar Qui-Qua.

SELECT thuwk_beg + INTERVAL 0 second thu_beg,
thuwk_beg + INTERVAL 604799 second wed_end
FROM (SELECT (DATE(NOW()) - INTERVAL daysbacktothursday DAY) thuwk_beg
FROM (SELECT SUBSTR('3456012',wkndx,1) daysbacktothursday
FROM (SELECT DAYOFWEEK(dt) wkndx FROM (SELECT DATE(NOW()) dt) AAAA) AAA) AA) A;

Que tal outras semanas ???

  • (SELECT SUBSTR('6012345',wkndx,1) faz a semana começando segunda-feira terminando dom
  • (SELECT SUBSTR('5601234',wkndx,1) faz a semana que começa ter que termina em seg
  • (SELECT SUBSTR('4560123',wkndx,1) a semana que começa em Qua e termina em Ter
  • (SELECT SUBSTR('3456012',wkndx,1) faz a semana começando Qui terminando Qua
  • (SELECT SUBSTR('2345601',wkndx,1) faz a semana que começa sexta-feira, terminando qui
  • (SELECT SUBSTR('1234560',wkndx,1) faz a semana que começa sáb, terminando sex
  • (SELECT SUBSTR('0123456',wkndx,1) faz a semana começando dom terminando sáb
RolandoMySQLDBA
fonte
1

Deixe-me reafirmar o que você está pedindo para garantir que eu esteja acertando:

Deseja extrair todos os registros dos últimos 7 dias em uma data especificada?

Pode parecer com:

select * from table where `date` between $date - interval 7 day and $date

É importante observar que $ date não é uma sintaxe literal do mysql, mas apenas um exemplo de local para a data de início que você deseja. Se for para reportar, imagino que a consulta será gerada a partir de algum script? Se isso for verdade, pode ser mais simples nessa linguagem e depois passar o valor literal como parte da consulta construída.

Sou fã de manter as consultas o mais simples possível, para que eu as deixe assim. Deixarei espaço para que outras pessoas contribuam com respostas para realizar o que você deseja em uma única consulta SQL-fu.

Editar: após reler sua postagem, parece que você provavelmente está usando o tipo de data. Nesse caso, o seguinte bloco em itálico pode ser redundante. Estou deixando no caso de ser útil para outras pessoas (e desde que dediquei tempo para escrevê-lo :-)

Você disse que está usando um "carimbo de data"? Tecnicamente, esse não é um tipo de dados Mysql. É uma data / hora, carimbo de data ou hora (a hora e o ano também existem, mas a partir do seu contexto acho que não são aplicáveis)? Eu pergunto, porque você pode fazer apenas uma coluna de data em vez das outras. Se esta é a escolha certa para você, realmente depende dos detalhes de como a coluna está sendo usada e a tabela está sendo consultada. Se seu único objetivo é obter registros em um intervalo de datas, independentemente da hora, a data é definitivamente o caminho a percorrer. Por um lado, eram necessários apenas 3 bytes em vez de 4 ou 8. Posso detalhar outros motivos pelos quais você deseja usar a data se ela corresponder aos seus requisitos de uso (ou seja, você não se importa com a parte do tempo). Você pode encontrar detalhes sobre os diferentes tipos em http://dev.mysql.com/doc/refman/5.0/en/datetime.html

http://dev.mysql.com/doc/refman/5.0/en/storage-requirements.html

Se e somente se você estiver usando a data especificamente, considere o seguinte:

Execute a consulta com o prefixo "explique". Detalhes sobre como interpretar a saída e o que é melhor podem ser encontrados em http://dev.mysql.com/doc/refman/5.0/en/explain-output.html

Depois de ter explicado, altere a consulta a ser

select * from table where date in ("N", "N+1"..."N+7")

onde você enumera todas as datas individuais nas quais está interessado. Me deparei com situações em que o MySQL não é inteligente o suficiente para usar com eficiência uma consulta de intervalo (ie entre x e y) versos enumerando valores específicos para pequenos conjuntos.

Seja qual for o caso de uso, você desejará garantir que a coluna seja indexada se você estiver fazendo consultas regulares de relatórios com base em seus valores.

atxdba
fonte
1

O Sr. @Rolando obviamente responde à pergunta, mas proponho outra decisão que permitirá que você manipule e personalize calendários entre fusos horários e o grupo mais importante por semanas, definidos em diferentes fusos horários.

então, suponha que o seu mySQL esteja sendo executado no servidor configurado no UTC e que você queira ter um calendário personalizado com 7 horas de antecedência e, portanto, suas semanas devem começar no sábado às 7 da manhã

CREATE TABLE `wh_blur_calendar` (
  `date` timestamp NOT NULL ,
  `y` smallint(6) DEFAULT NULL,
  `q` tinyint(4) DEFAULT NULL,
  `m` tinyint(4) DEFAULT NULL,
  `d` tinyint(4) DEFAULT NULL,
  `w` tinyint(4) DEFAULT NULL,
  PRIMARY KEY (`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `wh_ints` (
  `i` tinyint(4) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into wh_ints values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);

agora uma junção cartesiana popular que deve preencher sua tabela:

INSERT INTO wh_blur_calendar (date)
SELECT DATE('2010-01-01 00:00:00 ') + INTERVAL a.i*10000 + b.i*1000 + c.i*100 + d.i*10 + e.i DAY
FROM wh_ints a JOIN wh_ints b JOIN wh_ints c JOIN wh_ints d JOIN wh_ints e
WHERE (a.i*10000 + b.i*1000 + c.i*100 + d.i*10 + e.i) < 10245
ORDER BY 1;

Vamos atualizar as horas:

update  db_wh_repo.wh_blur_calendar set date = date_add(date, interval 7 hour);

e, finalmente, organize a semana do seu calendário de maneira personalizada

UPDATE wh_blur_calendar
SET 
    y = YEAR(date),
    q = quarter(date),
    m = MONTH(date),
    d = dayofmonth(date),
    w = week(date_add((date), interval 1 day));

Acredite, passo algumas horas com essa decisão, mas ela oferece muita liberdade, caso você queira agrupar seus resultados com base em definições personalizadas de fuso horário e semana.

ninjabber
fonte