使用Select Statement重组键/值数据

时间:2013-11-01 21:04:05

标签: sql postgresql select

我有一些有效组织的表,如下例所示:

Basic Schema

从广义上讲,有许多“资产”拥有带有名称和值的“标签”形式的多对多键/值对列表。每个资产都可以包含任何标记子集,每个资产的每个标记都有一个值。

如果只是转储显示资产,标记名称和标记值的数据,您可能会得到如下结果:

Data, but not how I'd like it

但是,我实际上想要组织数据的方式是这样的:

Now that looks better

使用此架构是否有可能在此方法中选择数据?实际上,这涉及转换标签表中的每个条目并将其转换为列,然后填充在适当的每个列中,每个列中都包含与该标记相关联的值。

This question有完全相同的问题,但由于以下原因,我无法接受:

  1. 关于Oracle,我正在使用 PostgreSQL
  2. 所有给出的答案都依赖于引用特定数据名称的硬编码查询,而我要求动态地处理任何数据
  3. 它根本没有足够的视图。被查看了37次?我不相信任何真正的专家都能看到这一点。
  4. 此外,是否有此类数据选择的名称?我认为这是一个抽象的普遍问题,但我不知道它会被称为什么。

1 个答案:

答案 0 :(得分:1)

这种设计模式有时被称为实体属性值(EAV)。您可以动态创建所需的查询,但需要付出一些努力。我不确定是否有办法返回匿名表类型,因此我必须创建一个函数来创建函数:

Create or replace function generatecrosstab() returns void as $BODY$
Declare
    sql varchar := '';
    type varchar := '(assetid int, ';
    sep varchar := '';
    tagid int;
    tname varchar(50);
Begin
    for tagid, tname in select id, tagname from tag order by id
    loop
        sql := sql || sep || 'max(case tagid when ' 
            || tagid::varchar || ' then tagvalue end) ' || tname;
        type := type || sep || tname || ' varchar(255)';
        sep := ', ';
    end loop;
    type := type || ')';
    sql = 'create or replace function crosstab() returns table ' 
       || type || ' as $$' 
       || ' select assetid, ' || sql
       || ' from assettag group by assetid order by assetid;'
       || ' $$ Language sql;';
    execute sql;
End;
$BODY$ Language plpgsql;

如果您执行此功能,例如:

select generatecrosstab(); 

它将创建一个函数交叉表(),您可以从中选择:

Select * From crosstab();

tablefunc module有一些功能可以简化事情。

<强> SQLFiddle Example