MySQL问题左连接

时间:2011-03-04 01:32:13

标签: mysql left-join

MySQL(5.1)中有3个表: account,bank_account_data和investment_account_data。

表定义如下: (注意:这是一个简化版本,实际表格中有更多列,银行和投资数据具有不同的信息,这些信息对于此特定情况不需要,但需要在其他地方使用)

CREATE TABLE account
(
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  type ENUM ('INVESTMENT', 'BANK') NOT NULL,
  PRIMARY KEY (id)
) ENGINE=InnoDB;

CREATE TABLE investment_account_data
(
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  account_id BIGINT UNSIGNED NOT NULL,
  as_of_date TIMESTAMP NULL DEFAULT NULL,
  total_balance NUMERIC(12, 4) NOT NULL,
  PRIMARY KEY (id),
  FOREIGN KEY (account_id) REFERENCES account (id),
  CONSTRAINT unique_per_account_and_date UNIQUE (account_id, as_of_date)
) ENGINE=InnoDB;

CREATE TABLE bank_account_data
(
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  account_id BIGINT UNSIGNED NOT NULL,
  as_of_date TIMESTAMP  NULL DEFAULT NULL,
  current_balance NUMERIC(12, 4) NOT NULL,
  PRIMARY KEY (id),
  FOREIGN KEY (account_id) REFERENCES account (id),
  CONSTRAINT unique_per_account_and_date UNIQUE (account_id, as_of_date)
) ENGINE=InnoDB;

目前我在帐户表中只有一个帐户,其中id = 1且type ='INVESTMENT' 我在investment_account_data表中有一条记录(id = 1,account_id = 1,total_balace = 15000,as_of_data ='2011-03-02 00:00:00')

bank_account_data没有行。

运行以下查询(令人惊讶)不会返回任何行:

SELECT A.id as account_id,
       COALESCE(BD.as_of_date, ID.as_of_date) as as_of_date,
       COALESCE(BD.current_balance, ID.total_balance) as balance
  FROM account A
       LEFT JOIN bank_account_data BD ON (BD.account_id = A.id and A.type='BANK') 
       LEFT JOIN investment_account_data ID ON (ID.account_id = A.id and A.type='INVESTMENT')
 WHERE A.id=1

但是这一行返回一行(如预期的那样):

SELECT A.id as account_id,
       COALESCE(BD.as_of_date, ID.as_of_date) as as_of_date,
       COALESCE(BD.current_balance, ID.total_balance) as balance
 FROM account A
      LEFT JOIN investment_account_data ID ON (ID.account_id = A.id and A.type='INVESTMENT')
      LEFT JOIN bank_account_data BD ON (BD.account_id = A.id and A.type='BANK') 
 WHERE A.id=1

为什么我看到这些结果的任何想法?

此外,如果我从连接中删除A.type条件,它也将返回一行。

3 个答案:

答案 0 :(得分:1)

我猜想看到行为的原因是你在第一个连接子句的类型字段上创建了一个过滤器,它掩盖了第二个连接子句中的所有帐户。

  1. 选择所有与“BANK”类型匹配的帐户
  2. 加入相应的银行帐户信息
  3. 从最后一个结果集中选择所有与“投资”匹配的帐户,即所有与“BANK”类型匹配的帐户
  4. 使用相应的投资帐户信息加入空集。
  5. 修复

    这样做的肮脏和坏方法是创建一组不同的连接条件

    SELECT A.id as account_id,
           COALESCE(BD.as_of_date, ID.as_of_date) as as_of_date,
           COALESCE(BD.current_balance, ID.total_balance) as balance
     FROM account A
          LEFT JOIN investment_account_data ID ON (ID.account_id = A.id)
          LEFT JOIN bank_account_data BD ON (BD.account_id = A.id) 
     WHERE 
          A.id=1
          AND ((ID.account_id is not null and A.TYPE='INVESTMENT') 
              OR
               (BD.account_id is not null and A.TYPE = 'BANK'))
    

    必须使用OR之类的东西让我觉得数据库模式有点臭。你可以用一个更适合的UNION做同样的事情。

    在我看来,您的意图是该帐户可以是银行帐户或投资帐户。数据库模式目前并未强制执行此问题中详细说明需要此类查询的情况。帐户表的目的似乎是为所有帐户ID提供空间。它可以用序列替换。我也不确定您是否需要根据当前定义为银行账户和投资账户设置不同的表格。您可以对两个表执行相同的操作,一个包含帐户信息,另一个包含每个日期的帐户余额。

答案 1 :(得分:1)

根据您发布的查询和数据,这正是您应该期望的输出。

在第一个查询中,第一个连接条件指定A.type = 'BANK'。因此,在第一个LEFT JOIN之后,您有一个零行的结果集,然后再次LEFT JOIN。如果“左”侧为零行,则LEFT JOIN(或INNER JOIN)也将始终返回零行。

对于第二个查询,订单相反。因此,在第一次连接之后,您将拥有单行结果集。您现在在“左”侧有一行用于第二次加入。

答案 2 :(得分:0)

问题在于左连接的处理方式和A.type条件。 UNION将是您尝试做的更合适的操作员。

SELECT account_id, as_of_date, current_balance as balance
       FROM bank_account_data
UNION
SELECT account_id, as_of_date, total_balance as balanceas
       FROM investment_account_data

(如果您需要从帐户表中获取其他信息,请插入联接)

另外,您可能想要重新考虑一般的数据库布局,如果它们实际上具有相同的属性且仅在类型上有所不同,则将三者合并为一个。