合并前消除重复项

Eliminate duplicates before union

我需要运行一个查询,select来自一个大table的两列(3m+行,有selecting两列,结果集是大约 6-7 米)和 returns 列表。所以我使用 union 将列合并到列表中并消除重复项。问题是我无法在一个查询中得到 return 结果,我需要对其进行分区,所以我对子查询应用了 LIMIT ?,? ,应用层通过 Prepared Statements 设置。

SELECT val
FROM 
(
    (SELECT fs.smr as val
    FROM `fr_search` as fs
    ORDER BY val LIMIT ?,?)

    UNION

    (SELECT fs.dmr as val
    FROM `fr_search` as fs
    ORDER BY val LIMIT ?,?)
) as vals
GROUP BY val

问题:并集消除了重复项,但仅在应用 LIMIT 之后。含义 如果两个查询 returns 100+100=200 行并且其中大部分是重复的,我只 return <200 行。我怎样才能对这样的查询应用限制,我可以 return 特定数量的行? (如果我在子查询之后应用LIMIT,到运行需要两分钟多,所以不能解决问题。)

您有两个选择:

你可以SELECT DISTINCT在内部和外部查询:

SELECT DISTINCT val
FROM 
(
    (SELECT DISTINCT fs.smr as val
    FROM `fr_search` as fs)

    UNION ALL

    (SELECT DISTINCT fs.dmr as val
    FROM `fr_search` as fs)
) as vals
ORDER BY val LIMIT ?,?;

或者您也可以先按内部查询分组,然后再按外部查询分组。

SELECT val
FROM 
(
    (SELECT fs.smr as val
    FROM `fr_search` as fs
    GROUP BY fs.smr)

    UNION ALL

    (SELECT fs.dmr as val
    FROM `fr_search` as fs
    GROUP BY fs.dmr)
) as vals
GROUP BY val
ORDER BY val LIMIT ?,?;

在这个特定的场景中,两者基本上会做同样的事情。但是,在两者中您都应该使用 union all,这样 UNION 部分就不会单独工作,并且您明确了您希望如何对记录进行分组。我还将 limit 子句移动到外部查询

您实际上不需要子查询。以下将适用于前 100 行:

 (SELECT DISTINCT fs.smr as val
  FROM `fr_search` as fs
  ORDER BY val
  LIMIT 100
 )
 UNION
 (SELECT DISTINCT fs.dmr as val
  FROM `fr_search` as fs
  ORDER BY val
  LIMIT 100
 )
 ORDER BY val
 LIMIT 100;

但是,一旦开始输入偏移量,它就会变得更加复杂。对于接下来的 100 行:

 (SELECT DISTINCT fs.smr as val
  FROM `fr_search` as fs
  ORDER BY val
  LIMIT 200
 )
 UNION
 (SELECT DISTINCT fs.dmr as val
  FROM `fr_search` as fs
  ORDER BY val
  LIMIT 200
 )
 ORDER BY val
 LIMIT 100, 100;

问题是你不知道第二组从哪里来。

如果您确实需要对结果集进行分页,我建议您将其存储在临时 table 中并从临时 table 分页。

查询优化总是有两个部分的解决方案。有时是尝试、测量和比较的迭代过程。

  1. 编写引擎可以运行 高效地(准确)的查询。
  2. 确保适当的索引可用,以便优化器可以选择一个好的执行计划。

最好的查询很可能是直接而简单的:

SELECT  v.val
FROM    (
        SELECT  fs.smr as val
        FROM    `fr_search` as fs
        UNION
        SELECT  fs.dmr as val
        FROM    `fr_search` as fs
        ) as v
ORDER BY v.val LIMIT ?,?;

为了 运行 高效,您需要 2 个索引:

  • 一个在 fr_search.smr
  • 另一个在 fr_search.dmr

如果优化器无法处理上述情况,请尝试使用索引提示强制其使用索引。

在极端情况下,您可以尝试通过以下方式强制解决问题:

SELECT  v.val
FROM    (
        SELECT  DISTINCT fs.smr as val
        FROM    `fr_search` as fs
        ORDER BY fs.smr LIMIT ?
        UNION
        SELECT  DISTINCT fs.dmr as val
        FROM    `fr_search` as fs
        ORDER BY fs.dmr LIMIT ?
        ) as v
ORDER BY v.val LIMIT ?,?;

请注意,您的替换(假设 100 页)应如下所示:

Page 1: 100, 100, 100, 0
Page 2: 200, 200, 100, 100
Page 3: 300, 300, 100, 200
Page 4: 400, 400, 100, 300
etc.

原因是,您需要解决可能有利于 table 的跨列排序不平衡问题。例如第 4 页:

  • 从每列中获取按键排序的前 400 个不同的行。
  • Return 合并数据的第 301 到 400 行。
  • 这可能是其中一个子查询的最后 400 行。但它更有可能 return 来自每个子查询的大约 50 行,高于 150 行标记。