考虑下表:
TABLE names
+-------+-------+-----+-------------+
| id | f_key |name | sort_metric |
+-------+-------+-----+-------------+
| 1 | 1 | a | 1 |
| 2 | 1 | b | 2 |
| 3 | 1 | c | 0 |
| 4 | 2 | d | 0 |
| 5 | 2 | e | 2 |
| 6 | 2 | f | 1 |
| 7 | 3 | g | 1 |
| 8 | 3 | h | 0 |
...
| 9999 | 2500 | zzz | 2 |
| 10000 | 2500 | zzz | 0 |
+-------+-------+-----+-------------+
此表中有近10,000行。我有一个查询,它会返回正确的结果,但似乎是 begging 进行优化。
对于每个f_key
,查询会从此表中按{({1}},name
)排序sort_metric
和id
。这个查询经常运行,所以我想让它尽可能高效。
f_key
每次IN(...)子句中的项目数为24,但这些项目不是连续的,而是经常更改。 SELECT
name_a.f_key, name_a.name
FROM (
SELECT
DISCTINCT f_key
FROM
names
WHERE
f_key IN ( 254, 257, ..., 273, 279 )
) f_keys
JOIN names names_a ON names_a.id = (
SELECT
names_b.id
FROM
names names_b
WHERE
names_b.f_key = f_keys.f_key
ORDER BY
sort_metric ASC, id ASC LIMIT 1
)
是主键,我在(id
)和(f_key
,sort_metric
)上有其他索引。
特别是,派生表id
对我来说似乎很愚蠢。有没有办法更有效地使用静态提供的列表作为派生表?我无法弄清楚如何做到这一点。任何人吗?
答案 0 :(得分:2)
如果我正确理解您的查询,您打算选择具有最低sort_metric的记录,如果有更多这些记录具有最低id。你的双重自我加入是因为不想复制丑陋的()列表。 另一种实现此目的的方法是通过CTE,只需要一个自连接:
WITH ext AS (
SELECT id,f_key,name,sort_metric
FROM tmp.names
WHERE f_key IN ( 1, 3, 254, 257, 301, 273, 279 )
)
SELECT t1.*
FROM ext t1
WHERE NOT EXISTS (
SELECT *
FROM ext t2
WHERE t2.sort_metric <= t1.sort_metric
AND t2.f_key = t1.f_key
AND t2.id < t1.id
)
ORDER BY t1.id
LIMIT 1
;
答案 1 :(得分:1)
这太复杂了!尝试:
SELECT DISTINCT names_a.f_key, names_a.name
FROM names names_a
LEFT JOIN names names_b ON names_b.f_key = names_a.f_key
AND (names_b.sort_metric < names_a.sort_metric
OR ( names_b.sort_metric = names_a.sort_metrict
AND names_b.id < names_a.id
)
)
WHERE names_a.f_key IN ( 254, 257, ..., 273, 279 )
AND names_b.id IS NULL;
答案 2 :(得分:1)
每次
时,IN(...)子句中的项目数为24
然后使用'IN子句是要走的路 - 但是你提供的查询会做很多不必要的工作(加入一个带有'WHERE'谓词引用的查询实际上工作了吗?????除非我错了,否则这应该产生相同的结果:
SELECT names.f_key, names.name
FROM names
WHERE names.f_key IN ( 254, 257, ..., 273, 279 );
<强>更新强>
好的 - 我看到第二个查询被限制为从每个f_key的名称中选择第一个项目 - 在这种情况下....
SELECT n1.f_key, n1.name
FROM names n1
WHERE n1.f_key IN ( 254, 257, ..., 273, 279 )
AND NOT EXISTS (
SELECT 1 FROM names n2
WHERE n2.f_key=n1.f_key
AND ((n2.sort_metric>n1.sort_metric)
OR (n2.sort_metric=n1.sort_metric
AND n2.id>n1.id))
)
...或使用max concat技巧消除子选择......
SELECT n.f_key, SUBSTRING(
MIN(CONCAT(LPAD(sort_metric, 8, '0'),LPAD(id, 8, '0'), name)),
17) AS name
FROM names n
WHERE n.f_key IN ( 254, 257, ..., 273, 279 )
GROUP BY f_key
答案 3 :(得分:0)
我不确定我是否理解你的问题(如果没有,请更准确地表达你想要的输出),但是从你的句子*“查询返回f_key和这个表中的名称(sort_metric,id)对于每个f_key“*似乎解决方案很简单:
select f_key, name
from names
where f_key IN ( 254, 257, ..., 273, 279 )
order by sort_metric, id