sql >> データベース >  >> RDS >> Mysql

MySql:複数の左結合が間違った出力を与える

    結果をフラット化する> 正しいカウントを取得するために、クエリの

    ファイルテーブルから他のテーブルへの1対多の関係があるとおっしゃいました

    SQLにキーワードLOOKUPしかない場合 JOINにすべてを詰め込む代わりに キーワードの場合、JOINを使用すると、テーブルAとテーブルBの関係が1対1であるかどうかを簡単に推測できます。 自動的に1対多を意味します。私は逸脱します。とにかく、私はあなたのファイルがdm_dataに対して1対多であることをすでに推測しているはずです。また、kc_dataに対するファイルも1対多です。 LEFT JOIN 最初のテーブルと2番目のテーブルの関係が1対多であることを示すもう1つのヒントです。ただし、これは決定的なものではありません。一部のコーダーは、すべてをLEFT JOINで記述するだけです。 。クエリのLEFTJOINに問題はありませんが、クエリに1対多のテーブルが複数ある場合は必ず失敗し、クエリは他の行に対して繰り返し行を生成します。

    from
        files
            left join
        dm_data ON dm_data.id = files.id
            left join
        kc_data ON kc_data.id = files.id
    

    したがって、この知識を使用して、ファイルがdm_dataに対して1対多であり、kc_dataに対しても1対多であることを示します。これらの結合をチェーンし、1つのモノリシッククエリにグループ化することに問題があると結論付けることができます。

    3つのテーブル、つまりapp(files)、ios_app(dm_data)、android_app(kc_data)があり、これがたとえばiosのデータである場合の例:

    test=# select * from ios_app order by app_code, date_released;
     ios_app_id | app_code | date_released | price  
    ------------+----------+---------------+--------
              1 | AB       | 2010-01-01    | 1.0000
              3 | AB       | 2010-01-03    | 3.0000
              4 | AB       | 2010-01-04    | 4.0000
              2 | TR       | 2010-01-02    | 2.0000
              5 | TR       | 2010-01-05    | 5.0000
    (5 rows)
    

    そしてこれはあなたのAndroidのデータです:

    test=# select * from android_app order by app_code, date_released;
    .android_app_id | app_code | date_released |  price  
    ----------------+----------+---------------+---------
                  1 | AB       | 2010-01-06    |  6.0000
                  2 | AB       | 2010-01-07    |  7.0000
                  7 | MK       | 2010-01-07    |  7.0000
                  3 | TR       | 2010-01-08    |  8.0000
                  4 | TR       | 2010-01-09    |  9.0000
                  5 | TR       | 2010-01-10    | 10.0000
                  6 | TR       | 2010-01-11    | 11.0000
    (7 rows)    
    

    このクエリを使用するだけの場合:

    select x.app_code, 
        count(i.date_released) as ios_release_count, 
        count(a.date_released) as android_release_count
    from app x
    left join ios_app i on i.app_code = x.app_code
    left join android_app a on a.app_code = x.app_code
    group by x.app_code
    order by x.app_code
    

    代わりに出力が間違っています:

     app_code | ios_release_count | android_release_count 
    ----------+-------------------+-----------------------
     AB       |                 6 |                     6
     MK       |                 0 |                     1
     PM       |                 0 |                     0
     TR       |                 8 |                     8
    (4 rows)
    

    連鎖結合はデカルト積と考えることができるため、最初のテーブルに3行あり、2番目のテーブルに2行ある場合、出力は6になります

    これが視覚化です。すべてのiOSABに対して2つのAndroidABが繰り返されていることを確認してください。 3つのiosABがあるので、COUNT(ios_app.date_released)を実行すると何がカウントされますか?それは6になります。 COUNT(android_app.date_released)でも同じです 、これも6になります。同様に、すべてのiosTRに対して4つの繰り返しandroidTRがあり、iosには2つのTRがあるため、8のカウントが得られます。

    .app_code | ios_release_date | android_release_date 
    ----------+------------------+----------------------
     AB       | 2010-01-01       | 2010-01-06
     AB       | 2010-01-01       | 2010-01-07
     AB       | 2010-01-03       | 2010-01-06
     AB       | 2010-01-03       | 2010-01-07
     AB       | 2010-01-04       | 2010-01-06
     AB       | 2010-01-04       | 2010-01-07
     MK       |                  | 2010-01-07
     PM       |                  | 
     TR       | 2010-01-02       | 2010-01-08
     TR       | 2010-01-02       | 2010-01-09
     TR       | 2010-01-02       | 2010-01-10
     TR       | 2010-01-02       | 2010-01-11
     TR       | 2010-01-05       | 2010-01-08
     TR       | 2010-01-05       | 2010-01-09
     TR       | 2010-01-05       | 2010-01-10
     TR       | 2010-01-05       | 2010-01-11
    (16 rows)
    

    したがって、他のテーブルやクエリに結合する前に、各結果をフラット化する必要があります。

    データベースがCTEに対応している場合は、それを使用してください。非常にきちんとしていて、非常に自己文書化されています:

    with ios_app_release_count_list as
    (
     select app_code, count(date_released) as ios_release_count
     from ios_app
     group by app_code
    )
    ,android_release_count_list as
    (
     select app_code, count(date_released) as android_release_count 
     from android_app 
     group by app_code  
    )
    select
     x.app_code, 
     coalesce(i.ios_release_count,0) as ios_release_count, 
     coalesce(a.android_release_count,0) as android_release_count
    from app x
    left join ios_app_release_count_list i on i.app_code = x.app_code
    left join android_release_count_list a on a.app_code = x.app_code
    order by x.app_code;
    

    一方、MySQLのようにデータベースにCTE機能がまだない場合は、代わりにこれを行う必要があります。

    select x.app_code, 
     coalesce(i.ios_release_count,0) as ios_release_count, 
     coalesce(a.android_release_count,0) as android_release_count
    from app x
    left join
    (
     select app_code, count(date_released) as ios_release_count
     from ios_app
     group by app_code
    ) i on i.app_code = x.app_code
    left join
    (
     select app_code, count(date_released) as android_release_count 
     from android_app 
     group by app_code   
    ) a on a.app_code = x.app_code
    order by x.app_code
    

    そのクエリとCTEスタイルのクエリは正しい出力を表示します:

     app_code | ios_release_count | android_release_count 
    ----------+-------------------+-----------------------
     AB       |                 3 |                     2
     MK       |                 0 |                     1
     PM       |                 0 |                     0
     TR       |                 2 |                     4
    (4 rows)
    

    ライブテスト

    不正なクエリ: http://www.sqlfiddle.com/#!2/9774a/ 2

    正しいクエリ: http://www.sqlfiddle.com/#!2/9774a/ 1



    1. OracleとKafkaを統合する方法

    2. json_encodeは配列をオブジェクトに変換します

    3. PHP:mysqli_fetch_array()の結果を2回ループできないのはなぜですか?

    4. SQL Server(T-SQL)のデータベースメールログからイベントを削除する