Select 相等参数只有一行,如果参数不同则所有行

Select just one row for equal parameters and all rows if they differ

我有一个数据库table:

| id | account | extra_account | state |special_value

我需要selectextra_accounts,其中有一个账户列表。

SELECT * FROM table
WHERE table.account in (111, 222, 333) and table.state = 'WORKS';
|id  | account         | extra_account     | state         |special_value
—-------------------------------------------------------------------------------
|100 |111              |111-1              |WORKS          |1
|200 |111              |111-2              |WORKS          |1
|300 |222              |222-1              |WORKS          |2
|400 |333              |333-1              |WORKS          |3
|500 |333              |333-2              |WORKS          |4

我必须将 extra_accounts 连接成一个用逗号分隔的字符串。

如果一个帐户有两个或更多 extra_accounts,并且它们的 special_value 是相同的,我必须取一个 extra_accounts,无论哪个。 所以对于 id=100id=200 我只需要一个 extra_account - 111-1 或 111-2,因为它们的 special_value 是相等的。

如果一个帐户有两个或更多 extra_accounts 并且它们的 special_value 不同,我必须接受所有这些。 所以对于 id=400id=500,我需要 - 333-1 和 333-2,因为它们的 special_value 是 3 和 4。

最终结果应该是:

|string_agg
|text
—----
|111-1, 222-1, 333-1, 333-2

我知道我可以使用以下方法连接值:

SELECT  string_agg(table.extra_account, ', ')
FROM table WHERE table.account in (111, 222, 333) and table.state = 'WORKS';

但我没有找到方法 select 如果 special_value 不同,则所有行,如果 special_value 相等,则只有一行。

这就是您想要的每个帐户:

SELECT account
     , CASE WHEN count(DISTINCT special_value) = 1
            THEN min(extra_account)
            ELSE string_agg(extra_account, ', ')
            END AS special_values
FROM   tbl
WHERE  account IN (111, 222, 333)
AND    state = 'WORKS'
GROUP  BY 1;

将上面的内容包装在子查询中并聚合:

SELECT string_agg(special_values, ', ')
FROM (
   SELECT account
        , CASE WHEN count(DISTINCT special_value) = 1
               THEN min(extra_account)
               ELSE string_agg(extra_account, ', ')
               END AS special_values
   FROM   tbl
   WHERE  account IN (111, 222, 333)
   AND    state = 'WORKS'
   GROUP  BY 1
   ) sub;

准确地产生您想要的结果。

如果你真的想要一个 extra_account 每个不同的 special_value (不像表达的那样):

SELECT string_agg(extra_account, ', ')
FROM  (SELECT DISTINCT ON (account, special_value) extra_account FROM tbl) sub;

db<>fiddle here - 添加一行以显示差异

关于DISTINCT ON

  • Select first row in each GROUP BY group?

(以及为什么是 typically faster。)

如果你计算出一个ROW_NUMBER,那么你可以根据账户取1个&special_value。

SELECT 
 STRING_AGG(DISTINCT extra_account, ', ' ORDER BY extra_account) AS extra_accounts
FROM (
  SELECT account, extra_account, special_value
  , ROW_NUMBER() OVER (PARTITION BY account, special_value ORDER BY id) AS rownum
  FROM "table" t
  WHERE account IN (111, 222, 333) 
    AND state = 'WORKS' 
) q
WHERE rownum = 1;
extra_accounts
111-1, 222-1, 333-1, 333-2

演示 db<>fiddle here

我会通过列帐户上的聚合函数 min 和 special_value.

来执行此操作
With CTE As (
Select account, special_value, Min(extra_account) As v
From Tbl
Where account In (111, 222, 333) And state = 'WORKS'
Group by account, special_value
)
Select string_agg(v, ',' Order by v)
From CTE

数据输出:

string_agg
111-1,222-1,333-1,333-2