【问题标题】:PostgreSQL 9.3: Pivot table queryPostgreSQL 9.3:数据透视表查询
【发布时间】:2015-03-16 07:18:40
【问题描述】:

我想显示下表的数据透视表(交叉表)。

表:Employee

CREATE TABLE Employee
(
Employee_Number varchar(10),
Employee_Role varchar(50),
Group_Name varchar(10)
);

插入:

INSERT INTO Employee VALUES('EMP101','C# Developer','Group_1'),
                           ('EMP102','ASP Developer','Group_1'),
                           ('EMP103','SQL Developer','Group_2'),
                           ('EMP104','PLSQL Developer','Group_2'),
                           ('EMP101','Java Developer',''),
                           ('EMP102','Web Developer','');

现在我想显示上述数据的数据透视表,如下所示:

预期结果:

Employee_Number     TotalRoles      TotalGroups       Available     Others     Group_1     Group_2
---------------------------------------------------------------------------------------------------
   EMP101               2                2                1           1           1           0
   EMP102               2                2                1           1           1           0
   EMP103               1                2                1           0           0           1 
   EMP104               1                2                1           0           0           1

说明:我想显示每个员工拥有的Employee_Number,TotalRoles, TotalGroups 显示给所有员工,Available 显示可用的员工 在多少组中,Others 必须显示该员工在其他组中也可用 group_name 尚未分配,最后Group_Names 必须以数据透视格式显示。

【问题讨论】:

  • 问题/问题是什么?
  • 问题在解释上。但我真的不明白最后 4 列计算
  • @Houari 我只看到预期的结果,而不是问题或问题。

标签: sql postgresql pivot-table postgresql-9.3 table-functions


【解决方案1】:
SELECT * FROM crosstab(
      $$SELECT grp.*, e.group_name
             , CASE WHEN e.employee_number IS NULL THEN 0 ELSE 1 END AS val
        FROM  (
           SELECT employee_number
                , count(employee_role)::int            AS total_roles
                , (SELECT count(DISTINCT group_name)::int
                   FROM   employee
                   WHERE  group_name <> '')            AS total_groups
                , count(group_name <> '' OR NULL)::int AS available
                , count(group_name =  '' OR NULL)::int AS others
           FROM   employee
           GROUP  BY 1
           ) grp
        LEFT   JOIN employee e ON e.employee_number = grp.employee_number
                              AND e.group_name <> ''
        ORDER  BY grp.employee_number, e.group_name$$
     ,$$VALUES ('Group_1'::text), ('Group_2')$$
   ) AS ct (employee_number text
          , total_roles  int
          , total_groups int
          , available    int
          , others       int
          , "Group_1"    int
          , "Group_2"    int);

SQL Fiddle 演示基本查询,但不是交叉表步骤,该步骤未安装在 sqlfiddle.com 上

交叉表的基础知识:

此交叉表的特别之处:所有“额外”列。这些列位于中间,在“行名”之后但在“类别”和“值”之前:

再一次,如果您有一组动态的组,您需要动态构建此语句并在第二次调用中执行它:

【讨论】:

  • 如果我只想显示选定数量的 group_name 来显示,那么我该如何计算 available 呢?我试过这个:(SELECT count(available) from ( SELECT CASE WHEN group_name IN ('Group_1') THEN 1 END AS available from employee group by group_name ) a ) as available 代替count(group_name &lt;&gt; '' OR NULL)::int AS available 但没有得到正确的结果。
  • @MAK:这是一个新问题。如果您对此感到困惑,请开始一个新问题。与往常一样,您可以链接到这个以获取上下文。
  • 看看this。
  • 如果我多次插入 INSERT INTO Employee values('EMP101','C# Developer','Group_1'); 相同的语句。在此之后,如果我执行您的脚本,那么 Available 会发生变化。请看看这个。谢谢:)
【解决方案2】:

您可以为此使用crosstab 函数。

如果您还没有添加 tablefunc 扩展,首先需要添加:

CREATE EXTENSION tablefunc;

交叉表函数要求您向其传递一个查询,该查询返回您需要透视的数据,然后是输出中的列列表。 (以其他方式“告诉我你想要的输入和输出格式”)。排序顺序很重要!

在您的情况下,输入查询非常复杂 - 我认为您需要执行大量单独的查询,然后将它们 UNION ALL 以获得所需的数据。我不完全确定您如何计算“TotalGroups”和“Available”值,但您可以在相关位置修改以下内容以获得您需要的内容。

 SELECT * FROM crosstab(

'SELECT employee_number, attribute, value::integer AS value FROM (with allemployees AS (SELECT distinct employee_number FROM employee) -- use a CTE to get distinct employees

SELECT employee_number,''attr_0'' AS attribute,COUNT(distinct employee_role) AS value FROM employee GROUP BY employee_number -- Roles by employee
UNION ALL
SELECT employee_number,''attr_1'' AS attribute,value from allemployees, (select count (distinct group_name) as value from employee where group_name <> '''') a
UNION ALL
SELECT employee_number,''attr_2'' AS attribute, COUNT(distinct group_name) AS value FROM employee where group_name <> '''' GROUP BY employee_number -- Available, do not know how this is calculate
UNION ALL

SELECT a.employee_number, ''attr_3'' AS attribute,coalesce(value,0) AS value FROM allemployees a LEFT JOIN -- other groups. Use a LEFT JOIN to avoid nulls in the output
(SELECT employee_number,COUNT(*) AS value FROM employee WHERE group_name ='''' GROUP BY employee_number) b on a.employee_number = b.employee_number
UNION ALL
SELECT a.employee_number, ''attr_4'' AS attribute,coalesce(value,0) AS value FROM allemployees a LEFT JOIN -- group 1
(SELECT employee_number,COUNT(*) AS value FROM employee WHERE group_name =''Group_1'' GROUP BY employee_number) b on a.employee_number = b.employee_number
UNION ALL
SELECT a.employee_number, ''attr_5'' AS attribute,coalesce(value,0) AS value FROM allemployees a LEFT JOIN -- group 2
(SELECT employee_number,COUNT(*) AS value FROM employee WHERE group_name =''Group_2'' GROUP BY employee_number) b on a.employee_number = b.employee_number) a order by 1,2')

AS ct(employee_number varchar,"TotalRoles" integer,"TotalGroups" integer,"Available" integer, "Others" integer,"Group_1" integer, "Group_2" integer)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-21
    • 2015-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-11
    • 2019-07-22
    相关资源
    最近更新 更多