Return 列名称和不同的值

Return column name and distinct values

假设我在 postgres 中有一个简单的 table,如下所示:

+--------+--------+----------+
|  Car   |  Pet   |   Name   |
+--------+--------+----------+
| BMW    |  Dog   |   Sam    |
| Honda  |  Cat   |   Mary   |
| Toyota |  Dog   |   Sam    |
| ...    |  ...   |   ...    |

我想 运行 一个 sql 查询,该查询可以 return 第一列中的列名和第二列中的唯一值。例如:

+--------+--------+
|  Col   |  Vals  |
+--------+--------+
| Car    |  BMW   |
| Car    | Toyota |
| Car    | Honda  |
| Pet    |  Dog   |
| Pet    |  Cat   |
| Name   |  Sam   |
| Name   |  Mary  |
| ...    |  ...   |

I found a bit of code that can be used to return all of the unique values from multiple fields into one column:

-- Query 4b.  (104 ms, 128 ms)
select distinct unnest( array_agg(a)||
                        array_agg(b)||
                        array_agg(c)||
                        array_agg(d) )
from t ;

但是我对代码的理解不够深入,不知道如何将列名附加到另一列中。

I also found a query that can return the column names in a table. 也许这个子查询与上面显示的 "Query 4b" 相结合?

SQL Fiddle

SELECT distinct
       unnest(array['car', 'pet', 'name']) AS col,
       unnest(array[car, pet, name]) AS vals
FROM t
order by col

JSON functions row_to_json()json_each_text() 你可以不指定列的数量和名称:

select distinct key as col, value as vals
from (
    select row_to_json(t) r
    from a_table t
    ) t,
    json_each_text(r)
order by 1, 2;

SqlFiddle.

将返回集合的函数放在 SELECT 列表中是一种糟糕的风格,并且在 SQL 标准中是不允许的。 Postgres 出于历史原因支持它,但自从 LATERAL 引入 Postgres 9.3 以来,它基本上已过时。我们也可以在这里使用它:

SELECT x.col, x.val
FROM   tbl, LATERAL (VALUES ('car', car)
                          , ('pet', pet)
                          , ('name', name)) x(col, val)
GROUP  BY 1, 2
ORDER  BY 1, 2;

您会在 the very same question on dba.SE you already found yourself 下找到此 LATERAL (VALUES ...) 技术的讨论。不要看到第一个答案就停止阅读。

直到 Postgres 9.4 仍然存在一个例外:"parallel unnest" 需要在 SELECT 列表中组合多个集合返回函数。 Postgres 9.4 也带来了 new variant of unnest() 来消除这种必要性。更多:

如果 SELECT 列表中所有返回集合的函数的返回行数不完全相同,则新函数也不会偏离笛卡尔积,这是(曾经)非常奇怪的行为。新的语法变体应该优于现在已经过时的语法变体:

SELECT DISTINCT x.*
FROM   tbl t, unnest('{car, pet, name}'::text[]
                   , ARRAY[t.car, t.pet, t.name]) AS x(col, val)
ORDER  BY 1, 2;

也比两个 unnest() 并行调用更短更快。

Returns:

 col  |  val
------+--------
 car  | BMW
 car  | Honda
 car  | Toyota
 name | Mary
 name | Sam
 pet  | Cat
 pet  | Dog

DISTINCTGROUP BY,两者都适合任务。