6

クエリで複数の左結合を使用することに少し問題があります。一部のテーブルは左側のテーブルと1対1の関係にあり、一部のテーブルは1対多の関係にあります。クエリは次のようになります。

Select 
    files.filename,
    coalesce(count(distinct case
                when dm_data.weather like '%clear%' then 1
                    end),
            0) as clear,
    coalesce(count(distinct case
                when dm_data.weather like '%lightRain%' then 1
                    end),
            0) as lightRain,
    coalesce(count(case
                when kc_data.type like '%bicycle%' then 1
                    end),
            0) as bicycle,
    coalesce(count(case
                when kc_data.type like '%bus%' then 1
                    end),
            0) as bus,
    coalesce(count(case
                when kpo_data.movement like '%walking%' then 1
                    end),
            0) as walking,
    coalesce(count(case
                when kpo_data.type like '%pedestrian%' then 1
                    end),
            0) as pedestrian
from
    files
        left join
    dm_data ON dm_data.id = files.id
        left join
    kc_data ON kc_data.id = files.id
        left join
    kpo_data ON kpo_data.id = files.id
where
    files.filename in (X, Y, Z, ........)
group by files.filename;

ここで、dm_dataテーブルは「files」テーブルと1対1の関係にあり(そのため、「Distinct」を使用しています)、kc_dataおよびkpo_dataデータは「files」テーブルと1対多の関係にあります。(kc_dataおよびkpo_dataは、1つのfiles.idに対して10〜20行を持つことができます)。このクエリは正常に機能します。

この問題は、別の1対多のテーブルpd_markings(1つのfiles.idに対して数百の行を持つ可能性がある)に別の左結合を追加すると発生します。

Select 
    files.filename,
    coalesce(count(distinct case
                when dm_data.weather like '%clear%' then 1
                    end),
            0) as clear,
    coalesce(count(distinct case
                when dm_data.weather like '%lightRain%' then 1
                    end),
            0) as lightRain,
    coalesce(count(case
                when kc_data.type like '%bicycle%' then 1
                    end),
            0) as bicycle,
    coalesce(count(case
                when kc_data.type like '%bus%' then 1
                    end),
            0) as bus,
    coalesce(count(case
                when kpo_data.movement like '%walking%' then 1
                    end),
            0) as walking,
    coalesce(count(case
                when kpo_data.type like '%pedestrian%' then 1
                    end),
            0) as pedestrian,
    **coalesce(count(case
                when pd_markings.movement like '%walking%' then 1
                    end),
            0) as walking**
from
    files
        left join
    dm_data ON dm_data.id = files.id
        left join
    kc_data ON kc_data.id = files.id
        left join
    kpo_data ON kpo_data.id = files.id
        left join
    **kpo_data ON pd_markings.id = files.id**
where
    files.filename in (X, Y, Z, ........)
group by files.filename;

これで、すべての値が互いに倍数になります。何か案は???

最初の2列は1または0の値を返すことに注意してください。1対1の関係テーブルにはfiles.idに対して1行または0行しかないため、実際には望ましい結果です。したがって、「Distinct」を使用しない場合、結果の値は正しくありません(同じfile.idに対して複数の行を返す他のテーブル)いいえ、残念ながら、私のテーブルには、「files」テーブルを除いて独自の一意のID列がありません。

4

2 に答える 2

7

正しいカウントを取得するには、クエリの結果をフラット化する必要があります。

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

SQLにキーワードLOOKUPを詰め込むのではなく、キーワードしかないJOIN場合、テーブルAとテーブルBの関係が1対1であるかどうかを簡単に推測できます。を使用JOINすると、自動的に1対多を意味します。私は逸脱します。とにかく、私はあなたのファイルがdm_dataに対して1対多であることをすでに推測しているはずです。また、kc_dataに対するファイルも1対多です。LEFT JOIN1番目のテーブルと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

于 2012-05-03T16:35:17.687 に答える
0

ここであなたの明確な使用法に疑問を投げかけます-それが書かれている方法は1または0を返します。つまり、明確なカウントは0、1、または2のみを返します。

各テーブルに一意のID列があると仮定します。大文字と小文字を変更してID値を返し、それを区別してカウントすることができます。結合がpd_markingsテーブルから同じ行の複数を返す場合、IDの個別のカウントは、まあ、行の個別のカウントのみを返します。

于 2012-05-03T12:52:38.573 に答える