【问题标题】:How to execute multiple joins with different params in a single query如何在单个查询中执行具有不同参数的多个连接
【发布时间】:2021-06-20 02:35:42
【问题描述】:

我有两个表,question(question_id) 和 question_exclusion(question_type, question_sub_type, question_id)

如果我指定 question_type 和 question_sub_type,我就能做到。

SELECT *
FROM question AS t1
         LEFT JOIN (SELECT t.question_id
                    FROM question_exclusion as t
                    WHERE t.question_type = 'A'
                      AND t.question_sub_type = 'A_1') AS t2
                   ON t1.question_id = t2.question_id
WHERE t2.question_id is null;

但我想要实现的是在单个查询中获取所有带有 questions_ids 的问题,以获取所有可能的 question_typequestions_sub_type

question_typequestions_sub_type 是动态参数,在查询执行之前我不知道确切的值

更新 1:

实际数据:

表:question

question_id|
42
10
2
36
49

表:question_exclusion

question_type|question_sub_type|question_id|
A            | A_1             | 42
A            | A_1             | 10
A            | A_2             | 10
B            | B_1             | 36 
C            | null            | 2

预期结果:

question_type|question_sub_type|question_id
A            | A_1             | 2
A            | A_1             | 36
A            | A_1             | 49
A            | A_2             | 42
A            | A_2             | 2
A            | A_2             | 36
A            | A_2             | 49
B            | B_1             | 42
B            | B_1             | 10
B            | B_1             | 2
B            | B_1             | 49
C            | null            | 42
C            | null            | 10
C            | null            | 36
C            | null            | 49

它就像每个类型和子类型组合的列表列表 考虑到排除表

例如:

type=A, sub_type=A_1 -> (select * from questions) - (select * from question_exclusion where type='A' and sub_type='A_1')
+
type=A, sub_type=A_2 -> (select * from questions) - (select * from question_exclusion where type='A' and sub_type='A_2')
+
type=B, sub_type=B_1 -> (select * from questions) - (select * from question_exclusion where type='B' and sub_type='B_2')

当然我可以查询所有不同的(类型,子类型)并通过结合联合进行另一个查询

SELECT *
FROM question AS t1
         LEFT JOIN (SELECT t.question_id
                    FROM question_exclusion as t
                    WHERE t.question_type = 'A'
                      AND t.question_sub_type = 'A_1') AS t2
                   ON t1.question_id = t2.question_id
WHERE t2.question_id is null
UNION
SELECT *
FROM question AS t1
         LEFT JOIN (SELECT t.question_id
                    FROM question_exclusion as t
                    WHERE t.question_type = 'B'
                      AND t.question_sub_type = 'B_1') AS t2
                   ON t1.question_id = t2.question_id
WHERE t2.question_id is null
...
...
N times for all type and sub_type

我正在寻找另一种在单个查询中执行此操作的可靠方法

【问题讨论】:

  • 请提供样本数据和期望的结果。您的数据模型也确实令人困惑。为什么user 有一个question_id 列?如果用户有多个问题怎么办?有questions 表吗?为什么“排除”表有类型和子类型?
  • 抱歉造成混淆,刚刚编辑并删除了用户前缀。这只是一个问题和问题排除表
  • 现在,什么这是id?回答此问题的用户是user_id 吗?表和列的适当描述名称可以帮助我们理解。 (如果是联结表,user_question可能是更具描述性的名称)
  • 我的错,删除了所有不必要的用户、ID等,只留下了需要的列
  • 您有针对特定类型/子类型的单一排除问题吗? question_exclusion 表中是否有效行:(A | A_1 | 42), (A | A_1 | 43)

标签: sql postgresql


【解决方案1】:
\i tmp.sql

create table question
        (question_id integer not null primary key)
        ;
INSERT INTO question(question_id) VALUES
( 42) , ( 10) , ( 2) , ( 36) , ( 49) ;

create table question_exclusion
        ( question_type text
        , question_sub_type text
        , question_id integer REFERENCES question( question_id)
        );

INSERT INTO question_exclusion(question_type, question_sub_type, question_id) VALUES
 ('A' , 'A_1' , 42 ) , ('A' , 'A_2' , 10 ) , ('B' , 'B_1' , 36  ) , ('C' , null  , 2 ) ;

WITH types AS (
        select distinct question_type, question_sub_type
        FROM question_exclusion
        )
SELECT t.question_type, t.question_sub_type, q.question_id
FROM question q
JOIN types t ON NOT EXISTS (
        SELECT * FROM question_exclusion x
        WHERE 1=1
        AND x.question_id = q.question_id
        AND x.question_type = t.question_type
        AND x.question_sub_type = t.question_sub_type
        )
ORDER BY t.question_type, t.question_sub_type
        ;

修改:


WITH types AS (
        select distinct question_type, question_sub_type
        FROM question_exclusion
        )
SELECT t.question_type, t.question_sub_type, q.question_id
FROM question q
CROSS JOIN types t
WHERE NOT EXISTS (
        SELECT * FROM question_exclusion x
        WHERE 1=1
        AND x.question_id = q.question_id
        AND x.question_type = t.question_type
        AND x.question_sub_type = t.question_sub_type
        )
ORDER BY t.question_type, t.question_sub_type
        ;

没有区别
WITH types AS (
        select distinct question_type, question_sub_type
        FROM question_exclusion
        )
SELECT t.question_type, t.question_sub_type, q.question_id
FROM question q
CROSS JOIN types t
WHERE NOT EXISTS (
        SELECT * FROM question_exclusion x
        WHERE 1=1
        AND x.question_id = q.question_id
        AND (x.question_type, x.question_sub_type) IS NOT DISTINCT FROM
            (t.question_type, t.question_sub_type)
        )
ORDER BY t.question_type, t.question_sub_type
        ;

【讨论】:

  • 谢谢,这几乎就是我想要的,请问可以稍微修改一下吗?它为 sub type 不为 null、type = C 和 sub_type = null 的所有情况给出了准确的结果,在这里我收到所有 5 个问题,但应排除 id=2 的问题
  • 是的,我修改了它。您可以稍微更改排除条件,可能使用IS NOT DISTINCT FROM
  • 因为null 值(至少在sub_type 列中)它应该类似于not (x.question_id, x.question_type, x.question_sub_type) is distinct from (q.question_id, t.question_type, t.question_sub_type)1=1 的目的是什么?
  • @wildplasser 1=1 有助于编辑/注释掉。 聪明 :) 感谢您的想法
  • 在从客户端语言(php/python 等)构建 WHERE 子句时也很方便。我通常从它开始,而实际的思考开始......
【解决方案2】:

我将行 (A | A_1 | 10) 添加到您的数据中的 question_exclusion 中以获得更干净的解决方案:

with question(question_id) as (
    select 42 union all
    select 10 union all
    select 2 union all
    select 36 union all
    select 49
),
question_exclusion(question_type, question_sub_type, question_id) as (
    select 'A', 'A_1', 42 union all
    select 'A', 'A_1', 10 union all
    select 'A', 'A_2', 10 union all
    select 'B', 'B_1', 36 union all 
    select 'C', null, 2
),
question_types(question_type, question_sub_type) as (
    select distinct question_type, question_sub_type from question_exclusion
)
select
    qt.question_type, qt.question_sub_type, q.question_id
from
    question q
    left join question_types qt on (1 = 1)
    left join question_exclusion qe on (q.question_id = qe.question_id and qt.question_type = qe.question_type and coalesce(qt.question_sub_type, '_') = coalesce(qe.question_sub_type, '_'))
where
    qe.question_id is null
order by 
    qt.question_type, qt.question_sub_type

【讨论】:

  • 感谢这也有效,我只是使用 select distinct question_type, question_sub_type from question_exclusion 作为嵌套选择而不是 question_types
【解决方案3】:
with
    -- questions
    q(id) as (select * from unnest('{42,10,2,36,49}'::int[])),
    -- excluded questions
    qe(tp, stp, id) as (select * from unnest(
        '{A,A,A,B,C}'::text[],
        '{A_1,A_1,A_2,B_1,NULL}'::text[],
        '{42,10,10,36,2}'::int[])),
    -- combinations of types and subtypes
    qt(tp, stp) as (select distinct tp, stp from qe)
select qt.tp, qt.stp, q.id from q cross join qt
except
select tp, stp, id from qe
order by 1, 2, 3;

┌────┬──────┬────┐
│ tp │ stp  │ id │
├────┼──────┼────┤
│ A  │ A_1  │  2 │
│ A  │ A_1  │ 36 │
│ A  │ A_1  │ 49 │
│ A  │ A_2  │  2 │
│ A  │ A_2  │ 36 │
│ A  │ A_2  │ 42 │
│ A  │ A_2  │ 49 │
│ B  │ B_1  │  2 │
│ B  │ B_1  │ 10 │
│ B  │ B_1  │ 42 │
│ B  │ B_1  │ 49 │
│ C  │ ░░░░ │ 10 │
│ C  │ ░░░░ │ 36 │
│ C  │ ░░░░ │ 42 │
│ C  │ ░░░░ │ 49 │
└────┴──────┴────┘

demo

【讨论】:

    【解决方案4】:

    这是一个完成您描述的示例查询:

    SELECT DISTINCT x.question_type, x.question_sub_type, q.question_id
    FROM question_exclusion x
    CROSS APPLY question q 
    WHERE NOT EXISTS (
        SELECT 1
        FROM question_exclusion
        WHERE question_type = x.question_type
            AND (question_sub_type = x.question_sub_type OR (question_sub_type IS NULL AND x.question_sub_type IS NULL))
            AND question_id = q.question_id)
    ORDER BY question_type, question_sub_type
    

    这使用CROSS APPLY 创建两个表的笛卡尔积,NOT EXISTS 子查询过滤掉排除的行,并使用 DISTINCT 从笛卡尔积中删除重复项(因为 @ 中有多个记录987654325@ 与 question_typequestion_sub_type 相同)。

    也可以使用LEFT JOIN 来代替子查询,如下所示:

    SELECT DISTINCT a.question_type, a.question_sub_type, a.question_id
    FROM (
        SELECT x.question_type, x.question_sub_type, q.question_id
        FROM question_exclusion x
            CROSS APPLY question q
        ) a
        LEFT JOIN question_exclusion r
            ON a.question_id = r.question_id
                AND a.question_type = r.question_type
                AND (a.question_sub_type = r.question_sub_type OR (a.question_sub_type IS NULL AND r.question_sub_type IS NULL))
    WHERE r.question_id IS NULL
    ORDER BY question_type, question_sub_type
    

    【讨论】:

    • 查询给了我 8 行 A、A_1 但预期的行是 3,即使我区分重复的行仍然没有为 A、A_1 应用排除,但其余的如 B、B_1 等。 。 看起来不错。可能是因为 B、C 类型只有单一排除?
    • 哎呀 :) 更新了。
    猜你喜欢
    • 1970-01-01
    • 2020-02-21
    • 2017-05-12
    • 2019-07-28
    • 2022-09-28
    • 2017-05-25
    • 2012-05-12
    • 2023-03-12
    • 1970-01-01
    相关资源
    最近更新 更多