web-dev-qa-db-ja.com

GROUP BY + CASEステートメント

ハードウェアモデルと結果によってデータをグループ化する作業クエリがありますが、問題は多くの"results"があることです。これを"結果= 0の場合は0のままにし、それ以外の場合は1に設定"に減らすことを試みました。これは一般に機能しますが、最終的には次のようになります。

    day     |      name      | type | case | count
------------+----------------+------+------+-------
 2013-11-06 | modelA         |    1 |    0 |   972
 2013-11-06 | modelA         |    1 |    1 |    42
 2013-11-06 | modelA         |    1 |    1 |     2
 2013-11-06 | modelA         |    1 |    1 |    11
 2013-11-06 | modelB         |    1 |    0 |   456
 2013-11-06 | modelB         |    1 |    1 |    16
 2013-11-06 | modelB         |    1 |    1 |     8
 2013-11-06 | modelB         |    3 |    0 | 21518
 2013-11-06 | modelB         |    3 |    1 |     5
 2013-11-06 | modelB         |    3 |    1 |     7
 2013-11-06 | modelB         |    3 |    1 |   563

私が達成しようとしている集計の代わりに、タイプ/ケースのコンボごとに1行のみです。

    day     |      name      | type | case | count
------------+----------------+------+------+-------
 2013-11-06 | modelA         |    1 |    0 |   972
 2013-11-06 | modelA         |    1 |    1 |    55
 2013-11-06 | modelB         |    1 |    0 |   456
 2013-11-06 | modelB         |    1 |    1 |    24
 2013-11-06 | modelB         |    3 |    0 | 21518
 2013-11-06 | modelB         |    3 |    1 |   575

私のクエリは次のとおりです。

select CURRENT_DATE-1 AS day, model.name, attempt.type, 
       CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END, 
       count(*) 
from attempt attempt, prod_hw_id prod_hw_id, model model
where time >= '2013-11-06 00:00:00'  
AND time < '2013-11-07 00:00:00'
AND attempt.hard_id = prod_hw_id.hard_id
AND prod_hw_id.model_id = model.model_id
group by model.name, attempt.type, attempt.result
order by model.name, attempt.type, attempt.result;

これを達成するためのヒントは素晴らしいものです。

日は常にWHERE句で定義されるため、変動しません。 name, type, result(case)countは異なります。つまり、特定のモデルでは、"type + case" comboごとに1行だけが必要です。最初の結果セットでわかるように、modelAには3つの行があり、type=1およびcase=1があります(多くの"result"値があるため) = 0およびelse = 1)になりました。例のデータセット2のように集計された1行で表されるようにします。

21
ssbsts

クエリは既に機能しています-ただし、名前の競合が発生したり、出力列CASE式)をsource columnresult、内容が異なります。

...
GROUP BY model.name, attempt.type, attempt.result
...

ソース列の代わりにCASE式をGROUP BYする必要があります。

...
GROUP BY model.name, attempt.type
       , CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END
...

または、FROMリストの列名とは異なるcolumn aliasを指定します-または、その列が優先されます:

SELECT ...
     , CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END AS result1
...
GROUP BY model.name, attempt.type, result1
...

SQL標準は、この点でかなり独特です。 ここでマニュアルを引用:

出力列の名前は、ORDER BY句とGROUP BY句で列の値を参照するために使用できますが、WHERE句またはHAVING句では使用できません。そこで、代わりに式を書き出す必要があります。

そして:

ORDER BY式が出力列名と入力列名の両方に一致する単純な名前である場合、ORDER BYはそれを出力列名として解釈します。 これは、GROUP BYが同じ状況でにする選択の反対です。この不整合は、SQL標準との互換性を保つために行われます。

太字強調鉱山。

これらの競合は、GROUP BYおよびORDER BY位置参照(序数)を使用し、SELECTリストの項目を左から右に参照することで回避できます。以下の解決策を参照してください。
欠点は、これが読みにくく、SELECTリストの編集に対して脆弱である可能性があることです(それに応じて位置参照を調整することを忘れる可能性があります)。

ただし、定数値(GROUP BY)を保持している限り、CURRENT_DATE-1句に列dayを追加する必要はありませんnot

適切なJOIN構文と位置参照を使用して書き直し簡略化すると、次のようになります。

SELECT m.name
     , a.type
     , CASE WHEN a.result = 0 THEN 0 ELSE 1 END AS result
     , CURRENT_DATE - 1 AS day
     , count(*) AS ct
FROM   attempt    a
JOIN   prod_hw_id p USING (hard_id)
JOIN   model      m USING (model_id)
WHERE  ts >= '2013-11-06 00:00:00'  
AND    ts <  '2013-11-07 00:00:00'
GROUP  BY 1,2,3
ORDER  BY 1,2,3;

また、列名timeを避けていることに注意してください。これは 予約語 であり、識別子として使用しないでください。また、「時間」は明らかに timestampまたはdate であるため、かなり誤解を招く可能性があります。

58

これを試してみてください:caseステートメントを以下のものに置き換えてください

Sum(CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END) as Count,
6
Malathi

他の2つの非COUNT列をGROUP BYに追加してみてください。

select CURRENT_DATE-1 AS day, 
model.name, 
attempt.type, 
CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END, 
count(*) 
from attempt attempt, prod_hw_id prod_hw_id, model model
where time >= '2013-11-06 00:00:00'  
AND time < '2013-11-07 00:00:00'
AND attempt.hard_id = prod_hw_id.hard_id
AND prod_hw_id.model_id = model.model_id
group by 1,2,3,4
order by model.name, attempt.type, attempt.result;
1
Filipe Silva

TSQLの場合、caseステートメントを外部適用にカプセル化します。これにより、caseステートメントを2回記述する必要がなくなり、将来の結合でエイリアスによるcaseステートメントへの参照が可能になり、位置参照の必要がなくなります。

select oa.day, 
model.name, 
attempt.type, 
oa.result
COUNT(*) MyCount 
FROM attempt attempt, prod_hw_id prod_hw_id, model model
WHERE time >= '2013-11-06 00:00:00'  
AND time < '2013-11-07 00:00:00'
AND attempt.hard_id = prod_hw_id.hard_id
AND prod_hw_id.model_id = model.model_id
OUTER APPLY (
    SELECT CURRENT_DATE-1 AS day,
     CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END result
    ) oa    
group by oa.day, 
model.name, 
attempt.type, 
oa.result
order by model.name, attempt.type, oa.result;
0
Ryan O'Donnell