栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

使用 CASE 和 GROUP BY 旋转的动态替代方案

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

使用 CASE 和 GROUP BY 旋转的动态替代方案

如果您尚未安装附加模块tablefunc,请为每个数据库运行一次此命令:

CREATE EXTENSION tablefunc;

回答问题

适用于您的案例的非常基本的交叉表解决方案:

SELECt * FROM crosstab(  'SELECt bar, 1 AS cat, feh   FROM   tbl_org   ORDER  BY bar, feh') AS ct (bar text, val1 int, val2 int, val3 int);  -- more columns?

这里的特殊困难在于,基表中没有类别(

cat
)。对于基本的1 参数形式,我们可以只提供一个带有虚拟值作为类别的虚拟列。无论如何都会忽略该值。

这是一个罕见的情况下,其中第二个参数

crosstab()
的功能并不需要,因为所有的
NULL
值只出现在这一问题的界定,晃来晃去的列到右边。并且顺序可以由确定。

如果我们有一个实际的类别与名称确定结果值的顺序列,我们需要的2参数形式

crosstab()
。这里我借助窗函数合成了一个类别列
row_number()
,以
crosstab()

SELECt * FROM crosstab(   $$   SELECt bar, val, feh   FROM  (      SELECt *, 'val' || row_number() OVER (PARTITION BY bar ORDER BY feh) AS val      FROM tbl_org      ) x   ORDER BY 1, 2   $$ , $$VALUES ('val1'), ('val2'), ('val3')$$         -- more columns?) AS ct (bar text, val1 int, val2 int, val3 int);  -- more columns?

其余的几乎都是普通的。在这些密切相关的答案中找到更多解释和链接。

正确的测试设置

这就是您应该提供一个测试用例的方式:

CREATE TEMP TABLE tbl_org (id int, feh int, bar text);INSERT INTO tbl_org (id, feh, bar) VALUES   (1, 10, 'A') , (2, 20, 'A') , (3,  3, 'B') , (4,  4, 'B') , (5,  5, 'C') , (6,  6, 'D') , (7,  7, 'D') , (8,  8, 'D');

动态交叉表?

正如@Clodoaldo 评论的那样,还不是很有活力。使用 plpgsql 很难实现动态返回类型。但是有一些方法可以解决 -有一些限制

所以为了不进一步复杂化其余部分,我用一个*更简单的*测试用例来演示:

CREATE TEMP TABLE tbl (row_name text, attrib text, val int);INSERT INTO tbl (row_name, attrib, val) VALUES   ('A', 'val1', 10) , ('A', 'val2', 20) , ('B', 'val1', 3) , ('B', 'val2', 4) , ('C', 'val1', 5) , ('D', 'val3', 8) , ('D', 'val1', 6) , ('D', 'val2', 7);

称呼:

SELECt * FROM crosstab('SELECt row_name, attrib, val FROM tbl ORDER BY 1,2')AS ct (row_name text, val1 int, val2 int, val3 int);

返回:

 row_name | val1 | val2 | val3----------+------+------+------ A        | 10   | 20   | B        |  3   |  4   | C        |  5   |      | D        |  6   |  7   |  8

tablefunc
模块内置功能

tablefunc 模块为通用

crosstab()
调用提供了一个简单的基础结构,而无需提供列定义列表。编写的许多函数
C
(通常非常快):

crosstabN()

crosstab1()
-
crosstab4()
是预先定义的。一个小问题:他们需要并返回所有
text
. 所以我们需要投射我们的
integer
价值观。但它简化了调用:

SELECt * FROM crosstab4('SELECt row_name, attrib, val::text  -- cast!   FROM tbl ORDER BY 1,2')

结果:

 row_name | category_1 | category_2 | category_3 | category_4----------+------------+------------+------------+------------ A        | 10         | 20         | | B        | 3          | 4          | | C        | 5          | | | D        | 6          | 7          | 8          |

自定义
crosstab()
功能

对于更多列**或其他数据类型*,我们创建自己的复合类型函数*(一次)。
类型:

CREATE TYPE tablefunc_crosstab_int_5 AS (  row_name text, val1 int, val2 int, val3 int, val4 int, val5 int);

功能:

CREATE OR REPLACe FUNCTION crosstab_int_5(text)  RETURNS SETOF tablefunc_crosstab_int_5AS '$libdir/tablefunc', 'crosstab' LANGUAGE c STABLE STRICT;

称呼:

SELECT * FROM crosstab_int_5('SELECt row_name, attrib, val   -- no cast!        FROM tbl ORDER BY 1,2');

结果:

 row_name | val1 | val2 | val3 | val4 | val5----------+------+------+------+------+------ A        |   10 |   20 |      |      | B        |    3 |    4 |      |      | C        |    5 |      |      |      | D        |    6 |    7 |    8 |      |

一种适用于所有人的多态动态函数

这超出了

tablefunc
模块所涵盖的范围。
为了使返回类型动态化,我使用多态类型和此相关答案中详述的技术:

1-参数形式:

CREATE OR REPLACe FUNCTION crosstab_n(_qry text, _rowtype anyelement)  RETURNS SETOF anyelement AS$func$BEGIN   RETURN QUERY EXECUTE    (SELECT format('SELECT * FROM crosstab(%L) t(%s)'     , _qry     , string_agg(quote_ident(attname) || ' ' || atttypid::regtype     , ', ' ORDER BY attnum))    FROM   pg_attribute    WHERe  attrelid = pg_typeof(_rowtype)::text::regclass    AND    attnum > 0    AND    NOT attisdropped);END$func$  LANGUAGE plpgsql;

使用此变体重载 2 参数形式:

CREATE OR REPLACE FUNCTION crosstab_n(_qry text, _cat_qry text, _rowtype anyelement)  RETURNS SETOF anyelement AS$func$BEGIN   RETURN QUERY EXECUTE    (SELECT format('SELECT * FROM crosstab(%L, %L) t(%s)'     , _qry, _cat_qry     , string_agg(quote_ident(attname) || ' ' || atttypid::regtype     , ', ' ORDER BY attnum))    FROM   pg_attribute    WHERe  attrelid = pg_typeof(_rowtype)::text::regclass    AND    attnum > 0    AND    NOT attisdropped);END$func$  LANGUAGE plpgsql;

pg_typeof(_rowtype)::text::regclass
:为每个用户定义的复合类型定义了一个行类型,以便在系统目录中列出属性(列)
pg_attribute
。获得它的快速通道:将注册的类型 (
regtype
) 转换为
text
并将其转换
text
regclass

一次创建复合类型:

您需要定义要使用的每个返回类型:

CREATE TYPE tablefunc_crosstab_int_3 AS (    row_name text, val1 int, val2 int, val3 int);CREATE TYPE tablefunc_crosstab_int_4 AS (    row_name text, val1 int, val2 int, val3 int, val4 int);...

对于临时调用,您还可以创建一个临时表以达到相同(临时)效果:

CREATE TEMP TABLE temp_xtype7 AS (    row_name text, x1 int, x2 int, x3 int, x4 int, x5 int, x6 int, x7 int);

或者使用现有表、视图或物化视图的类型(如果可用)。

称呼

使用上述行类型:

1-参数形式(无缺失值):

SELECT * FROM crosstab_n(   'SELECt row_name, attrib, val FROM tbl ORDER BY 1,2' , NULL::tablefunc_crosstab_int_3);

2 参数形式(某些值可能会丢失):

SELECt * FROM crosstab_n(   'SELECt row_name, attrib, val FROM tbl ORDER BY 1' , $$VALUES ('val1'), ('val2'), ('val3')$$ , NULL::tablefunc_crosstab_int_3);

一个函数适用于所有返回类型,而模块提供的框架需要为每个返回一个单独的函数。 如果您已按照上面演示的顺序命名您的类型,则只需替换粗体数字。要在基表中查找最大类别数:

crosstab*N*()``tablefunc

SELECt max(count(*)) OVER () FROM tbl  -- returns 3GROUP  BY row_nameLIMIT  1;

如果您想要单独的列,这将是动态的。像@Clocoaldo 演示的数组或简单的文本表示或包装在文档类型中的结果,例如

json
hstore
可以动态地用于任意数量的类别。

免责声明:将
用户输入转换为代码时总是存在潜在危险。确保这不能用于 SQL 注入。不要接受来自不受信任用户的输入(直接)。

原始问题:

SELECt * FROM crosstab_n('SELECt bar, 1, feh FROM tbl_org ORDER BY 1,2' , NULL::tablefunc_crosstab_int_3);


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/514623.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号