合并前消除重复项
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 分页。
查询优化总是有两个部分的解决方案。有时是尝试、测量和比较的迭代过程。
- 编写引擎可以运行 高效地(准确)的查询。
- 确保适当的索引可用,以便优化器可以选择一个好的执行计划。
最好的查询很可能是直接而简单的:
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 行标记。
我需要运行一个查询,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 分页。
查询优化总是有两个部分的解决方案。有时是尝试、测量和比较的迭代过程。
- 编写引擎可以运行 高效地(准确)的查询。
- 确保适当的索引可用,以便优化器可以选择一个好的执行计划。
最好的查询很可能是直接而简单的:
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 行标记。