Postgres选择并合并行

问题描述 投票:0回答:2

我正在研究一个看起来像这样的表:

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完成。任何指针赞赏!

postgresql
2个回答
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表明它的工作。


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


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]

© www.soinside.com 2019 - 2024. All rights reserved.