PostgreSQL交叉表查询
 有没有人知道如何在PostgreSQL中创build交叉表查询? 
 例如,我有下面的表格: 
Section Status Count A Active 1 A Inactive 2 B Active 4 B Inactive 5 我想查询返回以下交叉表:
 Section Active Inactive A 1 2 B 4 5 
这可能吗?
 每个数据库安装一个额外的模块tablefunc ,提供函数crosstab() 。 自从Postgres 9.1以后,你可以使用CREATE EXTENSION : 
 CREATE EXTENSION tablefunc; 
改进的testing用例
 CREATE TEMP TABLE t ( section text , status text , ct integer -- don't use "count" as column name. ); INSERT INTO t VALUES ('A', 'Active', 1), ('A', 'Inactive', 2) , ('B', 'Active', 4), ('B', 'Inactive', 5) , ('C', 'Inactive', 7); -- 'C' with 'Active' is missing 
-   count是标准SQL中的保留字 。 Postgres允许它,但我宁愿避免这些作为标识符。
简单的forms – 不适合缺less的属性
  crosstab(text)有1个input参数: 
 SELECT * FROM crosstab( 'SELECT section, status, ct FROM t ORDER BY 1,2' -- needs to be "ORDER BY 1,2" here ) AS ct ("Section" text, "Active" int, "Inactive" int); 
返回:
Section | 活动| 待用 --------- + -------- + ---------- A | 1 | 2 B | 4 | 五 C | 7 | - !
- 不需要铸造和重命名。
-  注意C的错误结果:第一列填入数值7。 有时候,这种行为是可取的,但不是这个用例。
- 简单的forms也仅限于提供的input查询中的三列: row_name , category , value 。 在下面的2参数替代中没有额外的列的空间。
安全的forms
  crosstab(text, text)有2个input参数: 
 SELECT * FROM crosstab( 'SELECT section, status, ct FROM t ORDER BY 1,2' -- could also just be "ORDER BY 1" here ,$$VALUES ('Active'::text), ('Inactive')$$) AS ct ("Section" text, "Active" int, "Inactive" int); 
返回:
Section | 活动| 待用 --------- + -------- + ---------- A | 1 | 2 B | 4 | 五 C | | 7 - !
- 
注意 C的正确结果。
- 
第二个参数可以是任何查询,每个属性返回一行 ,匹配最后列定义的顺序。 通常你会想要像这样从基础表中查询不同的属性: 'SELECT DISTINCT attribute FROM tbl ORDER BY 1'这在手册中。 由于无论如何您必须列出列定义列表中的所有列(除了预定义的 crosstab N ()变体),在VALUESexpression式中提供一个简短的列表通常会更有效,如下所示:$$VALUES ('Active'::text), ('Inactive')$$)或(不在手册中): $$SELECT unnest('{Active,Inactive}'::text[])$$ -- shorter for long lists
- 
我用美元报价使报价更容易。 
- 
您甚至可以使用 crosstab(text, text)输出具有不同数据types的列 – 只要值列的文本表示forms是目标types的有效input。 这样你可能有不同types的属性,并输出text,date,numeric等各自的属性。 在手册中的章节crosstab(text, text)末尾有一个代码示例。
先进的例子
- 
透视使用Tablefunc多列 – 也演示提到“额外的列” 
- 
使用CASE和GROUP BY进行dynamic替代 
  \crosstabview在psql中的\crosstabview 
  Postgres 9.6将这个元命令添加到了默认的交互式terminalpsql中 。 您可以运行您将用作第一个crosstab()参数的查询,并将其提供给\crosstabview (立即或在下一步中)。 喜欢: 
 db=> SELECT section, status, ct FROM t \crosstabview 
 与上面类似的结果,但是它仅仅是客户端上的表示特征 。 input行的处理方式稍有不同,因此不需要ORDER BY 。  手册中\crosstabview详细信息。 该页面底部有更多的代码示例。 
关于dba.SE的相关答案DanielVérité(psql特性的作者):
- 如何在生成的表定义未知的情况下生成一个pivoted的CROSS JOIN?
以前接受的答案已经过时。
- 
函数 crosstab(text, integer)的变体已过时。 第二个integer参数被忽略。 我引用当前手册 :crosstab(text sql, int N)…已过时的 crosstab(text)版本crosstab(text)。 参数N现在被忽略,因为值列的数量总是由调用查询决定的
- 
不必要的铸造和重命名。 
- 
如果一行没有全部属性,则失败。 请参阅上面两个input参数的安全变体,以正确处理缺失的属性。 
- 
ORDER BY在crosstab()的单参数forms中是必需的。 手册:在实践中,SQL查询应始终指定 ORDER BY 1,2以确保input行已正确sorting
 您可以使用附加模块tablefunc的crosstab()函数 – 您必须为每个数据库安装一次 。 从PostgreSQL 9.1开始,你可以使用CREATE EXTENSION来实现: 
 CREATE EXTENSION tablefunc; 
在你的情况下,我相信它会看起来像这样:
 CREATE TABLE t (Section CHAR(1), Status VARCHAR(10), Count integer); INSERT INTO t VALUES ('A', 'Active', 1); INSERT INTO t VALUES ('A', 'Inactive', 2); INSERT INTO t VALUES ('B', 'Active', 4); INSERT INTO t VALUES ('B', 'Inactive', 5); SELECT row_name AS Section, category_1::integer AS Active, category_2::integer AS Inactive FROM crosstab('select section::text, status, count::text from t',2) AS ct (row_name text, category_1 text, category_2 text); 
 SELECT section, SUM(CASE status WHEN 'Active' THEN count ELSE 0 END) AS active, SUM(CASE status WHEN 'Inactive' THEN count ELSE 0 END) AS inactive FROM t GROUP BY section 
JSON聚合解决scheme:
 CREATE TEMP TABLE t ( section text , status text , ct integer -- don't use "count" as column name. ); INSERT INTO t VALUES ('A', 'Active', 1), ('A', 'Inactive', 2) , ('B', 'Active', 4), ('B', 'Inactive', 5) , ('C', 'Inactive', 7); SELECT section, (obj ->> 'Active')::int AS active, (obj ->> 'Inactive')::int AS inactive FROM (SELECT section, json_object_agg(status,ct) AS obj FROM t GROUP BY section )X 
对不起,这是不完整的,因为我不能在这里testing,但它可能会让你在正确的方向。 我正在翻译从我使用的东西,使类似的查询:
 select mt.section, mt1.count as Active, mt2.count as Inactive from mytable mt left join (select section, count from mytable where status='Active')mt1 on mt.section = mt1.section left join (select section, count from mytable where status='Inactive')mt2 on mt.section = mt2.section group by mt.section, mt1.count, mt2.count order by mt.section asc; 
我正在从事的代码是:
 select m.typeID, m1.highBid, m2.lowAsk, m1.highBid - m2.lowAsk as diff, 100*(m1.highBid - m2.lowAsk)/m2.lowAsk as diffPercent from mktTrades m left join (select typeID,MAX(price) as highBid from mktTrades where bid=1 group by typeID)m1 on m.typeID = m1.typeID left join (select typeID,MIN(price) as lowAsk from mktTrades where bid=0 group by typeID)m2 on m1.typeID = m2.typeID group by m.typeID, m1.highBid, m2.lowAsk order by diffPercent desc; 
这将返回一个typeID,最高的价格出价和最低的价格要求和两者之间的差异(积极的差异将意味着可以买的东西less于它可以出售)。