Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/68.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mysql 左连接执行时间太长_Mysql_Sql - Fatal编程技术网

Mysql 左连接执行时间太长

Mysql 左连接执行时间太长,mysql,sql,Mysql,Sql,我有两张桌子。一个是日历,第二个是最终注册,如下所示: *--------------------------* | calender_id | datefield | *--------------------------* | 1 | 2015-07-13 | | 2 | 2015-07-14 | | 3 | 2015-07-15 | | 4 | 2015-07-16 | | - | --

我有两张桌子。一个是日历,第二个是最终注册,如下所示:

*--------------------------*
| calender_id |  datefield | 
*--------------------------*
|     1       | 2015-07-13 |
|     2       | 2015-07-14 |
|     3       | 2015-07-15 |
|     4       | 2015-07-16 |
|     -       | ---------- |
|     -       | ---------- |
|     -       | ---------- |
|     5647    | 2030-12-28 |
|     5648    | 2030-12-29 |
|     5649    | 2030-12-30 |
|     5650    | 2030-12-31 |
*--------------------------*
所以我的第一张表大约是5650条记录

现在,第二个表是我的注册表,我在其中存储用户信息和预订日期

*--------------------------------------------------*
| id |  name |     booking_date    | ticket_status | 
*--------------------------------------------------*
|  1 |  RAM  | 2018-12-24 12:54:53 |    active     |
|  2 |  RAO  | 2018-12-24 12:54:53 |    active     |
|  3 |  RAT  | 2018-12-24 12:54:53 |    active     |
|  4 |  PAL  | 2018-11-24 12:54:53 |    active     |
|  5 |  TOM  | 2018-10-24 12:54:53 |    active     |
|  6 |  SAM  | 2018-10-24 12:54:53 |    active     |
|  7 |  RAT  | 2018-09-24 12:54:53 |    active     |
|  8 |  MAT  | 2019-12-24 12:54:53 |    active     |
|  9 |  NOT  | 2019-12-24 12:54:53 |    active     |
| 10 |  RAM  | 2019-12-24 12:54:53 |    active     |
*--------------------------------------------------*
现在我想统计一下2018年哪本书的注册情况,按月份分类

| booking_date | countT |
|   2018-01    |   0    |
|   2018-02    |   0    |
|   2018-03    |   0    |
|   2018-04    |   0    |
|   2018-05    |   0    |
|   2018-06    |   0    |
|   2018-07    |   0    |
|   2018-08    |   0    |
|   2018-09    |   1    |
|   2018-10    |   2    |
|   2018-11    |   1    |
|   2018-12    |   3    |
我使用下面的查询,我的查询给出了正确的输出,但问题是执行时间。至少10分钟的执行时间太长

选择 日期\u FORMATcalendar.datefield,'%Y-%m'作为预订日期, COUNTfinal\u registration.booking\u日期为countT 从日历 左加入最终注册日期格式最终注册日期,%Y-%m-%d'= 日期\u FORMATcalendar.datefield,'%Y-%m-%d' 和最终注册。票证状态为“活动”、“取消” 其中DATE\u FORMATcalendar.datefield,'%Y'=年 按日期分组\u FORMATcalendar.datefield,'%Y-%m'
我建议使用相关子查询和索引:

SELECT yyyymm, 
       (SELECT COUNT(*)
        FROM final_registration fr
        WHERE fr.status IN ('active', 'cancelled') AND 
              fr.booking_date >= c.month_start AND
              fr.booking_date < c.month_start + interval 1 month
       ) as countT
FROM (SELECT DATE_FORMAT(c.datefield, '%Y-%m') as yyyymm,
             MIN(c.datefield) as month_start
      FROM calendar c
      WHERE YEAR(c.datefield) = ?  -- PASS IN AS PARAMETER!!!
      GROUP BY yyyymm
     ) c  
ORDER BY c.yyyymm;
您想要的索引位于final_registrationdatefield的status上

这比您的查询有几个好处:

它可以使用索引进行日期比较,因为在第二个日期中的日期列上没有使用函数。 它避免了昂贵的外部分组。
还要注意使用参数,而不是使用文本值来填充查询

我建议使用相关子查询和索引:

SELECT yyyymm, 
       (SELECT COUNT(*)
        FROM final_registration fr
        WHERE fr.status IN ('active', 'cancelled') AND 
              fr.booking_date >= c.month_start AND
              fr.booking_date < c.month_start + interval 1 month
       ) as countT
FROM (SELECT DATE_FORMAT(c.datefield, '%Y-%m') as yyyymm,
             MIN(c.datefield) as month_start
      FROM calendar c
      WHERE YEAR(c.datefield) = ?  -- PASS IN AS PARAMETER!!!
      GROUP BY yyyymm
     ) c  
ORDER BY c.yyyymm;
您想要的索引位于final_registrationdatefield的status上

这比您的查询有几个好处:

它可以使用索引进行日期比较,因为在第二个日期中的日期列上没有使用函数。 它避免了昂贵的外部分组。
还要注意使用参数,而不是使用文本值来填充查询

我想这是索引中的问题。 只有当您具有基于日期的函数索引\u format final\u registration.booking\u DATE、%Y-%m-%d时,您的查询才能正常工作。我不知道你有哪个版本的MySQL,它是否提供了这样一个选项

但无论如何,我打赌你有一个关于最终注册日期的简单索引。在这种情况下,join子句是不正确的,因为不会使用索引。因此,您不应将日期转换为字符以使索引正常工作:

LEFT JOIN final_registration ON final_registration.booking_date = calendar.datefield
顺便说一下,WHERE子句也有这个问题。始终更喜欢转换参数而不是表字段,例如:

WHERE calendar.datefield BETWEEN str_to_date(concat("01-01-", year(now())), "%d-%m-%Y") AND str_to_date(concat("31-12-", year(now())), "%d-%m-%Y")

我想这是索引中的问题。 只有当您具有基于日期的函数索引\u format final\u registration.booking\u DATE、%Y-%m-%d时,您的查询才能正常工作。我不知道你有哪个版本的MySQL,它是否提供了这样一个选项

但无论如何,我打赌你有一个关于最终注册日期的简单索引。在这种情况下,join子句是不正确的,因为不会使用索引。因此,您不应将日期转换为字符以使索引正常工作:

LEFT JOIN final_registration ON final_registration.booking_date = calendar.datefield
顺便说一下,WHERE子句也有这个问题。始终更喜欢转换参数而不是表字段,例如:

WHERE calendar.datefield BETWEEN str_to_date(concat("01-01-", year(now())), "%d-%m-%Y") AND str_to_date(concat("31-12-", year(now())), "%d-%m-%Y")

我建议在加入之前执行聚合,实际计算出所需范围的开始和结束,并使用中间值;如果在where条件中使用诸如DATE_格式甚至YEAR之类的函数,那么如果在调用它们的日期字段上没有索引,则会破坏性能。。。。另外,确保你有一个关于预订日期的索引

如果您有一个支持CTE的MySQL版本,您甚至可以不使用日历表。可以使用生成数字1-12作为预订月的CTE,并在该字段上加入

WITH calendar_months AS (
   SELECT 1 AS booking_month
   UNION SELECT booking_month + 1 FROM calendar_months WHERE booking_month < 12
)
SELECT [year] AS booking_year, cm.booking_month, bookingSummary.countT
FROM calendar_months AS cm 
LEFT JOIN (
    SELECT MONTH(booking_date) AS booking_month
        , COUNT(*) AS countT 
    FROM final_registration AS fr
    WHERE fr.ticket_status IN ('active', 'cancelled') 
        AND fr.booking_date BETWEEN [firstdayofyear] AND [lastdayofyear]
    GROUP BY booking_month
) AS bookingSummary
USING (booking_month)
;

注意:将我的[字段]符号视为参数的占位符;我建议使用CTE版本而不是我介绍的第一个版本的原因之一是,它需要维护的参数少了一个。

我建议在加入之前执行聚合,实际计算出所需范围的开始和结束,并使用中间值;如果在where条件中使用诸如DATE_格式甚至YEAR之类的函数,那么如果在调用它们的日期字段上没有索引,则会破坏性能。。。。另外,确保你有一个关于预订日期的索引

如果您有一个支持CTE的MySQL版本,您甚至可以不使用日历表。可以使用生成数字1-12作为预订月的CTE,并在该字段上加入

WITH calendar_months AS (
   SELECT 1 AS booking_month
   UNION SELECT booking_month + 1 FROM calendar_months WHERE booking_month < 12
)
SELECT [year] AS booking_year, cm.booking_month, bookingSummary.countT
FROM calendar_months AS cm 
LEFT JOIN (
    SELECT MONTH(booking_date) AS booking_month
        , COUNT(*) AS countT 
    FROM final_registration AS fr
    WHERE fr.ticket_status IN ('active', 'cancelled') 
        AND fr.booking_date BETWEEN [firstdayofyear] AND [lastdayofyear]
    GROUP BY booking_month
) AS bookingSummary
USING (booking_month)
;

注意:将我的[字段]符号视为参数的占位符;我建议使用CTE版本而不是我介绍的第一个版本的原因之一是,它需要维护的参数少了一个。

…例如,这个。。。其中,在这种情况下,c.datefield介于“2019-01-01”和“2019-12-31”之间而不是YEARSubquery比分组更昂贵,因为它将对主查询的每一行执行一次,而不是联接。@BitLord。绝对不是。您不知道查询是如何处理索引的。这不就是计数吗
每天,并仅以年和月标记每天?@uuerdo。非常感谢。我只是错过了订票日期并不是一个真正的日期,只是一年和一个月…比如这个。。。其中,在这种情况下,c.datefield介于“2019-01-01”和“2019-12-31”之间而不是YEARSubquery比分组更昂贵,因为它将对主查询的每一行执行一次,而不是联接。@BitLord。绝对不是。你不知道查询是如何处理索引的。这难道不只是获取每天的计数,并仅以年和月来标记每天吗?@uuerdo。非常感谢。我只是错过了预订日期不是真正的日期,只是年和月。日期字段和预订日期字段是日期/日期时间/时间戳数据类型还是varchar/char数据类型?日期字段和预订日期字段是日期/日期时间/时间戳数据类型还是varchar/char数据类型?