pak*_*oon 2 mysql postgresql dynamic-sql plpgsql
设想:
Table1 的字段名称命名为 testtable
ID、名称、大小、宽度、高度
Table2 的字段名称命名为 errortable
id,desc,field1,field2,operator
errortable 的值
+----+-------------------------------------+--------+--------+----------+
| 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 | <> |
+----+-------------------------------------+--------+--------+----------+
Run Code Online (Sandbox Code Playgroud)
现在我想检查testtable:
所需输出
+-------------------------------------+-------+
| errorname | count |
+-------------------------------------+-------+
| size should not greater than width | 6 |
| size should not greater than height | 2 |
| with should be equal to height | 3 |
+-------------------------------------+-------+
Run Code Online (Sandbox Code Playgroud)
有可能这样做吗?
当前查询:
select desc,
(select count(*) as "Total Errors"
from testtable
where errortable.field1 errortable.operator errortable.field2 )
from errortable group by id;
Run Code Online (Sandbox Code Playgroud)
是 SQL 中的保留字,请勿将其用作标识符。descdescr改为使用。
从同一个表中获取多个部分计数的最快和最优雅的方法是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;
Run Code Online (Sandbox Code Playgroud)
或者,对于 Postgres 9.3:
SELECT count(size > width OR NULL) AS ct1
, ...
FROM testtable;
Run Code Online (Sandbox Code Playgroud)
为了以您想要的形式获得结果,我VALUES在LATERAL连接中使用表达式进行反透视,id, descr在过程中添加 ID 和描述:
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);Run Code Online (Sandbox Code Playgroud)
粗体部分来自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;
Run Code Online (Sandbox Code Playgroud)
format()%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;
Run Code Online (Sandbox Code Playgroud)
称呼:
SELECT * FROM f_error_count();
Run Code Online (Sandbox Code Playgroud)
使用外部format(). 还要注意所有列如何进行表限定以避免冲突。
同样,由于涉及动态 SQL,请确保它不会被滥用于 SQL 注入。
自 9.4 以来,用于 Postgres 9.3 的SQL Fiddle不可用。