Sql:将行转换为列

时间:2011-07-13 13:55:46

标签: mysql sql postgresql

考虑以下示例,其中我有一个包含人员记录的表和一个 PersonAttribute 表,其中包含链接到某人的可选属性:

表:人

ID    Name
1     Joe Bloggs
2     Jane Doe

表PersonAttribute

PersonId  Key         Value
1         Age         27            
2         HairColor   Brown

如何编写一个返回具有属性的所有人的查询,就像它们是列一样?我需要的结果集是:

ID    Name        Age    HairColor
1     Joe Bloggs  27     
2     Jane Doe           Brown

所以基本上我需要编写一个查询,它将所有个人记录与所有唯一的属性键转换为具有每个人记录值的列。

请注意, PersonAttribute 表中的主键是 PersonID ,因此我们不会为特定键和人员提供重复的条目

显然,我可以将年龄 HairColor 添加为表中的字段,而不是使用 PersonAttribute 表,但这只是一个例子来说明问题。实际上,我有大量的自定义属性,这些属性因不同的人物记录而异,因此以这种方式进行操作是不切实际的。

2 个答案:

答案 0 :(得分:3)

我不能谈论MySQL,但在PostgreSQL中你可以使用tablefunc模块中的交叉表函数:

CREATE OR REPLACE VIEW PersonAttributePivot AS
    SELECT PersonId AS ID, Age, HairColor
    FROM crosstab
    (
       'SELECT PersonId, Key, Value FROM PersonAttribute',
       'SELECT DISTINCT Key FROM PersonAttribute ORDER BY Key'
    )
    AS
    (
        PersonId integer,
        Age text,
        HairColor text
    );

加入查询:

SELECT id, name, age, haircolor
FROM Person JOIN PersonAttributePivot USING(id)
ORDER BY id;

通缉结果:

 id |    name    | age | haircolor 
----+------------+-----+-----------
  1 | Joe Bloggs | 27  | 
  2 | Jane Doe   |     | Brown
(2 rows)

如您所见,我在PersonAttributePivot视图中添加了明确的列列表。我不知道任何带有隐式列列表的“自动透视”创建方式。

修改

对于巨大的列列表(假设总是text类型)作为一种解决方法,我看到了这样一个很小的修改方法:

动态类型创建(这里简单地基于Java):

Class.forName("org.postgresql.Driver");
Connection c =
        DriverManager.getConnection("jdbc:postgresql://localhost/postgres", "postgres", "12345");
Statement s = c.createStatement();
ResultSet rs = s.executeQuery("SELECT DISTINCT Key FROM PersonAttribute ORDER BY Key");
List<String> columns = new ArrayList<String>();

while (rs.next())
    columns.add(rs.getString(1));

System.out.println("CREATE TYPE PersonAttributePivotType AS (");
System.out.println("\tPersonId integer,");
for (int i = 0; i < columns.size(); ++i)
{
    System.out.print("\t" + columns.get(i) + " text");
    if (i != columns.size() - 1)
        System.out.print(",");
    System.out.println();
}
System.out.println(");");

结果:

CREATE TYPE PersonAttributePivotType AS (
    PersonId integer,
    Age text,
    HairColor text
);

功能包装器:

CREATE OR REPLACE FUNCTION crosstabPersonAttribute(text, text)
    RETURNS setof PersonAttributePivotType
    AS '$libdir/tablefunc','crosstab_hash' LANGUAGE C STABLE STRICT;

自动创建视图:

CREATE OR REPLACE VIEW PersonAttributePivot AS
    SELECT * FROM crosstabPersonAttribute
    (
       'SELECT PersonId, Key, Value FROM PersonAttribute',
       'SELECT DISTINCT Key FROM PersonAttribute ORDER BY Key'
    );

结果:

TABLE PersonAttributePivot;
 personid | age | haircolor
----------+-----+-----------
        1 | 27  |
        2 |     | Brown
(2 rows)

答案 1 :(得分:1)

在基于列的数据集中转换基于行的数据集的过程称为pivoting。您可以通过此链接了解如何执行此操作:How to pivot a MySQL entity-attribute-value schema