7

レポートの見栄えを良くするために、SQL クエリを改良しようとしています。私のクエリは、1 つのテーブルからデータを読み取り、いくつかの列でグループ化し、いくつかの集計フィールド (カウントと合計) を計算します。

SELECT A, B, C, COUNT(*), SUM(D) FROM T
GROUP BY A, B, C
ORDER BY A, B, C

ここで、B 列と C 列が定義済みの定数文字列であると仮定しましょう。たとえば、B は'B1'または'B2'、C は'C1'または'C2'です。したがって、結果セットの例は次のとおりです。

A  | B  | C  | COUNT(*) | SUM(D)
--------------------------------
A1 | B1 | C1 |       34 |   1752
A1 | B1 | C2 |        4 |    183
A1 | B2 | C1 |      199 |   8926
A1 | B2 | C2 |       56 |   2511
A2 | B1 | C2 |        6 |     89
A2 | B2 | C2 |       12 |    231
A3 | B1 | C1 |       89 |    552
...

ご覧のとおり、'A1'4 つの可能な (B、C) の組み合わせがすべてありますが、それは には当てはまりません'A2'。私の質問は、実際には、指定されたテーブルに存在しない (B, C) の組み合わせの集計行を生成するにはどうすればよいですか? つまり、たとえば次の行も印刷するにはどうすればよいですか。

A  | B  | C  | COUNT(*) | SUM(D)
--------------------------------
A2 | B1 | C1 |        0 |      0
A2 | B2 | C1 |        0 |      0

私が見ることができる唯一の解決策は、すべての (B, C) 値を持ついくつかの補助テーブルを作成し、その補助テーブルで RIGHT OUTER JOIN を作成することです。しかし、私はよりクリーンな方法を探しています...

皆さん、ありがとうございました。

4

3 に答える 3

2

補助テーブルは実際のテーブルである必要はありません。少なくとも、テーブル自体からすべての可能な値 (または関心のあるすべて) を取得できる場合は、共通のテーブル式にすることができます。@Bob Jarvis のクエリを使用して、次のようなことができるすべての可能な組み合わせを生成します。

WITH CTE AS (
    SELECT * FROM (SELECT DISTINCT a FROM T)
    JOIN (SELECT DISTINCT b, c FROM T) ON (1 = 1)
)
SELECT CTE.A, CTE.B, CTE.C,
    SUM(CASE WHEN T.A IS NULL THEN 0 ELSE 1 END), NVL(SUM(T.D),0)
FROM CTE
LEFT JOIN T ON T.A = CTE.A AND T.B = CTE.B AND T.C = CTE.C
GROUP BY CTE.A, CTE.B, CTE.C
ORDER BY CTE.A, CTE.B, CTE.C;

テーブルにない可能性のある固定値がある場合は、もう少し複雑になります (またはとにかく醜く、より多くの可能な値で悪化します):

WITH CTE AS (
    SELECT * FROM (SELECT DISTINCT a FROM T)
    JOIN (SELECT 'B1' AS B FROM DUAL
        UNION ALL SELECT 'B2' FROM DUAL) ON (1 = 1)
    JOIN (SELECT 'C1' AS C FROM DUAL
        UNION ALL SELECT 'C2' FROM DUAL) ON (1 = 1)
)
SELECT CTE.A, CTE.B, CTE.C,
    SUM(CASE WHEN T.A IS NULL THEN 0 ELSE 1 END), NVL(SUM(T.D),0)
FROM CTE
LEFT JOIN T ON T.A = CTE.A AND T.B = CTE.B AND T.C = CTE.C
GROUP BY CTE.A, CTE.B, CTE.C
ORDER BY CTE.A, CTE.B, CTE.C;

しかし、「欠けている」値を知っている何かに参加する必要があります。他の場所でも同じロジックが必要で、値が固定されている場合は、永続テーブルの方がクリーンである可能性があります。もちろん、どちらの方法でもメンテナンスが必要になる場合があります。パイプライン化された関数を代理テーブルとして機能させることも考えられますが、ボリュームに依存する可能性があります。

于 2012-05-10T14:48:05.780 に答える
1

問題は、データベースに特定の組み合わせがない場合、エンジンはどのようにしてその組み合わせを結果に含めることを知るのでしょうか? 結果にすべての組み合わせを含めるには、すべての組み合わせを使用できるようにする必要があります (メイン テーブルまたは参照用に使用されるその他のテーブル)。たとえば、次のようなデータを含む別のテーブル R を作成できます。

A  | B  | C  
------------
A1 | B1 | C1
A1 | B1 | C2
A1 | B2 | C1
A1 | B2 | C2
A2 | B1 | C1
A2 | B1 | C2
A2 | B2 | C1
A2 | B2 | C2
A3 | B1 | C1
A3 | B1 | C2
A3 | B1 | C1
A3 | B2 | C2
...

そして、クエリは次のようになります。

SELECT r.*, COUNT(t.d), coalesce(SUM(t.d), 0)
FROM r LEFT OUTER JOIN t on (r.a=t.a and r.b=t.b and r.c=t.c)
GROUP BY r.a, r.b, r.c
ORDER BY r.a, r.b, r.c

0 | 0これにより、メイン テーブルに存在しない組み合わせに対して必要なセットが返されます。これは、含める可能性のあるすべての組み合わせを知っている場合にのみ可能であることに注意してください。常にそうであるとは限りません。

一方、A、B、C が数値であり、範囲内のすべての数値を含めたい場合は、これを処理する別の方法、次のようなものがあるかもしれません。

SELECT a.n, b.n, c.n, COUNT(t.d), coalesce(SUM(t.d), 0)
FROM (SELECT (rownum) "n" FROM DUAL WHERE LEVEL >= start_a CONNECT BY LEVEL <= end_a) a,
     (SELECT (rownum) "n" FROM DUAL WHERE LEVEL >= start_b CONNECT BY LEVEL <= end_b) b,
     (SELECT (rownum) "n" FROM DUAL WHERE LEVEL >= start_c CONNECT BY LEVEL <= end_c) c,
     t
WHERE a.n = t.a(+) AND b.n = t.b(+) AND c.n = t.c(+)
GROUP BY a.n, b.n, c.n
ORDER BY a.n, b.n, c.n

(これをテストするのに便利な Oracle インスタンスを持っていないので、これは他のものではなく、ある程度知識に基づいた推測です。)

肝心なのは、エンジンは最終結果に何を含めるかを何らかの方法で知る必要があるということです。

于 2012-05-10T12:58:26.667 に答える
0

これを行うにはおそらくもっときれいな方法がありますが、次のようにして、目的に向けて開始する必要があります。

SELECT * FROM 
  (SELECT DISTINCT a FROM T)
JOIN
  (SELECT DISTINCT b, c FROM T)
  ON (1 = 1)
ORDER BY a, b, c

これにより、存在するすべての A とともに、B と C の存在するすべての組み合わせが得られます。

A1  B1  C1
A1  B1  C2
A1  B2  C1
A1  B2  C2
A2  B1  C1
A2  B1  C2
A2  B2  C1
A2  B2  C2

共有してお楽しみください。

于 2012-05-10T13:00:35.550 に答える