14

我有一个非常复杂的 MySQL 查询,其中包括三次使用相同的子查询。MySQL 会实际运行子查询 3 次吗?(这是一个昂贵的。)如果是这样,我有没有办法告诉 MySQL 保存或缓存结果,所以它不会那样做?我可以将数据保存在一个大数组中,然后将其重新输入 MySQL,但我不想像那样将它移出并放回数据库中。

这是出现 3 次的子查询:

SELECT id FROM programs 
WHERE submitter_id=32 AND id in (
    SELECT id FROM programs 
    WHERE feed_id=2478 AND id in (
        SELECT program_id FROM playlist_program_map 
        WHERE playlist_id=181)))

这是查询出现的完整查询的示例:

SELECT object_id, programs.created AS created, 
MATCH(text) AGAINST ('excellent ' IN BOOLEAN MODE) AS relevance 
FROM comments_programs USE INDEX (text) 
LEFT JOIN programs ON programs.id=object_id 
WHERE object_id IN (
    SELECT id FROM programs 
    WHERE 1 AND id IN (
        SELECT id FROM programs 
        WHERE submitter_id=32 AND id in (
            SELECT id FROM programs 
            WHERE feed_id=2478 AND id in (
                SELECT program_id FROM playlist_program_map 
                WHERE playlist_id=181)))) 
AND MATCH(text) AGAINST ('excellent ' IN BOOLEAN MODE)>0)

UNION (

SELECT object_id, programs.created AS created, 
MATCH(text) AGAINST ('excellent ' IN BOOLEAN MODE) AS relevance 
FROM descriptions_programs USE INDEX (text) 
LEFT JOIN programs ON programs.id=object_id 
WHERE object_id IN (
    SELECT id FROM programs 
    WHERE 1 AND id IN (
        SELECT id FROM programs 
        WHERE submitter_id=32 AND id in (
            SELECT id FROM programs 
            WHERE feed_id=2478 AND id in (
                SELECT program_id FROM playlist_program_map 
                WHERE playlist_id=181)))) 
AND MATCH(text) AGAINST ('excellent ' IN BOOLEAN MODE)>0 AND current=1 ) 

UNION (

SELECT object_id, programs.created AS created, 
MATCH(text) AGAINST ('excellent ' IN BOOLEAN MODE) AS relevance 
FROM titles_programs USE INDEX (text) 
LEFT JOIN programs ON programs.id=object_id 
WHERE object_id IN (
    SELECT id FROM programs 
    WHERE 1 AND id IN (
        SELECT id FROM programs 
        WHERE submitter_id=32 AND id in (
            SELECT id FROM programs 
            WHERE feed_id=2478 AND id in (
                SELECT program_id FROM playlist_program_map 
                WHERE playlist_id=181)))) 
AND MATCH(text) AGAINST ('excellent ' IN BOOLEAN MODE)>0 AND current=1;
4

2 回答 2

9

看看怎么EXPLAIN EXTENDED说。

如果显示DEPENDENT SUBQUERYor UNCACHEABLE SUBQUERY,则每次使用时都会重新评估。

如果子查询使用会话变量或者是相关子查询,就会发生这种情况。

如果没有,它很可能会被缓存。

If your case the subquery will not be cached, it will be reevaluated in each UNION'ed set.

You subquery, though, seems to be too complicated. Why don't you just use:

SELECT id
FROM   playlist_program_map ppm, programs p
WHERE  ppm.playlist_id = 181
       AND p.id = ppm.program_id
       AND submitter_id = 32
       AND feed_id = 2478

If you have an index on playlist_program_map (playlist_id), this query should work like a charm.

Could you please tell me two more things:

  1. How many rows are there in playlist_program_map and how many DISTINCT playlist_id values are there?
    • How many rows are there in programs and how many DISTINCT submitter_id, feed_id pairs are there?

From your comment I can conclude that there are 10 programs per playlist in average, and 200 programs per (submitter, feed) pair. This means your index on playlist_program_map is more selective than the one on (submitter, feed), and playlist_program_map must be leading in the join.

The fulltext index in your case also doesn't seem to be very selective, given that you need to join 10 programs out of 2,000,000.

You may better try the following:

SELECT object_id, programs.created AS created
FROM   playlist_program_map ppm, programs p, comments_programs cp
WHERE  ppm.playlist_id = 181
       AND p.id = ppm.program_id
       AND p.submitter_id = 32
       AND p.feed_id = 2478
       AND cp.object_id = p.id
       AND cp.text REGEXP 'excellent'

, and repeat this for all three tables.

于 2009-03-18T16:13:42.190 回答
0

For whatever reason, mysql IN clauses with a sub-select run very slow. Better to use join. Your sub-query becomes:

SELECT id from programs P1 INNER JOIN programs P2 ON P1.id = P2.id INNER JOIN playlist_program_map PMAP ON P2.id = PMAP.program_id WHERE P1.submitter_id=32 AND P2.feed_id=2478 AND PMAP.playlist_id=181

It will run much faster.

于 2012-03-09T01:21:17.683 回答