如何避免某些行的子查询执行?

时间:2016-06-18 10:32:44

标签: mysql sql

我有这张桌子:

// QandA
+----+--------+----------------------------------------+------+---------+
| id |  title |                  content               | type | related |
+----+--------+----------------------------------------+------+---------+
| 1  | title1 | content of question1                   | 0    | NULL    |
| 2  |        | content of first answer for question1  | 1    | 1       |
| 3  | title2 | content of question2                   | 0    | NULL    |
| 4  | title3 | content of question3                   | 0    | NULL    |
| 5  |        | content of second answer for question1 | 1    | 1       |
| 6  |        | content of first answer for question3  | 1    | 4       |
| 7  | title4 | content of question4                   | 0    | NULL    |
| 8  |        | content of first answer for question2  | 1    | 3       |
+----+--------+----------------------------------------+------+---------+
-- type colum: it is 0 for questions and 1 for answers.
-- related column: it is NULL for questions and {the id of its own question} for answers.

我还有另外两张表:

// interface_tags
+---------+--------+
| post_id | tag_id |
+---------+--------+
| 1       | 1      |
| 1       | 5      |
| 3       | 4      |
| 4       | 1      |
| 4       | 2      |
| 4       | 5      |
| 7       | 2      |
+---------+--------+

// tags
+----+----------+
| id | tag_name |
+----+----------+
| 1  | PHP      |
| 2  | SQL      |
| 3  | MySQL    |
| 4  | CSS      |
| 5  | Java     |
| 6  | HTML     |
| 7  | JQuery   |
+----+----------+

这是我的疑问:

SELECT id,
       title,
       content
       (SELECT i_t.tag_name
        FROM tags t
        JOIN interface_tags i_t
        ON t.id = i_t.tag_id
        WHERE i_t.post_id = :id1) tag
FROM QandA
WHERE id = :id2 OR related = :id3

-- Note: :id1, :id2 and :id3 are identical

如您所见,我的查询同时选择了问题(id = :id2)及其所有答案(related = :id3)。有一个子查询可以获取问题的所有标签。但它将按行执行。因此,子查询将针对问题和答案执行。有很多浪费过程,因为答案没有标签。

我的问题是什么?如何避免执行该子查询以获得答案?我的意思是我只想针对这个问题执行该子查询。

编辑:这是预期的输出:

-- :id = 1

// QandA
+----+--------+----------------------------------------+------------+
| id |  title |                  content               |   tag      |
+----+--------+----------------------------------------+------------+
| 1  | title1 | content of question1                   | PHP, JAVA  |
| 2  |        | content of first answer for question1  |            |
| 5  |        | content of second answer for question1 |            |
+----+--------+----------------------------------------+------------+

4 个答案:

答案 0 :(得分:2)

你需要CASE:

SELECT id,
       title,
       content,
       CASE
            WHEN type = 0 THEN      -- get tags only when post type is QUESTION
                (SELECT i_t.tag_name
                FROM tags t
                JOIN interface_tags i_t
                ON t.id = i_t.tag_id
                WHERE i_t.post_id = :id1)
            ELSE        -- else consider it an ANSWER and don't get tags
                NULL
        END tag
FROM QandA
WHERE id = :id2 OR related = :id3

答案 1 :(得分:1)

你试过这个吗?

SELECT q.id,
       q.title,
       q.content,
       GROUP_CONCAT(i_t.tag_name)       
FROM QandA q
LEFT JOIN interface_tags i_t
    ON q.id = i_t.post_id
LEFT JOIN tags t
    ON t.id = i_t.tag_id    
WHERE 
    q.id = :id2 OR q.related = :id3
GROUP BY
    q.id    

这是您想要的可能解决方案。

答案 2 :(得分:1)

为什么不用UNION执行两个单独的查询?

SELECT id, title, content,
       (SELECT GROUP_CONCAT(t.tag_name)
        FROM tags t
        JOIN interface_tags i_t
        ON t.id = i_t.tag_id
        WHERE i_t.post_id = :id1) tag
FROM QandA WHERE id = :id2
UNION ALL
SELECT id, title, content, NULL
FROM QandA WHERE related = :id3

sqlfiddle

答案 3 :(得分:0)

您可以使用CASE表达式,并添加1个参数,因此我们有:id1 ...:id4(全部相同)

SELECT 
    id,
    title,
    content
    CASE WHEN id = :id1 
        THEN (SELECT i_t.tag_name
            FROM tags t
            JOIN interface_tags i_t
            ON t.id = i_t.tag_id
            WHERE i_t.post_id = :id2) 
    END tag
FROM 
    QandA
WHERE 
    id = :id3 OR related = :id4;