【问题标题】:How to find rows in SQL that end with the same string?如何在 SQL 中查找以相同字符串结尾的行?
【发布时间】:2017-09-19 18:27:46
【问题描述】:

我有一个与此处发现的问题类似的问题:How to find rows in SQL that start with the same string (similar rows)?,此解决方案适用于 MySQL 5.6,但不适用于 5.7。

我有一个包含多个列的数据库 (t),其中重要的是 id 和文件路径,我想要完成的是检索所有最后 5 个字符相同的文件路径。以下在 MySQL5.6 中工作,第二个 SELECT 在 5.7 中工作正常:

SELECT id, filepath FROM t
WHERE SUBSTRING(filepath, -5) IN
(
  SELECT SUBSTRING(filepath, -5) 
  FROM t 
  GROUP BY SUBSTRING(filepath, -5)
  HAVING COUNT(*) > 1
)

但是当我尝试在 5.7 上运行它时,我得到了错误

Expression #1 of HAVING clause is not in GROUP BY clause and contains 
nonaggregated column 't.filepath' which is not functionally dependent on 
columns in GROUP BY clause; this is incompatible with 
sql_mode=only_full_group_by

样本数据:

id     filepath
1      /Desktop/file1.txt
2      /Desktop/file2.txt
3      /Desktop/file1.txt

我想返回 id 为 1 和 3 的行。我该如何为 MySQL5.7 解决这个问题?

编辑:还有人可以指出我正确的方向,让 SQL 删除重复项吗?所以我想删除 id 3 的条目,但保留 id 1 和 2 的条目。

【问题讨论】:

  • 您正在查看的数据是一个庞大的数据库还是更小?如果不是太大,您可以选择整个文件路径列并使用 unix 工具来执行此操作。
  • 太大了,有几百万个数据点
  • @lad2025 不适用于 mysql 5.7

标签: mysql mysql-5.7 mysql-5.6


【解决方案1】:

请阅读有关 GROUP BY 和 sql_mode only_full_group_by 主题的 mysql 文档(如您的错误消息所述): https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html

我认为将内部查询更改为此可能会解决问题:

SELECT SUBSTRING(filepath, -5) AS fpath
FROM t 
GROUP BY fpath
HAVING COUNT(fpath) > 1

编辑:

关于为什么添加“AS fpath”有效的问题: 添加别名“fpath”只是一种干净的方法。 ONLY_FULL_GROUP_BY 的要点是,您在 SELECT、HAVING 或 ORDER BY 中使用的每个字段也必须在 GROUP BY 中。

所以我出于多种原因添加了 fpath-alias:

  • 为了性能:你写的查询有 SUBSTRING(filepath, -5) 两次,这 对性能不利。 Mysql 必须执行两次 SUBSTRING 调用, 而在我的情况下,它只需要执行一次(每行)。
  • 解决分组问题:你有 COUNT(),但“”不在你的 GROUP BY 语句中(我什至不确定是否将是可能的)。您必须计算“某物”,因此由于“fpath”在您的 SELECT 和 GROUP BY 中,使用它作为您的 COUNT() 可以解决问题。

【讨论】:

  • 谢谢,我没有尝试按照您的建议更改查询(每次运行大约需要 30 分钟),但我删除了 only_full_group_by 的设置,到目前为止它似乎工作正常。谢谢!
  • 没问题。因此,您可以将自己的答案标记为答案,或者我的答案或其他任何答案。
  • 像这样更改查询有效;为什么添加“AS”语句并将 * 更改为 fpath 可以解决此问题?再次感谢您!
【解决方案2】:

我不喜欢将子查询放在 IN() 谓词中,因为 MySQL 倾向于多次运行子查询。

您可以以不同的方式编写查询,将子查询作为派生表放在FROM 子句中。这将使 MySQL 只运行一次子查询。

SELECT id, filepath 
FROM (
  SELECT SUBSTRING(filepath, -5) AS suffix, COUNT(*) AS count
  FROM t 
  GROUP BY suffix
  HAVING count > 1
) AS t1
JOIN t AS t2 ON SUBSTRING(t2.filepath, -5) = t1.suffix

不过,这必然会进行表扫描,因此这将是一个代价高昂的查询。在进行这样的子字符串比较时,它不能使用索引。

为了优化这一点,您可以创建一个带有索引的虚拟列。

ALTER TABLE t 
  ADD COLUMN filepath_last VARCHAR(10) AS (SUBSTRING_INDEX(filepath, '/', -1)),
  ADD KEY (filepath_last);

那你可以这样查询,至少子查询使用索引:

SELECT id, filepath 
FROM (
  SELECT filepath_last, COUNT(*) AS count
  FROM t 
  GROUP BY filepath_last
  HAVING count > 1
) AS t1
STRAIGHT_JOIN t AS t2 ON t2.filepath_last = t1.filepath_last

【讨论】:

  • 第一部分成功了,谢谢你的回答!但是,它只比上面建议的编辑(花了 21 分钟)少了 2 分钟。不幸的是,我也无法更改查询的数据库。
【解决方案3】:

最终为我工作的解决方案在这里找到:Disable ONLY_FULL_GROUP_BY

我运行SELECT @@sql_mode,然后运行SET @@sql_mode =,后跟一个包含第一个查询返回的所有值的字符串,only_full_group_by 除外,但我仍然对如何在不更改 SQL 设置的情况下完成此操作感兴趣。

【讨论】:

    猜你喜欢
    • 2011-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-14
    相关资源
    最近更新 更多