从星期四或一周中的任何一天开始,如何改善每周语句的SQL语句?


8

我是一个新手,我找不到在任何地方执行此操作的好方法。我有一个数据库表,其中包含一周中不同时间记录的统计信息。报告周从星期四开始。该表包含一个datestamp列(日期),该列存储记录数据的时间。

我需要提取给定一周的数据(报告周从星期四开始)。我写了以下查询:

   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

这似乎适用于初始测试。但是我不确定这是最好的方法。我对MySQL的性能了解不多,但是有十万条记录需要过滤。由于检查的条件数量众多,此查询会变慢吗?

在提取最新报告时会使用NOW()函数-但是,在某些情况下,我将需要为其他周做报告-因此我将在该位置替换另一个日期。

同样,如果报告周发生更改(例如,开始日期更改为星期三),则需要重新编写查询。

我不能使用WEEK()函数,因为您只能使用它在Sun或Mon上开始一周。

任何改进此查询的想法将不胜感激!

其他说明:当前正在使用MariaDB 5.3。

Answers:


5

这是我撰写的查询,目的是为您提供最近的星期四和结尾的星期三

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;

这是今天的一个例子,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)

只需将NOW()函数调用替换为所需的任何日期时间,您就可以选择从星期四开始的一周中的任意一个给定日期时间。

这是另一个使用特定日期“ 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)

table今天查询的查询类似于以下内容:

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;

试试看 !!!

更新2011-09-22 16:27 EDT

这是我建议的标记星期四的查询。

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;

其他星期怎么样?

  • (SELECT SUBSTR('6012345',wkndx,1) 从星期一开始到星期日结束的一周
  • (SELECT SUBSTR('5601234',wkndx,1) 从星期二开始到星期一结束
  • (SELECT SUBSTR('4560123',wkndx,1) 从星期三开始到星期二结束
  • (SELECT SUBSTR('3456012',wkndx,1) 从星期四开始到星期三结束
  • (SELECT SUBSTR('2345601',wkndx,1) 从周五开始到周四结束
  • (SELECT SUBSTR('1234560',wkndx,1) 从周六开始到周五结束
  • (SELECT SUBSTR('0123456',wkndx,1) 周日开始于星期日结束吗

1

让我重新陈述您的要求,以确保我做对了:

您要拉过去7天在指定日期的所有记录吗?

它可能看起来像:

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

重要的是要注意,$ date不是文字mysql语法,而只是您想要的开始日期的示例占位符。如果这是为了报告,我想查询将最终从某些脚本生成?如果是这样,那么使用该语言可能会更简单,然后将字面值作为构造查询的一部分传递。

我喜欢保持查询尽可能简单,所以我会这样。我将留出空间供其他人提供答案,以在单个SQL-fu查询中完成所需的操作。

编辑:重新阅读您的帖子后,看来您可能正在使用日期类型。在这种情况下,以下斜体块可能是多余的。如果对其他人有帮助,我会保留它(并且因为我花了时间写它:-)

您说您正在使用“日期戳”?从技术上讲,这不是Mysql数据类型。是日期时间,时间戳还是日期(时间和年份也存在,但从您的角度来看,我认为这些都不适用)?我问是因为您可能只想将其设为日期列而不是其他日期列。如果这是您的正确选择,则实际上取决于列的使用方式以及整个表的查询细节。如果唯一的目的只是在日期范围内提取记录,而不考虑时间,那么日期绝对是正确的选择。例如,它只需要3个字节,而不是4或8个字节。我可以详细说明您希望使用date的其他原因(如果它符合您的使用要求)(即您不在乎时间部分)。您可以在以下位置找到有关不同类型的详细信息 http://dev.mysql.com/doc/refman/5.0/en/datetime.html

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

当且仅当您正在使用日期时,才可以考虑以下事项:

以“ explain”为前缀运行查询。有关如何解释输出以及最佳输出的详细信息,请参见http://dev.mysql.com/doc/refman/5.0/en/explain-output.html。

解释完之后,将查询更改为

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

在这里您枚举了所有您感兴趣的日期。我遇到了这样一种情况,很明显,MySQL不够聪明,无法有效地使用范围查询(即x和y之间)来枚举小集合的特定值。

如果您要根据列的值进行常规报告查询,则要确保使用哪种列都可以为该列建立索引。


1

@Rolando先生显然回答了这个问题,但我将提出另一个决定,该决定将使您可以操纵和自定义时区之间的日历,以及最重要的按周分组(在不同时区中定义)

因此,假设您的mySQL在配置了UTC的服务器上运行,并且您想要提前7个小时的自定义日历,因此您的周数应该从周六上午7点开始

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

现在流行的笛卡尔联接将填充您的表:

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;

让我们更新时间:

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

最后以自定义方式安排日历的星期

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

相信我,我花了几个小时来做​​出这个决定,但是如果您想根据自定义的时区和周定义对结果进行分组,它给您带来了很大的自由度。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.