MySQL可以针对提供的静态数据列表加入表吗?

时间:2011-10-12 11:23:32

标签: mysql sql

考虑下表:

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_metricid。这个查询经常运行,所以我想让它尽可能高效。

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_keysort_metric)上有其他索引。

特别是,派生表id对我来说似乎很愚蠢。有没有办法更有效地使用静态提供的列表作为派生表?我无法弄清楚如何做到这一点。任何人吗?

4 个答案:

答案 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)

  

每次

时,I​​N(...)子句中的项目数为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