2

テストフィールドを含むテーブルがあります。例

id         | test1    | test2    | test3    | test4    | test5
+----------+----------+----------+----------+----------+----------+
12345      | P        | P        | F        | I        | P

したがって、各レコードについて、合格、不合格、または不完全(P、F、またはI)の数を知りたいのです。

GROUP BY値を設定する方法はありますか?

擬似:

SELECT ('P' IN (fields)) AS pass
WHERE id = 12345

どういうわけかグループ化する必要のあるテストフィールドが約40ありますが、この非常に醜い長いクエリを作成したくありません。はい、テーブルを2つまたは3つの別々のテーブルに書き直す必要があることはわかっていますが、これは別の問題です。

推測される結果:

passed     | failed   | incomplete
+----------+----------+----------+
3          | 1        | 1

提案?

注:私はPostgreSQL 7.4を実行していますが、アップグレードしています

4

4 に答える 4

3

私は解決策を思いついたかもしれません:

SELECT id
      ,l - length(replace(t, 'P', '')) AS nr_p
      ,l - length(replace(t, 'F', '')) AS nr_f
      ,l - length(replace(t, 'I', '')) AS nr_i
FROM   (SELECT id, test::text AS t, length(test::text) AS l  FROM test) t

トリックは次のように機能します。

  • 行タイプをテキスト表現に変換します。
  • 文字の長さを測定します。
  • カウントしたい文字を置き換えて、長さの変化を測定します。
  • 繰り返し使用するために、副選択の元の行の長さを計算します。

これP, F, Iには、行の他のどこにも存在しないことが必要です。副選択を使用して、干渉する可能性のある他の列を除外します。

8.4〜9.1でテスト済み。現在、PostgreSQL 7.4を使用している人は誰もいません。自分でテストする必要がありますが、基本的な関数しか使用していませんが、7.4で行タイプをテキストにキャストできるかどうかはわかりません。それが機能しない場合は、すべてのテスト列を手動で1回連結する必要があります。

SELECT id
      ,length(t) - length(replace(t, 'P', '')) AS nr_p
      ,length(t) - length(replace(t, 'F', '')) AS nr_f
      ,length(t) - length(replace(t, 'I', '')) AS nr_i
FROM   (SELECT id, test1||test2||test3||test4 AS t FROM test) t

これには、すべての列がである必要がありますNOT NULL

于 2011-11-22T14:01:18.813 に答える
1

基本的に、テストによってデータのピボットを解除する必要があります。

id         | test     | result   
+----------+----------+----------+
12345      | test1    | P        
12345      | test2    | P        
12345      | test3    | F        
12345      | test4    | I        
12345      | test5    | P       

..。

-テスト結果ごとにグループ化できるようにします。

残念ながら、PostgreSQLにはピボット/ピボット解除機能が組み込まれていないため、これを行う最も簡単な方法は次のようになります。

select id, 'test1' test, test1 result from mytable union all
select id, 'test2' test, test2 result from mytable union all
select id, 'test3' test, test3 result from mytable union all
select id, 'test4' test, test4 result from mytable union all
select id, 'test5' test, test5 result from mytable union all

..。

これにアプローチする方法は他にもありますが、40列のデータを使用すると、これは非常に醜くなります。

編集:代替アプローチ-

select r.result, sum(char_length(replace(replace(test1||test2||test3||test4||test5,excl1,''),excl2,'')))
from   mytable m, 
       (select 'P' result, 'F' excl1, 'I' excl2 union all
        select 'F' result, 'P' excl1, 'I' excl2 union all
        select 'I' result, 'F' excl1, 'P' excl2) r
group by r.result
于 2011-11-22T13:09:19.867 に答える
0

補助的なオンザフライテーブルを使用して列を行に変換すると、次のような集計関数を適用できるようになります。

SELECT
  SUM(fields = 'P') AS passed,
  SUM(fields = 'F') AS failed,
  SUM(fields = 'I') AS incomplete
FROM (
  SELECT
    t.id,
    CASE x.idx
      WHEN 1 THEN t.test1
      WHEN 2 THEN t.test2
      WHEN 3 THEN t.test3
      WHEN 4 THEN t.test4
      WHEN 5 THEN t.test5
    END AS fields
  FROM atable t
    CROSS JOIN (
      SELECT 1 AS idx
      UNION ALL SELECT 2
      UNION ALL SELECT 3
      UNION ALL SELECT 4
      UNION ALL SELECT 5
    ) x
  WHERE t.id = 12345
) s
于 2011-11-22T14:43:19.347 に答える
0

編集:7.4についてのコメントを見たばかりですが、これはその古いバージョンでは機能しないと思います(unnest()はかなり後で登場しました)。誰かがこれを維持する価値がないと思うなら、私はそれを削除します。

ソリューションのベースとして「行表現」を使用するというErwinのアイデアをもう少し進めて、テーブルをオンザフライで自動的に「正規化」します。

select id,
       sum(case when flag = 'F' then 1 else null end) as failed,
       sum(case when flag = 'P' then 1 else null end) as passed,
       sum(case when flag = 'I' then 1 else null end) as incomplete
from (
  select id, 
         unnest(string_to_array(trim(trailing ')' from substr(all_column_values,strpos(all_column_values, ',') + 1)), ',')) flag
  from (
    SELECT id,
           not_normalized::text AS all_column_values
    FROM not_normalized
  ) t1
) t2
group by id

ソリューションの中心は、キャストを使用して完全な行から単一の値を作成するErwinのトリックnot_normalized::textです。文字列関数は、先頭のid値とその周囲の角かっこを削除するために適用されます。

その結果は配列に変換され、その配列はunnest()関数を使用して結果セットに変換されます。

その部分を理解するには、単に内部選択を段階的に実行します。

次に、結果がグループ化され、対応する値がカウントされます。

于 2011-11-22T15:36:33.473 に答える