Php MySQL:计算MTD的正确方法

Php MySQL:计算MTD的正确方法,php,mysql,pdo,Php,Mysql,Pdo,我一直看到这样的答案:作为如何在MySQL查询中获取月至今行的默认接受答案 目前的答复如下: 这个查询可以工作,但效率很低,MySQL将执行完整扫描以获得结果 因此,获取Month to Date(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 equ

我一直看到这样的答案:作为如何在MySQL查询中获取月至今行的默认接受答案

目前的答复如下:

这个查询可以工作,但效率很低,MySQL将执行完整扫描以获得结果

因此,获取Month to Date(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();
$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();
要获取昨天的所有记录,请执行以下操作:

$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();
$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中的列设置为datetime格式。

MySQL不允许创建基于表达式/函数的索引。MySQL 5.7+中有一种解决方法,可以使用该方法编制索引。
确保将表设置为InnoDB,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 |

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

您还需要知道解释输出中的行输出是InnoDB引擎的预期记录外的数字,而不是实际数字。

但是15倍于一个随机磁盘I.O(6ms*15)和流,它比一个完整的表扫描更昂贵(很多),后者执行一个随机磁盘I.O(6ms*1),可以一次性流式传输数据

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

但是请注意,它说可能的key
dy
MySQL知道它现在可以使用索引进行查询

在哪里
月(d)=月(CURDATE())
和
年份(d)=年份(CURDATE())

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

仍然在等待MySQL支持表达式/函数索引,也许他们会在以后的MySQL 8版本中添加这些索引。
MySQL 8仍在开发和更新中

在表上创建索引名称(年份(d));

但我认为这不会很快发生,MySQL确实通过我使用的变通方法或多或少地支持它,所以它不认为他们急于实现它

MySQL 8.0.16之前的MySQL也不支持检查约束,他们也不急于实现它,因为MySQL已经或多或少地支持了它,并提供了一个很好的示例

编辑:

我很快就跟他说MySQL不会很快支持这个特性表达式/函数索引

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

功能关键部件

“普通”索引索引列值或列值的前缀。 例如,在下表中,给定t1的索引项 行包括完整的col1值和col2值的前缀 由前10个字符组成:

MySQL 8.0.13及更高版本支持索引的功能关键部分 表达式值,而不是列或列前缀值。使用 功能键部件支持对未直接存储在中的值进行索引 下表。示例:

创建表t1(col1 INT,col2 INT,INDEX func_INDEX((ABS(col1)));
在t1上创建索引idx1((col1+col2));在t1上创建索引idx2
((col1+col2),(col1-col2),col1);修改表t1添加索引((col1
*40)描述);

在MySQL 8.0.13+中,您可以


在表上创建索引名称(年(已创建),月(已创建));

基于日期或日期时间范围筛选行的标准模式是表中有一列数据类型日期、日期时间或时间戳;然后将其与范围开始和范围结束值进行比较。例如,
其中t.mydtcol>='2019-02-01'和t.mydtcol<'2019-02-01'+间隔1个月。
我们可以使用expresession代替了文本,例如,
其中t.mydtcol>=DATE\u格式(NOW(),“%Y-%m-01”)+间隔0个月和t.mydtcol
非常确定这
像CONCAT(DATE\u格式(),“%Y-%m”),“%”一样,至少它将在DATETIME上运行,并且可以使用索引。。。我基本上看到了你的答案你想避免使用anny运算符左边的函数或关键字,如
IN
like
Sjoe,对于试图帮助他人的教程,你投了很多反对票,人们看不到我下面的答案和解释吗?如果你想为新程序员写一篇关于编写高效查询的教程,你难道不知道自己该怎么做吗?我不明白,我写了下面的帖子,然后stackoverflow说我也需要给出答案。让我删除当前的OP并在那里发布我的答案instead@ErikThiart请参阅更新MySQL 8.0.13+以支持表达式/函数索引