我有一张桌子1
线 | a | b | c | d | e | f | g | h |
---|---|---|---|---|---|---|---|---|
1 | 18 | 2 | 2 | 22 | 0 | 2 | 1 | 2 |
2 | 20 | 2 | 2 | 2 | 0 | 0 | 0 | 2 |
3 | 10 | 2 | 2 | 222 | 0 | 2 | 1 | 2 |
4 | 12 | 2 | 2 | 3 | 0 | 0 | 0 | 0 |
5 | 15 | 2 | 2 | 3 | 0 | 0 | 0 | 0 |
还有一张桌子2
线 | 标准 |
---|---|
1 | a,b |
2 | b、c、f、h |
3 | a、b、e、g、h |
4 | c,e |
我正在使用此代码来查看/选择连接/连接列的独特结果,例如来自
concat(c,',',d)
的 concat(b,',',d,',',g)
、table1
等,并且工作正常:
SELECT DISTINCT(CONCAT(c,',',d))
FROM table1
但是,我不想像
concat(c,',',d)
那样手动编写,而是想参考table2.criteria
来从table1
获取要连接/连接的列引用,以便我可以根据每个连接条件看到整个唯一结果
尝试过这个,但出现错误:
SELECT DISTINCT(SELECT criteria FROM table2)
FROM table1
错误:用作表达式的子查询返回不止一行
SQL状态:21000
预期的独特结果是这样的;
| criteria | result |
| ------------ | ---------- |
| a,b | 15,2 |
| a,b | 10,2 |
| a,b | 20,2 |
| a,b | 12,2 |
| a,b | 18,2 |
| b,c,f,h | 2,2,2,2 |
| b,c,f,h | 2,2,0,2 |
| b,c,f,h | 2,2,0,0 |
| a,b,e,g,h | 20,2,0,0,2 |
| a,b,e,g,h | 12,2,0,0,0 |
| a,b,e,g,h | 15,2,0,0,0 |
| a,b,e,g,h | 10,2,0,1,2 |
| a,b,e,g,h | 18,2,0,1,2 |
| c,e | 2,0 |
SQL 不允许参数化标识符。有多种方法可以解决此限制。
问题不清楚,但根据评论,您希望为
table1
中的每一行连接给定的模式。
创建一个辅助函数(一次!),动态连接和执行语句。
基础知识:
CREATE OR REPLACE FUNCTION f_concat_cols(_cols text)
RETURNS TABLE (result text)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY EXECUTE format(
$q$SELECT concat_ws(',', %s) FROM table1 ORDER BY line$q$, _cols);
END
$func$;
这是一个集合返回函数(又名“表函数”),对于每个给定模式,为
table1
中的每一行返回一个结果行。
警告:将用户输入转换为这样的代码是SQL注入的绝佳机会。您必须确保
table1.criteria
只能保存有效的字符串!
要获取完整的结果矩阵(表 2 中每行都有不同的结果),查询现在很简单:
SELECT DISTINCT line AS t2_line, criteria, t1.*
FROM table2, f_concat_cols(criteria) t1
ORDER BY t2_line;
SELECT DISTINCT t2.line AS t2_line, t2.criteria, c.*
FROM table2 t2
CROSS JOIN (SELECT line, to_json(t) AS js FROM table1 t) t1
CROSS JOIN LATERAL (
SELECT string_agg(t1.js->>sub, ',') AS result
FROM string_to_table(t2.criteria, ',') sub
) c
ORDER BY t2_line;
将行从
t1
转换为JSON记录后,我们可以直接访问键(从列名转换)。string_to_table()
(Postgres 14+) 取消模式的嵌套,访问每个单个键,并将结果聚合到 LATERAL
子查询中。参见:
您可以将逻辑封装在1.中的函数中,但在本例中这是可选的。
SELECT DISTINCT t2.line AS t2_line, t2.criteria, c.*
FROM table2 t2
CROSS JOIN (SELECT line, ARRAY [a,b,c,d,e,f,g,h] AS arr FROM table1 t) t1
CROSS JOIN LATERAL (
SELECT string_agg(t1.arr[idx::int]::text, ',') AS result
FROM string_to_table(translate(t2.criteria, 'abcdefgh', '12345678'), ',') idx
) c
ORDER BY t2_line;
与 JSON 的“技巧”类似,我们可以通过将列转换为普通的 Postgres 数组来避免动态 SQL。然后将列名称投影到整数数组索引。我使用
translate()
来表示简单的情况,但这仅适用于单个字母!使用 replace()
或 regexp_replace()
或其他一些方法来获得更长的名称。小提琴 - 显示全部。