【问题标题】:Unexpected LEFT JOIN behavior with condition in ON clauseON 子句中带有条件的意外 LEFT JOIN 行为
【发布时间】:2022-08-18 21:42:07
【问题描述】:

我有以下三个表:

  • fees - 包含有关账单费用的信息
  • payments - 包含有关账单支付的信息(包括支付费用)
  • details - 有关于付款的详细信息

我很难理解为什么我的查询没有返回我期望的结果。

SQL 小提琴:sqlfiddle.com/#!9/942636/3

CREATE TABLE fees (
  receipt_number int(11) NOT NULL,
  bill_number    int(11) NOT NULL,
  version        int(11) NOT NULL,
  fee_id         int(11) NOT NULL,
  fee_type       varchar(30) NOT NULL
);

CREATE TABLE payments (
  receipt_number int(11) NOT NULL,
  bill_number    int(11) NOT NULL,
  version        int(11) NOT NULL,
  payment_id     int(11) NOT NULL,
  amount         decimal(13,2) NOT NULL DEFAULT \'0.00\'
);

CREATE TABLE details (
  receipt_number int(11) NOT NULL,
  payment_id     int(11) NOT NULL,
  fee_type       varchar(30) DEFAULT NULL
  amount         decimal(13,2) NOT NULL DEFAULT \'0.00\'
);

INSERT INTO fees (receipt_number, bill_number, version, fee_id, fee_type)
VALUES (111, 100, 1, 1, \'a\'),
       (111, 100, 1, 1, \'b\'),
       (111, 100, 1, 2, \'c\'),
       (111, 100, 1, 2, \'d\');

INSERT INTO payments (receipt_number, bill_number, version, payment_id, amount)
VALUES (111, 100, 1, 98, 30.00),
       (111, 100, 1, 99, 60.00);

INSERT INTO details (receipt_number, payment_id, fee_type, amount)
VALUES (111, 98, \'a\', 10.00),
       (111, 98, \'b\', 10.00),
       (111, 98, \'d\', 10.00),
       (111, 99, \'a\', 20.00),
       (111, 99, \'b\', 20.00),
       (111, 99, \'c\', 20.00);

我试图找出是否:

  • 帐单的费用类型为 \'c\'
  • 支付了该费用(由费用类型确定)

我的查询:

SELECT    fees.bill_number, details.receipt_number AS has_payment_type_c
FROM      fees
LEFT JOIN payments
          USING (bill_number, version)
LEFT JOIN details
          ON  details.receipt_number = payments.receipt_number
          AND details.payment_id     = payments.payment_id
          AND details.fee_type = \'c\'
WHERE     fees.fee_type = \'c\'
AND       details.receipt_number IS NULL;

我得到结果:

bill_number  has_payment_type_c
100          (null)

我应该不是请参阅此列表中的 bill_number,因为该账单有费用和支付该费用的费用。

我正在过滤 ON 子句中的 details.fee_type 以减少在该表中查找的记录数,并仅针对该特定费用类型加入该表。通过将 fee_type 连接条件移动到 WHERE 子句,查询似乎“工作”(0 个结果),但我认为这是不正确的。

我的问题:

  1. 我在查询中做错了什么?如何修复它以产生我正在寻找的结果,以及该修复如何工作?
  2. 假设我得到一个有效的查询,我是否可以简单地颠倒表顺序和 SELECT 以找到相反的信息 - 某种费用类型的付款而没有相同类型的相关费用记录?
  • 请问1个问题。请使用强制性minimal reproducible example 询问 1 个错误的查询/函数以及为什么您认为它应该在第一个子表达式中返回其他内容,因为它没有给出您期望通过参考权威文档证明的合理性,或者询问您的总体目标提供您可以做的工作部件,最好是minimal reproducible example。但是请先询问前者,因为对前者的误解会妨碍对后者的理解。糟糕的代码不会告诉我们你希望它做什么。
  • 很高兴你有点minimal reproducible example。但这应该是您可以提供的最少代码,即您显示的代码可以通过您显示的代码扩展为不正常的代码。当你得到一个你不期望的结果时,暂停你的总体目标,切到第一个具有意外结果的子表达式并说出你的期望和原因,并通过文档证明是合理的。 (调试基础。)(否则,您希望我们在您没有完成代码的情况下进行检查,并且您希望我们编写该语言的另一个演示文稿,而不会详细说明您已经误解或做或不做的事情\'不懂。)
  • 1. 有什么限制? minimal reproducible example 2. NULL 可以来自任一左连接。未付款的费用将在第一次加入后为空付款,第二次后为空详细信息。 3. 在 AND 和 ON 中都有 details.fee_type = \'c\' 是一个危险信号。 LEFT JOIN 返回由 NULL 扩展的 INNER JOIN 行 UNION ALL 不匹配的左表行。作为 OUTER JOIN 的一部分,始终知道您想要什么 INNER JOIN。
  • 将选择更改为选择 *,这样您就可以看到返回的所有内容并从那里开始。
  • 这是一个相当不明显的设计。账单有各种版本,每个版本都收取一些费用或一项费用?付款,每笔费用,或其中的一些账单版本的费用?目前尚不清楚系统的状态是什么。您没有为每个基表和您的查询或业务规则或约束提供明确的行成员资格条件(含义)。您对查询如何成为基表或业务情况的功能的描述不清楚且零散。所以我们不知道什么是正确的查询,也不知道什么约束允许进一步的查询表达式是正确的。

标签: mysql left-join filtering where-clause


【解决方案1】:

答案是使用派生表,内联payments和details,然后左联派生表来收费:

SELECT    DISTINCT fees.bill_number, fees.version, x.bill_number AS has_payment_type_c
FROM      fees
LEFT JOIN (
             SELECT payments.bill_number, payments.version, details.fee_type
             FROM   payments
             JOIN   details
                    USING(receipt_number, payment_id)
             WHERE  details.fee_type = 'c'
) x
           ON  fees.bill_number = x.bill_number
           AND fees.version     = x.version
WHERE    fees.fee_type = 'c'
AND      x.bill_number IS NULL;

这将返回预期的 0 结果,并且可以通过删除检查 null x.bill_number 的 where 条件来验证:

bill_number  version  has_payment_type_c
100          1        100

同样的方法也适用于免费查找付款。使用派生表将付款和详细信息进行内部连接,然后将连接留给费用。选择 fee.bill_number AS has_payment_type_c 并将 WHERE fee.bill_number IS NULL 添加到 WHERE 子句。

【讨论】:

    猜你喜欢
    • 2012-02-04
    • 2021-09-13
    • 2018-12-28
    • 2013-03-20
    • 2014-03-14
    • 2016-01-11
    • 1970-01-01
    • 2011-01-30
    • 1970-01-01
    相关资源
    最近更新 更多