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=100
和 id=200
我只需要一个 extra_account - 111-1 或 111-2,因为它们的 special_value 是相等的。
如果一个帐户有两个或更多 extra_accounts 并且它们的 special_value 不同,我必须接受所有这些。
所以对于 id=400
和 id=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
我有一个数据库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=100
和 id=200
我只需要一个 extra_account - 111-1 或 111-2,因为它们的 special_value 是相等的。
如果一个帐户有两个或更多 extra_accounts 并且它们的 special_value 不同,我必须接受所有这些。
所以对于 id=400
和 id=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 |