Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/61.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 JOIN未对WHERE子句进行筛选,带有<&燃气轮机;从MySQL 5.6迁移到>;5.7_Mysql_Left Join_Mariadb_Mysql 5.6_Mysql 5.7 - Fatal编程技术网

MySQL JOIN未对WHERE子句进行筛选,带有<&燃气轮机;从MySQL 5.6迁移到>;5.7

MySQL JOIN未对WHERE子句进行筛选,带有<&燃气轮机;从MySQL 5.6迁移到>;5.7,mysql,left-join,mariadb,mysql-5.6,mysql-5.7,Mysql,Left Join,Mariadb,Mysql 5.6,Mysql 5.7,我们正在将数据库系统从MySQL 5.6升级到MySQL 5.7,自升级以来,一些查询运行速度非常慢 经过一些调查后,我们将其缩小为几个连接查询,当使用“大于”或“小于”操作符时,这些查询突然不再侦听“WHERE”子句。当使用“=”运算符时,它会按预期工作。当查询一个大表时,这会导致持续100%的CPU使用率 这些问题已被简化,以解释手头的问题;使用explain时,我们得到以下输出: explain select * from TableA as A

我们正在将数据库系统从MySQL 5.6升级到MySQL 5.7,自升级以来,一些查询运行速度非常慢

经过一些调查后,我们将其缩小为几个连接查询,当使用“大于”或“小于”操作符时,这些查询突然不再侦听“WHERE”子句。当使用“=”运算符时,它会按预期工作。当查询一个大表时,这会导致持续100%的CPU使用率

这些问题已被简化,以解释手头的问题;使用explain时,我们得到以下输出:

explain 
        select * from TableA as A
                left join
                (
                    select
                        DATE_FORMAT(created_at,'%H:%i:00') as `time`
                    FROM
                        TableB
                    WHERE
                        created_at < DATE_ADD(CURDATE(), INTERVAL -3 HOUR) 
                )
                as V ON V.time = A.time
如您所见,它正在查询/匹配488389行,而没有使用where子句,因为这是该表中的总记录

现在运行相同的查询,但使用LIMIT 9999999命令或使用“=”运算符:

explain 
        select * from TableA as A
                left join
                (
                    select
                        DATE_FORMAT(created_at,'%H:%i:00') as `time`
                    FROM
                        TableB
                    WHERE
                        created_at < DATE_ADD(CURDATE(), INTERVAL -3 HOUR) LIMIT 999999999
                )
                as V ON V.time = A.time
id  select_type table   partitions  type    possible_keys   key key_len ref rows    filtered    Extra
1   SIMPLE  A   NULL    ALL NULL    NULL    NULL    NULL    10080   100.00  NULL
1   SIMPLE  TableB  NULL    ref created_at  created_at  4   const   1   100.00  Using where; Using index
正如所料,只有一排

所以现在的问题是,我们是否以错误的方式进行了查询 刚刚在升级时发现,或者自升级后情况发生了变化 MySQL 5.6?=操作符工作起来似乎很奇怪,但是由于某种原因被忽略,除非在使用限制?时

我们四处搜索,无法找到此问题的原因,出于明显的原因,我们不希望在代码中使用limit 999999解决方案

注意仅在联接内部运行查询时,它也能正常工作


注意我们也在MariaDB 10.1上运行了相同的测试,同样的问题。

解释
-输出只是猜测它将命中多少行。它以统计数据为基础,这些数据已随您的更新而重置。如果我不得不猜测你现有的所有行中有多少行比昨天晚上9点旧,我也会猜测它更接近于“所有行”,而不是“只是一些行”。“limit 9999999”显示另一个行数的原因是相同的:它只是猜测限制将产生影响;在这种情况下,mysql猜测它将是正好是行的一半(如果为真,这将是一个奇怪的巧合),当然,它实际上没有查看限制值,因为当您只有500k行时,999999999不会限制任何内容;即使是“=”中的“1”也只是一个猜测(通常是0而不是1,有时可能更多)

这个估计将有助于选择正确的执行计划,如果它选择了错误的计划,那么在这个猜测中出错只是一个问题;不过,您的执行计划看起来不错,没有太多其他选择。它的操作与预期完全一样:使用在创建时创建的索引扫描索引中的所有日期。由于执行左联接,因此即使从内部查询开始,也不能跳过表a中的值,因此实际上没有可用的替代执行计划。(优化器实际上已在5.7中进行了更改,但此处没有任何效果。)

如果这是您的实际查询,那么就没有真正的理由说明它应该比以前慢(仅针对此查询;当然,有许多通用性能选项可能会产生间接影响,如缓存策略、缓冲区大小等,但对于标准选项,在这里不应产生影响)

如果没有,例如,您在子查询中实际使用了
TableB
中的其他列(通常很难猜到哪些重要的内容在问题中“简化了”),因此需要访问实际的表,这可能取决于您的数据是如何构造的(或者更好:以什么顺序添加的)。您可以尝试
优化表TableB
,使您的表和索引变得新鲜和新,这不会有什么坏处(但会锁定您的表一段时间)


使用mysql 5.7,您现在可以添加生成的列,因此可能值得尝试生成一个已清理的列
time as DATE\u格式(创建于,'%H:%i:00')
,这样您就不必再计算它了。并且可能会将其添加到索引中,这样您就不必再对其进行排序以改进
块嵌套联接
,但这可能取决于您的实际查询和使用频率(垃圾邮件索引会增加开销并占用空间).

使用
JOIN
而不是
LEFT JOIN
,除非您需要选择“right”表

避免加入(选择…)。虽然5.6和5.7添加了一些特性来处理它,但通常最好将子查询转换为更简单的
连接

你的时间表达导致昨天晚上9点;你是说“3小时前”吗

查看这是否能提供所需的结果并运行得更快:

select  A.*, DATE_FORMAT(B.created_at,'%H:%i:00') as `time`
    from  TableA as A
    JOIN  TableB as B  ON B.time = A.time
    WHERE  B.created_at < NOW() - INTERVAL 3 HOUR   -- (assuming "3 hours ago")

在MySQL 5.7中,如果可能,派生表(FROM子句中的子查询)将合并到外部查询中。这通常是一个优势,因为可以避免子查询的结果存储在临时表中。但是,对于您的查询,MySQL 5.6将在此临时表上创建一个索引,该索引可用于连接执行

合并查询的问题是,当列是函数的参数时,不能使用TableB.created_at上的索引。如果可以更改查询以便对联接左侧的列进行转换,则可以使用索引访问右侧的表。比如:

   select * from TableA as A
            left join
            (
                select created_at as time
                FROM TableB
                WHERE created_at < DATE_ADD(CURDATE(), INTERVAL -3 HOUR) 
            )
            as V ON V.time = func(A.time)
从表A中选择*作为
左连接
(
选择创建时间为
从表B
其中创建时间
或者,如果您可以使用内部连接而不是左连接,MySQL可以反转连接顺序,这样tableA.time上的索引就可以用于连接

如果子查询使用限制,则无法合并。因此,通过使用LIMIT,您将获得相同的qu
select  A.*, DATE_FORMAT(B.created_at,'%H:%i:00') as `time`
    from  TableA as A
    JOIN  TableB as B  ON B.time = A.time
    WHERE  B.created_at < NOW() - INTERVAL 3 HOUR   -- (assuming "3 hours ago")
B:  INDEX(time, created_at) -- in that order
A:  INDEX(time)
   select * from TableA as A
            left join
            (
                select created_at as time
                FROM TableB
                WHERE created_at < DATE_ADD(CURDATE(), INTERVAL -3 HOUR) 
            )
            as V ON V.time = func(A.time)