web-dev-qa-db-ja.com

MySQL 5.1.73でWITHコマンドをエミュレートすることは可能ですか?

次のクエリがあります。

SELECT
    COUNT(*) AS `count`
FROM
    agreements
WHERE
    start_date >= STR_TO_DATE('14-01-2015','%d-%m-%Y')
    AND
    start_date < STR_TO_DATE('15-01-2015','%d-%m-%Y')
    AND
    active = 1
    AND
    (driv_id, veh_id) NOT IN (
        SELECT
            a.driv_id, a.veh_id
        FROM
            agreements a
        INNER JOIN
            types t
            ON
            a.agreement_type_id = t.id
        WHERE
            DATE_ADD(a.start_date,INTERVAL t.initial_term YEAR) <
                DATE_SUB(STR_TO_DATE('14-01-2015','%d-%m-%Y'),INTERVAL 2 MONTH)
    )

次に、同じクエリを実行する必要がありますが、1つの変更を加えて、NOT句の(driv_id, veh_id) NOT IN ...部分からWHEREを削除します。

これは明らかに良くありません。なぜなら、DRYに違反する1つの単語を除いたクエリを文字通りコピーして貼り付けるからです。

グーグルで検索してWITHコマンドを見つけましたが、MySQL5.1.73では使用できません。 WITHを使用して実行したクエリは次のとおりです。

WITH tuples AS
(
        SELECT
            a.driv_id, a.veh_id
        FROM
            agreements a
        INNER JOIN
            types t
            ON
            a.agreement_type_id = t.id
        WHERE
            DATE_ADD(a.start_date,INTERVAL t.initial_term YEAR) < 
                DATE_SUB(STR_TO_DATE('14-01-2015','%d-%m-%Y'),INTERVAL 2 MONTH)
)
SELECT
    SUM(IF((a.driv_id, a.veh_id) IN `tuples`, 1, 0)) as in_sum
    SUM(IF((a.driv_id, a.veh_id) NOT IN `tuples`, 1, 0)) as not_in_sum
FROM
    agreements
WHERE
    start_date >= STR_TO_DATE('14-01-2015','%d-%m-%Y')
    AND
    start_date < STR_TO_DATE('15-01-2015','%d-%m-%Y')
    AND
    active = 1

サブクエリの結果をtuplesという一時テーブルに保存し、(メインクエリで)次を選択して、一時テーブルでもこれを実行してみました。

SUM(IF((a.driv_id, a.veh_id) IN tuples, 1, 0)) as in_sum
SUM(IF((a.driv_id, a.veh_id) NOT IN tuples, 1, 0)) as not_in_sum

しかし、これは構文エラーをスローします(理由はよくわかりません)。

おそらく結合を使用してこれを行う方法はありますか(結合クエリを実行しようとしましたが、ブレークスルーはありませんでした)?

1
Jacob F

はい。外部結合を使用してクエリを書き換えることで、サブクエリを繰り返さずに両方の結果を返すことができます。以下は、お使いのバージョンと同じ結果を返します。

_SELECT
    COUNT(*) AS `count`
FROM
    agreements AS ag
LEFT JOIN
    (
        SELECT
            a.driv_id, a.veh_id
        FROM
            agreements a
        INNER JOIN
            types t
            ON
            a.agreement_type_id = t.id
        WHERE
            DATE_ADD(a.start_date,INTERVAL t.initial_term YEAR) < DATE_SUB(STR_TO_DATE('14-01-2015','%d-%m-%Y'),INTERVAL 2 MONTH)
    ) AS s ON ag.driv_id = s.driv_id AND ag.veh_id = s.veh_id
WHERE
    ag.start_date >= STR_TO_DATE('14-01-2015','%d-%m-%Y')
    AND
    ag.start_date < STR_TO_DATE('15-01-2015','%d-%m-%Y')
    AND
    ag.active = 1
    AND
    s.driv_id IS NULL
;
_

それは事実上あなたの_not_in_sum_になります。両方のカウントを一度に返すには、where句からNULLチェックを削除し、次のようにSELECTCOUNT(s.driv_id)を使用します。

_SELECT
    COUNT(s.driv_id) AS in_sum,
    COUNT(*) - COUNT(s.driv_id) AS not_in_sum
FROM
    ...
_

サブクエリが重複を返す可能性がある場合は、それにDISTINCTを追加します。そうしないと、_in_sum_が誤った結果を返す可能性があります。

WITH句については、まだ正式にリリースされていないバージョン5.7のマニュアルにも記載されていませんが、一時テーブルを使用してエミュレートすることはおそらく理にかなっています。そのメソッドを試しているときに発生したエラーは、試行したIN/_NOT IN_の右側が単なるテーブル名であったのに対し、サブクエリである必要があったためです。

_(a.driv_id, a.veh_id) IN (SELECT driv_id, veh_id FROM tuples)
_
2
Andriy M