シナリオ:
testtableという名前のTable1のフィールド名
iD、名前、サイズ、幅、高さ
エラーテーブルと名付けられたTable2のフィールド名
id、desc、field1、field2、operator
エラーテーブルの値
+----+-------------------------------------+--------+--------+----------+
| id | desc | field1 | field2 | operator |
+----+-------------------------------------+--------+--------+----------+
| 1 | size should not greater than width | size | width | > |
| 2 | size should not greater than height | size | height | > |
| 3 | with should be equal to height | width | height | <> |
+----+-------------------------------------+--------+--------+----------+
testtable
から確認したい:
必要な出力
+-------------------------------------+-------+
| errorname | count |
+-------------------------------------+-------+
| size should not greater than width | 6 |
| size should not greater than height | 2 |
| with should be equal to height | 3 |
+-------------------------------------+-------+
このようにすることは可能ですか?
現在のクエリ:
select desc,
(select count(*) as "Total Errors"
from testtable
where errortable.field1 errortable.operator errortable.field2 )
from errortable group by id;
SQLの予約語です。識別子として使用しないでください。代わりにdesc
descr
を使用します。
同じテーブルから複数の部分カウントを取得する最も速くて最もエレガントな方法は、SELECT
句を使用して複数の集約関数を含む単一のFILTER
ステートメントです。
次のようなステートメント:
_SELECT count(*) FILTER (WHERE size > width) AS ct1
, count(*) FILTER (WHERE size > height) AS ct2
, count(*) FILTER (WHERE width <> height) AS ct3
FROM testtable;
_
または、Postgres 9.3の場合:
_SELECT count(size > width OR NULL) AS ct1
, ...
FROM testtable;
_
希望する形式で結果を取得するには、VALUES
結合でLATERAL
式を使用してカウンターピボットし、プロセスにIDと説明_id, descr
_を追加します。
_SELECT x.*
FROM (
SELECT count(*) FILTER (WHERE size > width) AS ct1
, count(*) FILTER (WHERE size > height) AS ct2
, count(*) FILTER (WHERE width <> height) AS ct3
FROM testtable
) t
, LATERAL (
VALUES (1, 'size should not greater than width' , ct1)
, (2, 'size should not greater than height', ct2)
, (3, 'with should be equal to height' , ct3)
) x(id, descr, ct);
_
太字ステートメントは、ステートメントを動的に作成するときにerrortable
から取得されます。
上記のステートメントをerrortable
で提供された値から動的に連結するには:
_CREATE OR REPLACE FUNCTION
SELECT 'SELECT x.* FROM (SELECT '
|| string_agg(
format('count(*) FILTER (WHERE %I %s %I) AS c%s'
, field1, operator, field2, id)
, ', ')
|| ' FROM testtable) t, LATERAL (VALUES ('
|| string_agg(format('%s, %L, c%s', id, descr, id), '), (')
|| ')) x(id, dscr, ct)'
FROM errortable;
_
format()
with _%I
_は、必要に応じて列名を引用し、ステートメントをSQLインジェクションに対して安全にします-operator
、そのまま連結されます。 OPERATOR()
構文 ...を使用して、それを安全にすることもできます。
信頼できないユーザーにerrortable
への書き込みアクセス権がない場合は、そのコンテンツを制御でき、心配する必要はありません。
戻り値の型は均一でよく知られているので、関数にカプセル化できます。
_CREATE OR REPLACE FUNCTION f_error_count()
RETURNS TABLE (id int, descr text, ct bigint) AS
$func$
BEGIN
RETURN QUERY EXECUTE (
SELECT format('SELECT x.* FROM (SELECT %s FROM testtable) t
, LATERAL (VALUES (%s)) x(id, dscr, ct)'
, string_agg(format('count(*) FILTER (WHERE %I %s %I) AS c%s'
, e.field1, e.operator, e.field2, e.id), ', ')
, string_agg(format('%s, %L, c%s', e.id, e.descr, e.id), '), ('))
FROM errortable e
);
END
$func$ LANGUAGE plpgsql;
_
コール:
_SELECT * FROM f_error_count();
_
外側のformat()
を使用して、連結をもう1ステップ簡略化します。また、競合を回避するために、すべての列がテーブル修飾されていることに注意してください。
ここでも、動的SQLが関係しているため、SQLインジェクションのために悪用されないようにしてください。
SQL Fiddle Postgres 9.3の場合、9.4は使用できません。