Postgres选择并合并行

时间:2019-04-10 00:05:59

标签: postgresql

我正在一张看起来像这样的桌子上

user_id | key         | scope     | value
--------+-------------+-----------+-------
1       | someSetting | user      | false
1       | someSetting | group     | true
1       | someSetting | company   | false
2       | someSetting | user      | false
2       | someSetting | group     | true
3       | someSetting | user      | false
4       | someSetting | group     | true

这些设置位于层次结构company -> group -> user中,其中用户覆盖了该组,而该组又覆盖了公司。通过user_id进行查询时,我想通过此层次结构有效地合并设置(如果存在)。对于以上示例,我想将其作为结果:

user_id | key         | value
--------+-------------+-------
1       | someSetting | false
2       | someSetting | true
3       | someSetting | false
4       | someSetting | true

我目前正在从Postgres检索行之后进行合并操作,但是如果可以在查询本身中完成,则效率会更高。我查看了聚合函数,但看起来其中任何一个都不符合我的要求。

这个似乎很简单,我相信可以使用Postgres完成。任何指针表示赞赏!

2 个答案:

答案 0 :(得分:1)

您可以将ROW_NUMBER()窗口函数与PARTITION BY和非常酷的ORDER BY结合使用。

想法:

  1. 使用相同的ROW_NUMBERuser_id自定义排序顺序为每条记录获取ORDER BY
  2. SELECT您希望从CTE a WHERE行号获得的所有内容都是1。

示例:

WITH a AS
(
    SELECT user_id
         , key
         , scope
         , ROW_NUMBER() OVER(PARTITION BY user_id 
                             ORDER BY array_position(array['user','group','company'], scope)) AS rno
    FROM test
)
SELECT user_id
     , key
     , scope
FROM a
WHERE rno = 1;

DBFiddle以显示其工作原理。


奖金: 如果您要创建函数来执行此操作,则甚至可以传入其他数组来设置自定义排序顺序。

答案 1 :(得分:0)

您要做的是将范围设置从单独的行更改为单独的列,因此您的记录集如下所示(请注意,我使用0表示false,1表示true):

+---------+-------------+--------------+---------------+-----------------+
| user_id |     key     | user_setting | group_setting | company_setting |
+---------+-------------+--------------+---------------+-----------------+
|       1 | someSetting | 0            | 1             | 0               |
|       2 | someSetting | 0            | 1             | NULL            |
|       3 | someSetting | 0            | NULL          | NULL            |
|       4 | someSetting | NULL         | 1             | NULL            |
+---------+-------------+--------------+---------------+-----------------+

为此,您有几种选择。这是其中之一,使用条件聚合。基本上,您将user_idkey分组,然后将聚合函数(可以是MINMAX)与CASE语句组合在一起:

WITH 
    settings_pivot AS
    (
        SELECT
            [user_id],
            [key],
            MIN(CASE WHEN [scope] = 'user' THEN [value] ELSE NULL END) AS user_setting,
            MIN(CASE WHEN [scope] = 'group' THEN [value] ELSE NULL END) AS group_setting,
            MIN(CASE WHEN [scope] = 'company' THEN [value] ELSE NULL END) AS company_setting
        FROM settings
        GROUP BY
            [user_id],
            [key]
    )
SELECT 
    [user_id], 
    [key], 
    COALESCE(user_setting, group_setting, company_setting) AS derived_setting
FROM settings_pivot

如果您只是通过settings_pivot CTE SELECT *,您将获得我一开始拥有的数据透视表。但是,使用COALESCE可以按照您指定的优先级。

注意:我正在使用SQL Server,因为我的计算机上的Postgres不想启动。因此,您必须用双引号代替方括号:"user_id",而不是[user_id]