MySQL:在 WHERE 子句中带有 NOT IN 的从属子查询非常慢
Posted
技术标签:
【中文标题】MySQL:在 WHERE 子句中带有 NOT IN 的从属子查询非常慢【英文标题】:MySQL : Dependent Sub Query with NOT IN in the WHERE clause is very slow 【发布时间】:2012-09-10 17:39:09 【问题描述】:我正在使用 open Id login 审核我的应用程序中的用户详细信息。如果用户第一次登录 OPEN ID,我们将其视为注册。我正在使用此详细信息生成审核登录报告。示例表数据。
+---------+----------+-----------+---------------+
| USER_ID | PROVIDER | OPERATION | TIMESTAMP |
+---------+----------+-----------+---------------+
| 120 | Google | SIGN_UP | 1347296347000 |
| 120 | Google | SIGN_IN | 1347296347000 |
| 121 | Yahoo | SIGN_IN | 1347296347000 |
| 122 | Yahoo | SIGN_IN | 1347296347000 |
| 120 | Google | SIGN_UP | 1347296347000 |
| 120 | FaceBook | SIGN_IN | 1347296347000 |
+---------+----------+-----------+---------------+
在此表中,我想根据提供者排除已 SIGN_UP ed "SIGN_IN" ed 用户计数。
显示创建表
CREATE TABLE `signin_details` (
`USER_ID` int(11) DEFAULT NULL,
`PROVIDER` char(40) DEFAULT NULL,
`OPERATION` char(40) DEFAULT NULL,
`TIMESTAMP` bigint(20) DEFAULT NULL
) ENGINE=InnoDB
我正在使用这个查询。
select
count(distinct(USER_ID)) as signin_count,
PROVIDER from signin_details s1
where
s1.USER_ID NOT IN
(
select
USER_ID
from signin_details
where
signin_details.PROVIDER=s1.PROVIDER
and signin_details.OPERATION='SIGN_UP'
and signin_details.TIMESTAMP/1000 BETWEEN UNIX_TIMESTAMP(CURRENT_DATE()-INTERVAL 1 DAY) * 1000 AND UNIX_TIMESTAMP(CURRENT_DATE()) * 1000
)
AND OPERATION='SIGN_IN' group by PROVIDER;
解释输出:
+----+--------------------+----------------+------+---------------+------+---------+------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+----------------+------+---------------+------+---------+------+------+-----------------------------+
| 1 | PRIMARY | s1 | ALL | NULL | NULL | NULL | NULL | 6 | Using where; Using filesort |
| 2 | DEPENDENT SUBQUERY | signin_details | ALL | NULL | NULL | NULL | NULL | 6 | Using where |
+----+--------------------+----------------+------+---------------+------+---------+------+------+-----------------------------+
查询输出:
+--------------+----------+
| signin_count | PROVIDER |
+--------------+----------+
| 1 | FaceBook |
| 2 | Yahoo |
+--------------+----------+
执行 200k 行需要 40 多分钟。
我的假设是它会检查每一行的依赖子查询输出的总数。
我对此查询的假设。
A -> Dependant Outputs (B,C,D) .
A check with B
A check with C
A check with D
如果依赖查询输出较大,则执行时间会很长。如何改进这个查询?
【问题讨论】:
您基本上在 4 天前问了同样的问题,而我的回答是使用NOT EXISTS
而不是 NOT IN
。你试过吗? ***.com/questions/12305066/…
你能显示show create table......
吗?
嗨沃克,我试过了,没有时间。 IN 所用的时间完全相同,不存在。
您好 jcho360,我添加了 show create table output。
你可以给你的表添加索引吗?
【参考方案1】:
如果您使用 MySQL,您必须知道子查询的执行速度非常慢。
IN
很慢...
EXISTS
通常比 IN
快
JOIN
是执行此类操作的最快方式。
SELECT DISTINCT
s1.PROVIDER,
COUNT(DISTINCT s1.USER_ID)
FROM
signin_details s1
LEFT JOIN
(
SELECT DISTINCT
USER_ID, PROVIDER
FROM
signin_details
WHERE
signin_details.OPERATION='SIGN_UP'
AND
signin_details.TIMESTAMP
BETWEEN
UNIX_TIMESTAMP(CURRENT_DATE()-INTERVAL 1 DAY) * 1000
AND UNIX_TIMESTAMP(CURRENT_DATE()) * 1000
) AS t USING (USER_ID, PROVIDER)
WHERE
t.USER_ID IS NULL
AND OPERATION='SIGN_IN'
GROUP BY s1.PROVIDER
http://sqlfiddle.com/#!2/122ac/12
注意:如果您想知道 sqlfiddle 结果,请考虑这里是查询中的UNIX_TIMESTAMP
。
结果:
| PROVIDER | COUNT(DISTINCT S1.USER_ID) |
-----------------------------------------
| FaceBook | 1 |
| Yahoo | 2 |
mysql 和 INTERSECT
的故事。你会得到你不想计算的USER_ID
和PROVIDER
的所有组合。然后LEFT JOIN
他们到你的数据。现在,您要计算的所有行都没有来自LEFT JOIN
的值。您可以通过t.USER_ID IS NULL
获得它们。
输入:
| rn° | USER_ID | PROVIDER | OPERATION | TIMESTAMP |
-------------------------------------------------------
| 1 | 120 | Google | SIGN_UP | 1347296347000 | -
| 2 | 120 | Google | SIGN_IN | 1347296347000 | - (see rn° 1)
| 3 | 121 | Yahoo | SIGN_IN | 1347296347000 | Y
| 4 | 122 | Yahoo | SIGN_IN | 1347296347000 | Y
| 5 | 120 | Google | SIGN_UP | 1347296347000 | -
| 6 | 120 | FaceBook | SIGN_IN | 1347296347000 | F
| 7 | 119 | FaceBook | SIGN_IN | 1347296347000 | - (see rn° 8)
| 8 | 119 | FaceBook | SIGN_UP | 1347296347000 | -
【讨论】:
【参考方案2】:在 HAVING 子句中使用“NOT IN”。 它会比“不在哪里”更快
【讨论】:
以上是关于MySQL:在 WHERE 子句中带有 NOT IN 的从属子查询非常慢的主要内容,如果未能解决你的问题,请参考以下文章
Laravel 5.8,在 where 子句中带有 Count(*) 的棘手子查询
为啥以及何时在 WHERE 子句中带有条件的 LEFT JOIN 不等于在 ON 中的相同 LEFT JOIN? [复制]