在 mysql 上优化查询 sql

Optimise query sql on mysql

我有这样的查询:

 DELETE FROM doublon  WHERE id in 
( Select id  from `doublon` where `id` not in
    ( Select id
         From `doublon` 
         group  by etablissement_id,amenities_id
         having Count(etablissement_id) > 1  and Count(amenities_id) > 1
         union
      Select id
         From `doublon` 
         group  by etablissement_id,amenities_id
         having Count(etablissement_id) = 1  and Count(amenities_id) = 1
     )
 )

我的table'doublon'的结构是这样的:

id
etablissement_id
amenities_id

结构table是这样的:

http://hpics.li/bbb5eda

我有 200 万行,查询速度很慢,很多小时.. 有人知道如何优化此查询以更快地执行吗?

SqlFiddle

如果我没记错的话应该可以

DELETE FROM doublon
WHERE  id IN (SELECT id
              FROM   doublon
              WHERE  id NOT IN (SELECT id
                                FROM   doublon
                                GROUP  BY etablissement_id,
                                          amenities_id
                                HAVING Count(etablissement_id) >= 1
                                       AND Count(amenities_id) >= 1)) 

首先,您的查询不正确。但请继续阅读,可能在回答结束时我发现了您需要这种奇怪查询的原因。

让我们讨论最后一个子查询:

Select id
From `doublon` 
group  by etablissement_id,amenities_id
having Count(etablissement_id) = 1  and Count(amenities_id) = 1

您可以在具有 GROUP BY 的查询的 SELECT 子句中使用列,前提是至少发生以下情况之一:

  • 它也出现在 GROUP BY 子句中;
  • 它被用作aggregate function的参数;
  • 该列的值在功能上取决于 GROUP BY 子句中存在的列的值;例如,如果存在具有 UNIQUE 索引的列(或存在于 table 的 UNIQUE 索引中的所有列)。

id 列不符合上述任何情况1。这使得查询 illegal 根据 SQL 规范。

然而,

MySQL 接受它并努力为其生成结果集,但它在 documentation:

中表示

... the server is free to choose any value from each group, so unless they are the same, the values chosen are indeterminate, which is probably not what you want.

HAVING 子句包含 Count(etablissement_id)Count(amenities_id)。当 etablissement_idamenities_id 都不是 -NULL 时,这两个表达式具有相同的值并且与 COUNT(*)(组中的行数)相同。它总是大于 0(一个组不能包含 0 行)。

对于etablissement_idamenities_idNULL时生成的组对应的COUNT()returns0。这也适用于两者同时 NULL 的情况。

使用此信息,此查询 return 行的 id 行的组合(etablissement_idamenities_id)在 table 中是唯一的(这些组只包含一行)并且两个字段都不是 NULL.

其他 GROUP BY 查询(即 UNION-ed 与此查询)returns 来自行组的不确定值其组合 (etablissement_idamenities_id) 在 table 中不是唯一的(并且两个字段都不是 NULL),如文档中引用的片段中所述。

似乎 UNION 从每组 (etablissement_id, amenities_id) 中选择一个(随机)id,其中 etablissement_idamenities_id 不是-NULL。外层 SELECT 打算忽略 UNION 选择的 id 并提供给 DELETE 其余的。

(我认为甚至不需要中间的 SELECT,您可以在 DELETE 查询中使用它的 WHERE 子句)

我能想象你需要 运行 这个查询的唯一原因是 table doublon 是在没有 UNIQUE 的情况下创建的 correspondence table of a many-to-many relationship索引 (etablissement_id, amenities_id)(从相关 table 导入的 FOREIGN KEY 列)。

如果这是你的意图那么有更简单的方法可以实现这个目标。

我将使用正确的结构创建 doublon table 的副本,然后我将使用带有 DISTINCTINSERT ... SELECT 查询从旧的 table 需要的值。然后我会交换 tables 并删除旧的。

查询:

# Create the new table
CREATE TABLE `doublon_fixed` LIKE `doublon`;

# Add the needed UNIQUE INDEX
ALTER TABLE `doublon_fixed`
ADD UNIQUE INDEX `etablissement_amenities`(`etablissement_id`, `amenities_id`);

# Copy the needed values
INSERT INTO `doublon_fixed` (`etablissement_id`, `amenities_id`)
SELECT DISTINCT `etablissement_id`, `amenities_id`
FROM `doublon`;

# Swap the tables
RENAME TABLE `doublon` TO `doublon_old`, `doublon_fixed` TO `doublon`;

# Remove the old table
DROP TABLE `doublon_old`;

RENAME 查询以原子方式从左到右操作重命名。避免停机很有用。


备注:

1 如果 id 列在功能上依赖于 (etablissement_id, amenities_id) 对,则由UNION-ed 查询包含一行。第一个 SELECT 不会产生任何结果,第二个 SELECT 将 return 整个 table).