MySQL:计算MTD的正确方法

时间:2019-02-22 20:14:23

标签: php mysql pdo

我一直看到这样的答案:https://stackoverflow.com/a/21277074/4236404是如何在MySQL查询中获取“月份”日期行的默认接受答案。

当前答案:

SELECT id, created
  FROM table1
 WHERE MONTH(created) = MONTH(CURDATE())
   AND YEAR(created) = YEAR(CURDATE())

此查询将起作用,但是效率低下,MySQL将执行全面扫描以获取结果。

因此,获取月份(MTD)查询的行的正确方法如下(我将使用PDO和PHP进行演示:

//get the first day of the month
$month_first_day = date( "Y-m-01" );

//select the record where the created date is equal to or bigger than the first day of the current month.
$stmt = $pdo->prepare('SELECT id, created FROM table1 WHERE created >= :first_day_of_month');
$stmt->bindParam(':first_day_of_month', $month_first_day);
$stmt->execute();
$realm_data = $stmt->fetch();

奖励查询

要获取今天的所有记录,请执行以下操作:

$today = date( "Y-m-d" );
$stmt = $pdo->prepare('SELECT id, created FROM table1 WHERE created >= :today');
$stmt->bindParam(':today', $today);
$stmt->execute();
$realm_data = $stmt->fetch();

要获取昨天的所有记录,请执行以下操作:

$today = date( "Y-m-d" );
$yesterday = date( "Y-m-d", time()-86400 );
$stmt = $pdo->prepare('SELECT id, created FROM table1 WHERE created >= :yesterday AND created < :today');
$stmt->bindParam(':today', $today);
$stmt->bindParam(':yesterday', $yesterday);
$stmt->execute();
$realm_data = $stmt->fetch();

要获取最近7天的所有记录,请执行以下操作:

$seven_days_ago = date('Y-m-d', strtotime('-7 days'));
$stmt = $pdo->prepare('SELECT id, created FROM table1 WHERE created >= :seven_days_ago');
$stmt->bindParam(':seven_days_ago', $seven_days_ago);
$stmt->execute();
$realm_data = $stmt->fetch();

假设/提示

MySQL中的列设置为日期时间格式。

1 个答案:

答案 0 :(得分:1)

MySQL不允许创建基于表达式/函数的索引,从MySQL 5.7+起,可以使用generated columns进行索引,这是可以解决的。
确保将Tabele设置为InnoDB,从而可以在未存储的生成列上建立索引。

CREATE TABLE t (
 d DATETIME 
 , dy INT AS (YEAR(d)) 
 , dm INT AS (MONTH(d))
 , INDEX(dy, dm)                    
) Engine = InnoDB ; 

说明

EXPLAIN
SELECT 
 *
FROM 
 t
WHERE
    MONTH(d) = MONTH(CURDATE())
  AND
    YEAR(d) = YEAR(CURDATE())

结果

| id  | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra       |
| --- | ----------- | ----- | ---------- | ---- | ------------- | --- | ------- | --- | ---- | -------- | ----------- |
| 1   | SIMPLE      | t     |            | ALL  | dy            |     |         |     | 19   | 78.95    | Using where |

请参阅demo

是的,我知道它说ALL,但是MySQL就在这里。返回的结果为15条记录的表是19条记录。

此外,您还需要知道说明输出中的行输出是InnoDB引擎的预期记录数,不是实际数。

但是,与进行一个随机磁盘I.O(6ms * 1)并可以一次流传输数据的全表扫描相比,随机磁盘I.O(6ms * 15)进行流传输的价格要高出(alot)15倍。

MySQL的优化器/执行程序是基于成本的,它根据最昂贵的随机磁盘I / O计算相对成本。

但是请注意,它说到可能的密钥dy MySQL知道它现在可以将索引用于

WHERE MONTH(d) = MONTH(CURDATE()) AND YEAR(d) = YEAR(CURDATE())

只有MySQL认为我不会通过阅读索引来做更多的工作。

仍然等到MySQL支持表达式/函数索引,也许它们会在以后的MySQL 8版本中添加。
MySQL 8仍在开发中并正在获取更新。

CREATE INDEX index_name ON table (YEAR(d));

但是我认为很快不会发生,MySQL确实通过我使用的解决方法或多或少地支持了它,因此它不认为他们急于实现它。.

MySQL 8.0.16之前的MySQL也不支持CHECK CONSTRAINTS,他们也没有急于实现它,因为MySQL已经或多或少地通过带有CHECK OPTION的VIEW来支持它,这是一个很好的例子。

修改:

我很快就说MySQL很快将不支持该功能表达式/功能索引。

我注意到MySQL 8.0.13更新了添加的表达式/函数索引。

功能关键部件

  

“正常”索引对列值或列值的前缀进行索引。   例如,在下表中,给定t1的索引条目   该行包含完整的col1值和col2值的前缀   由前10个字符组成:

     

MySQL 8.0.13和更高版本支持索引的功能关键部分   表达式值,而不是列或列前缀值。用于   功能性的关键部件可以索引未直接存储在其中的值   桌子。例子:

     

CREATE TABLE t1 (col1 INT, col2 INT, INDEX func_index ((ABS(col1)))); CREATE INDEX idx1 ON t1 ((col1 + col2)); CREATE INDEX idx2 ON t1 ((col1 + col2), (col1 - col2), col1); ALTER TABLE t1 ADD INDEX ((col1 * 40) DESC);

请参阅source

在MySQL 8.0.13+中,您可以做到

CREATE INDEX index_name ON table (YEAR(created), MONTH(created));