簡體   English   中英

自我修改和按修改過的行過濾的高效方式

[英]Performant way to self-join and filter by revised rows

我試圖選擇此表中的所有行,但要限制選擇的是修訂ID而不是原始ID。 因此,如果某行具有修訂,則選擇該修訂而不是該行,如果有多個修訂號,則首選最高修訂號。

我認為示例表,輸出和查詢將更好地解釋這一點:

表:

+----+-------+-------------+-----------------+-------------+
| id | value | original_id | revision_number | is_revision |
+----+-------+-------------+-----------------+-------------+
|  1 | abcd  | null        | null            |           0 |
|  2 | zxcv  | null        | null            |           0 |
|  3 | qwert | null        | null            |           0 |
|  4 | abd   | 1           | 1               |           1 |
|  5 | abcde | 1           | 2               |           1 |
|  6 | zxcvb | 2           | 1               |           1 |
|  7 | poiu  | null        | null            |           0 |
+----+-------+-------------+-----------------+-------------+

所需輸出:

+----+-------+-------------+-----------------+
| id | value | original_id | revision_number |
+----+-------+-------------+-----------------+
|  3 | qwert | null        | null            |
|  5 | abcde | 1           | 2               |
|  6 | zxcvb | 2           | 1               |
|  7 | poiu  | null        | null            |
+----+-------+-------------+-----------------+

查看被稱為的revisions_max

SELECT 
    responses.original_id AS original_id,
    MAX(responses.revision_number) AS revision
FROM
    responses
 WHERE
    original_id IS NOT NULL   
GROUP BY responses.original_id

我當前的查詢:

SELECT
    responses.*
FROM
    responses
WHERE
    id NOT IN (
        SELECT
            original_id
        FROM
            revisions_max
    )
AND
    is_revision = 0

UNION

SELECT
    responses.*
FROM
    responses
INNER JOIN revisions_max ON revisions_max.original_id = responses.original_id
    AND revisions_max.revision_number = responses.revision_number

該查詢有效,但是需要0.06秒才能運行。 與只有2000行的表。 該表將迅速開始擴展到成千上萬的行。 union下的查詢是大多數時間的事情。

我該怎么做才能提高查詢性能?

我將對任何其他DBMS采取的方法是使用NOT EXISTS

SELECT  r1.*
FROM    Responses AS r1
WHERE   NOT EXISTS
        (   SELECT  1
            FROM    Responses AS r2
            WHERE   r2.original_id = COALESCE(r1.original_id, r1.id)
            AND     r2.revision_number > COALESCE(r1.revision_number, 0)
        );

刪除同一ID(或original_id(如果已填充)的版本號更高)的任何行。 但是,在MySQL中, LEFT JOIN/IS NULL將優於NOT EXISTS 1 因此,我將以上內容重寫為:

SELECT  r1.*
FROM    Responses AS r1
        LEFT JOIN Responses AS r2
            ON r2.original_id = COALESCE(r1.original_id, r1.id)
            AND r2.revision_number > COALESCE(r1.revision_number, 0)
WHERE   r2.id IS NULL;

關於DBFiddle的示例

我意識到您已經說過,您不想使用LEFT JOIN並檢查null,但是我看不出有更好的解決方案。


1.至少從歷史上看是這樣,我不積極使用MySQL,所以不要跟上優化器的最新發展

使用coalesce()怎么樣?

SELECT COALESCE(y.id, x.id)                           AS id,
       COALESCE(y.value, x.value)                     AS value,
       COALESCE(y.original_id, x.original_id)         AS original_id,
       COALESCE(y.revision_number, x.revision_number) AS revision_number
FROM   responses x
       LEFT JOIN (SELECT r1.*
                  FROM   responses r1
                         INNER JOIN (SELECT responses.original_id          AS
                                            original_id,
                                            Max(responses.revision_number) AS
                                            revision
                                     FROM   responses
                                     WHERE  original_id IS NOT NULL
                                     GROUP  BY responses.original_id) rev
                                 ON r1.original_id = rev.original_id
                                    AND r1.revision_number = rev.revision) y
              ON x.id = y.original_id
WHERE  y.id IS NOT NULL
        OR x.original_id IS NULL;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM