2011-10-12 43 views
1

考虑这个表:MySQL可能会根据提供的静态数据列表加入表格吗?

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 | 
+-------+-------+-----+-------------+ 

有此表近10000行。我有一个查询,它返回正确的结果,但似乎是乞求优化。

查询返回从该表中通过(sort_metricid)对于每个f_key下令f_keyname。这个查询经常运行,所以我想尽可能提高效率。

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 
) 

IN(...)子句中的项目数每次都是24,但这些项目不是顺序的,并且经常变化。 id是主键和我对(f_key)和(sort_metricid)额外的索引。

尤其是派生表SELECT DISTINCT f_key FROM names WHERE f_key IN (254, 257, ..., 273, 279)似乎傻我。是否没有办法更有效地使用静态提供的列表作为派生表?我无法弄清楚如何做到这一点。任何人?

回答

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

...或者使用最大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 
+0

谢谢你。我能够a)理解它并且b)使其适应我的项目,并且它似乎工作。 (我看到你做了一些编辑,我必须做出相同的编辑=) – mkoistinen

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; 
+1

伯努瓦,你的解决问题的方法主要工作,是,其实非常相似,@ symcbean的。但是,它不符合我提供f_keys列表的需要,并且只返回整个列表。这似乎很小,但最终,symcbean的更完整。尽管我在努力解决你的问题! – mkoistinen

+0

@mkoistinen:其实我忘了在'names_a.f_key'上报告你的情况。这里是。 – Benoit

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 
+0

这将只返回一行。我需要每行f_key一行。 – mkoistinen

+0

@mkoistinen,nope,它会返回许多行,并为每个f_key返回多行。 – TMS

+0

多行,真的吗?你试过了吗?此外,我需要每行f_key一行。具体而言,sort_metric最小或id最小的一个,当sort_metric最小时有多行。 – mkoistinen

2

如果我理解你的查询正确,你打算选择具有最低sort_metric的记录,如果有更多的这些在一个具有最低ID。你的双重selfjoin是由于不想重复()列表中的丑陋而引起的。 另一种实现这种方式是通过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 
    ; 
+0

谢谢wildplasser。不幸的是,当你还在提交时,我正在选择一个答案。这看起来不错,但我很好奇,如果这可以在MySQL中工作? – mkoistinen

+0

我也期待有人提出一个WINDOW解决方案。 – wildplasser

+0

我对MySQL不太了解。 IIRC它确实有CTE,但它没有递归查询。我发布它只是因为它是一个相对干净的解决方案,并且该帖子被标记为SQL。因人而异。 – wildplasser