如何使用表列中的值作为 SELECT 查询中的字段名称?

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

  1. 计算大小 > 宽度的所有记录。
  2. 计算大小 > 高度的所有记录。
  3. 计算宽度 <> 高度的所有记录。

所需输出

+-------------------------------------+-------+
|              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)

Erw*_*ter 5

假设和澄清

  • Postgres 9.4
  • 您想要来自同一个给定表的各种计数。

desc是 SQL 中的保留字,请勿将其用作标识符。descr改为使用。

1. 基本查询

从同一个表中获取多个部分计数的最快和最优雅的方法是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)

2. 高级查询

为了以您想要的形式获得结果,我VALUESLATERAL连接中使用表达式进行反透视,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动态构建语句时:

3.动态查询

从 中提供的值动态连接上述语句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,您可以控制其内容而不必担心。

4. 全自动化

由于返回类型是统一且众所周知的,我们可以将其封装在一个函数中:

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不可用。