¿Cómo puedo mejorar mi declaración SQL con resultados semanales con una semana que comienza el jueves o cualquier otro día de la semana?


8

Soy totalmente nuevo, y no pude encontrar una buena manera de hacer esto en ningún lado. Tengo una tabla de base de datos que contiene estadísticas que se registran en distintos momentos durante la semana. La semana de informes comienza el jueves. La tabla contiene una columna de fecha y fecha (fecha) que almacena cuándo se registraron los datos.

Necesito extraer los datos de una semana determinada (las semanas de informes comienzan el jueves). He escrito la siguiente 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

Esto parece funcionar en las pruebas iniciales. Pero no estoy seguro de que sea la mejor manera de hacerlo. No sé mucho sobre el rendimiento de MySQL, pero habrá más de cien mil registros para filtrar. ¿Esta consulta será muy lenta debido a la cantidad de condiciones verificadas?

La función NOW () se usa al extraer el informe más reciente; sin embargo, en algunos casos tendré que hacer informes durante otras semanas, por lo que sustituiría otra fecha en el lugar.

Además, hacerlo de esta manera requiere volver a escribir la consulta si cambia la semana del informe, por ejemplo, el día de inicio cambia al miércoles.

No puedo usar la función WEEK () porque solo puedes comenzar una semana en Sun o Mon con ella.

¡Cualquier idea para mejorar esta consulta es muy apreciada!

Otras notas: Actualmente usando MariaDB 5.3.

Respuestas:


5

Aquí hay una consulta que escribí para darle el jueves más reciente y el miércoles final

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;

Aquí hay un ejemplo para hoy, 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)

Simplemente reemplace las llamadas a la función NOW () con la fecha y hora que desee y tendrá la semana que comienza el jueves todo el tiempo para la fecha y hora que elija.

Aquí hay otro ejemplo usando la fecha 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)

Su consulta de tablereferencia hoy se parecería a algo como esto:

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;

Darle una oportunidad !!!

ACTUALIZACIÓN 2011-09-22 16:27 EDT

Esta fue mi consulta propuesta para marcar Thu-Wed.

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;

¿Qué tal otras semanas?

  • (SELECT SUBSTR('6012345',wkndx,1) hace la semana que comienza el lunes y termina el sol
  • (SELECT SUBSTR('5601234',wkndx,1) hace la semana que comienza el martes y termina el lunes
  • (SELECT SUBSTR('4560123',wkndx,1) la semana que comienza el miércoles y termina el martes
  • (SELECT SUBSTR('3456012',wkndx,1) la semana que comienza el jueves y termina el miércoles
  • (SELECT SUBSTR('2345601',wkndx,1) la semana que comienza el viernes y termina el jueves
  • (SELECT SUBSTR('1234560',wkndx,1) hace la semana que comienza el sábado y termina el viernes
  • (SELECT SUBSTR('0123456',wkndx,1) hace la semana que comienza el domingo y termina el sábado

1

Permítanme repetir lo que están pidiendo para asegurarme de que lo estoy haciendo bien

¿Desea extraer todos los registros de los últimos 7 días de una fecha específica?

Podría verse así:

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

Es importante tener en cuenta que $ date no es una sintaxis literal de mysql, sino solo un marcador de posición de ejemplo para la fecha de inicio que desea. Si esto es para informar, ¿imagino que la consulta se generará en última instancia a partir de algún script? Si eso es cierto, podría ser más simple en ese idioma y luego pasar el valor literal como parte de la consulta construida.

Soy fanático de mantener las consultas lo más simple posible, así que lo dejaría así. Dejaré espacio para que otros contribuyan con respuestas para lograr lo que quieres en una sola consulta SQL-fu.

Editar: después de volver a leer su publicación, parece que probablemente esté usando el tipo de fecha. En cuyo caso, el siguiente bloque en cursiva puede ser redundante. Lo dejo en caso de que sea útil para otros (y como me tomé el tiempo de escribirlo :-)

¿Dijiste que estás usando un "sello de fecha"? Eso no es técnicamente un tipo de datos Mysql. ¿Es una fecha, hora o fecha (la hora y el año también existen pero, según su contexto, creo que no son aplicables)? Pregunto porque es posible que desee que sea solo una columna de fecha en lugar de las otras. Si esta es la opción correcta para usted, realmente depende de los detalles de cómo se usa la columna y de la tabla que se consulta sobre todo. Si su único propósito es extraer registros en un rango de fechas, independientemente de la hora, entonces la fecha es definitivamente el camino a seguir. Por un lado, solo requería 3 bytes en lugar de 4 u 8. Puedo dar más detalles sobre otras razones por las que desearía usar la fecha si coincide con sus requisitos de uso (es decir, no le importa la parte del tiempo). Puede encontrar detalles sobre los diferentes tipos en http://dev.mysql.com/doc/refman/5.0/en/datetime.html

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

Si y solo si está usando la fecha específicamente, puede considerar lo siguiente:

Ejecute la consulta con el prefijo "explicar". Los detalles sobre cómo interpretar el resultado y qué es lo mejor se pueden encontrar en http://dev.mysql.com/doc/refman/5.0/en/explain-output.html

Una vez que tenga esa explicación, cambie la consulta para que sea

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

donde enumera todas las fechas individuales que le interesan. Me he encontrado con situaciones en las que está claro que MySQL no es lo suficientemente inteligente como para usar eficazmente una consulta de rango (es decir, entre x e y) versos que enumeran valores específicos para conjuntos pequeños.

Cualquiera que sea el caso de uso, querrá asegurarse de que la columna esté indexada si realiza consultas de informes regulares en función de sus valores.


1

El Sr. @Rolando obviamente responde la pregunta, pero propondré otra decisión que le permitirá manipular y personalizar los calendarios entre las zonas horarias y el grupo más importante por semanas, que se definen en diferentes zonas horarias.

entonces, supongamos que su mySQL se está ejecutando en un servidor configurado UTC y desea tener un calendario personalizado con 7 horas de anticipación y, por lo tanto, sus semanas deben comenzar el sábado a las 7 a.m.

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);

ahora es una popular combinación cartesiana que debe llenar su tabla:

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 a actualizar las horas:

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

y finalmente organice la semana de su calendario de manera 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));

Créeme, paso algunas horas pensando en esta decisión, pero te da mucha libertad en caso de que quieras agrupar tus resultados en función de una zona horaria personalizada y definiciones semanales.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.