158

我在要检查的数据库中有几个重复项,所以我做了什么来查看哪些是重复项,我这样做了:

SELECT relevant_field
FROM some_table
GROUP BY relevant_field
HAVING COUNT(*) > 1

这样,我将获得所有相关字段出现不止一次的行。此查询需要几毫秒才能执行。

现在,我想检查每个重复项,所以我想我可以在上面查询中使用相关字段选择 some_table 中的每一行,所以我这样做了:

SELECT *
FROM some_table 
WHERE relevant_field IN
(
    SELECT relevant_field
    FROM some_table
    GROUP BY relevant_field
    HAVING COUNT(*) > 1
)

由于某种原因,这变得非常缓慢(需要几分钟)。这里到底发生了什么让它这么慢?相关字段已编入索引。

最终,我尝试从第一个查询创建一个视图“temp_view” (SELECT relevant_field FROM some_table GROUP BY relevant_field HAVING COUNT(*) > 1),然后像这样进行第二个查询:

SELECT *
FROM some_table
WHERE relevant_field IN
(
    SELECT relevant_field
    FROM temp_view
)

这很好用。MySQL 在几毫秒内完成此操作。

这里有任何 SQL 专家可以解释发生了什么吗?

4

11 回答 11

136

正在为每一行运行子查询,因为它是一个相关查询。通过从子查询中选择所有内容,可以将相关查询变成非相关查询,如下所示:

SELECT * FROM
(
    SELECT relevant_field
    FROM some_table
    GROUP BY relevant_field
    HAVING COUNT(*) > 1
) AS subquery

最终查询如下所示:

SELECT *
FROM some_table
WHERE relevant_field IN
(
    SELECT * FROM
    (
        SELECT relevant_field
        FROM some_table
        GROUP BY relevant_field
        HAVING COUNT(*) > 1
    ) AS subquery
)
于 2011-05-27T21:18:27.183 回答
115

将查询改写成这个

SELECT st1.*, st2.relevant_field FROM sometable st1
INNER JOIN sometable st2 ON (st1.relevant_field = st2.relevant_field)
GROUP BY st1.id  /* list a unique sometable field here*/
HAVING COUNT(*) > 1

我认为st2.relevant_field必须在 select 中,否则having子句会报错,但我不是 100% 确定

永远不要IN与子查询一起使用;这是出了名的慢。
IN与固定的值列表一起使用。

更多提示

  1. 如果您想更快地进行查询,请不要SELECT *只选择您真正需要的字段。
  2. 确保你有一个索引relevant_field来加速等值连接。
  3. 确保group by在主键上。
  4. 如果您在 InnoDB 上并且只选择索引字段(并且事情并不太复杂) ,那么MySQL 将仅使用索引来解析您的查询,从而加快速度。

IN (select 90%查询的通用解决方案

使用此代码

SELECT * FROM sometable a WHERE EXISTS (
  SELECT 1 FROM sometable b
  WHERE a.relevant_field = b.relevant_field
  GROUP BY b.relevant_field
  HAVING count(*) > 1) 
于 2011-05-26T09:09:03.917 回答
7
SELECT st1.*
FROM some_table st1
inner join 
(
    SELECT relevant_field
    FROM some_table
    GROUP BY relevant_field
    HAVING COUNT(*) > 1
)st2 on st2.relevant_field = st1.relevant_field;

我已经在我的一个数据库上尝试了您的查询,还尝试将其重写为子查询的连接。

这工作得更快,试试吧!

于 2011-05-26T09:08:05.460 回答
6

我已经用 www.prettysql.net 重新格式化了你的慢 sql 查询

SELECT *
FROM some_table
WHERE
 relevant_field in
 (
  SELECT relevant_field
  FROM some_table
  GROUP BY relevant_field
  HAVING COUNT ( * ) > 1
 );

在查询和子查询中都使用表时,应始终为两者取别名,如下所示:

SELECT *
FROM some_table as t1
WHERE
 t1.relevant_field in
 (
  SELECT t2.relevant_field
  FROM some_table as t2
  GROUP BY t2.relevant_field
  HAVING COUNT ( t2.relevant_field ) > 1
 );

这有帮助吗?

于 2011-05-26T08:06:15.320 回答
6

子查询与连接

http://www.scribd.com/doc/2546837/New-Subquery-Optimizations-In-MySQL-6

于 2011-05-26T08:46:16.720 回答
3

试试这个

SELECT t1.*
FROM 
 some_table t1,
  (SELECT relevant_field
  FROM some_table
  GROUP BY relevant_field
  HAVING COUNT (*) > 1) t2
WHERE
 t1.relevant_field = t2.relevant_field;
于 2013-04-04T10:16:05.897 回答
3

首先,您可以找到重复的行并找到使用了多少次的行数,然后按这样的数字排序;

SELECT q.id,q.name,q.password,q.NID,(select count(*) from UserInfo k where k.NID= q.NID) as Count,
(
		CASE q.NID
		WHEN @curCode THEN
			@curRow := @curRow + 1
		ELSE
			@curRow := 1
		AND @curCode := q.NID
		END
	) AS No
FROM UserInfo q,
(
		SELECT
			@curRow := 1,
			@curCode := ''
	) rt
WHERE q.NID IN
(
    SELECT NID
    FROM UserInfo
    GROUP BY NID
    HAVING COUNT(*) > 1
) 

之后创建一个表并将结果插入其中。

create table CopyTable 
SELECT q.id,q.name,q.password,q.NID,(select count(*) from UserInfo k where k.NID= q.NID) as Count,
(
		CASE q.NID
		WHEN @curCode THEN
			@curRow := @curRow + 1
		ELSE
			@curRow := 1
		AND @curCode := q.NID
		END
	) AS No
FROM UserInfo q,
(
		SELECT
			@curRow := 1,
			@curCode := ''
	) rt
WHERE q.NID IN
(
    SELECT NID
    FROM UserInfo
    GROUP BY NID
    HAVING COUNT(*) > 1
) 

最后,删除重复的行。No 是从 0 开始。除了每组的第一个数字之外,删除所有重复的行。

delete from  CopyTable where No!= 0;

于 2017-08-21T12:53:13.327 回答
1

有时当数据变大时,由于查询优化,mysql WHERE IN 的速度可能会很慢。尝试使用 STRAIGHT_JOIN 告诉 mysql 按原样执行查询,例如

SELECT STRAIGHT_JOIN table.field FROM table WHERE table.id IN (...)

但要注意:在大多数情况下 mysql 优化器工作得很好,所以我建议仅在遇到此类问题时使用它

于 2014-03-10T12:39:40.050 回答
0

这类似于我的情况,我有一个名为tabel_buku_besar. 我需要的是

  1. 寻找其中有account_code='101.100'和也有的记录tabel_buku_besarcompanyarea='20000'IDRcurrency

  2. 我需要获取tabel_buku_besar与第 1 步具有相同 account_code 但transaction_number在第 1 步结果 中具有的所有记录

使用时select ... from...where....transaction_number in (select transaction_number from ....),我的查询运行速度极慢,有时会导致请求超时或使我的应用程序没有响应...

我尝试了这种组合,结果……还不错……

`select DATE_FORMAT(L.TANGGAL_INPUT,'%d-%m-%y') AS TANGGAL,
      L.TRANSACTION_NUMBER AS VOUCHER,
      L.ACCOUNT_CODE,
      C.DESCRIPTION,
      L.DEBET,
      L.KREDIT 
 from (select * from tabel_buku_besar A
                where A.COMPANYAREA='$COMPANYAREA'
                      AND A.CURRENCY='$Currency'
                      AND A.ACCOUNT_CODE!='$ACCOUNT'
                      AND (A.TANGGAL_INPUT BETWEEN STR_TO_DATE('$StartDate','%d/%m/%Y') AND STR_TO_DATE('$EndDate','%d/%m/%Y'))) L 
INNER JOIN (select * from tabel_buku_besar A
                     where A.COMPANYAREA='$COMPANYAREA'
                           AND A.CURRENCY='$Currency'
                           AND A.ACCOUNT_CODE='$ACCOUNT'
                           AND (A.TANGGAL_INPUT BETWEEN STR_TO_DATE('$StartDate','%d/%m/%Y') AND STR_TO_DATE('$EndDate','%d/%m/%Y'))) R ON R.TRANSACTION_NUMBER=L.TRANSACTION_NUMBER AND R.COMPANYAREA=L.COMPANYAREA 
LEFT OUTER JOIN master_account C ON C.ACCOUNT_CODE=L.ACCOUNT_CODE AND C.COMPANYAREA=L.COMPANYAREA 
ORDER BY L.TANGGAL_INPUT,L.TRANSACTION_NUMBER`
于 2014-09-25T03:43:54.170 回答
0

我发现这是查找值是否存在的最有效方法,可以轻松地反转逻辑以查找值是否不存在(即 IS NULL);

SELECT * FROM primary_table st1
LEFT JOIN comparision_table st2 ON (st1.relevant_field = st2.relevant_field)
WHERE st2.primaryKey IS NOT NULL

*将相关字段替换为您要检查的值的名称是否存在于您的表中

*将primaryKey替换为对比表中主键列的名称。

于 2015-11-30T11:51:48.540 回答
0

relevant_field这很慢,因为您的子查询对于您的IN子句的子查询之间的每次比较都会执行一次。您可以像这样避免这种情况:

SELECT *
FROM some_table T1 INNER JOIN 
(
    SELECT relevant_field
    FROM some_table
    GROUP BY relevant_field
    HAVING COUNT(*) > 1
) T2 
USING(relevant_field)

这将创建一个派生表(在内存中,除非它太大而无法容纳)作为 T2,然后INNER JOIN是 T1。JOIN 发生一次,因此查询被执行一次。

我发现这对于优化使用数据透视表将批量数据表与更具体的数据表相关联并且您希望根据更具体的相关行的子集生成批量表的计数的情况特别方便。如果您可以将批量行缩小到 <5%,那么生成的稀疏访问通常会比全表扫描更快。

即您有一个用户表(条件)、一个订单表(数据透视表)和一个行项目表(批量),它们引用了产品的数量。您想要在PostCode“90210”中按用户分组的产品总和。在这种情况下,JOIN 将比使用时小几个数量级WHERE relevant_field IN( SELECT * FROM (...) T2 ),因此速度更快,尤其是当 JOIN 溢出到磁盘时!

于 2021-11-12T21:04:03.323 回答