SQL - JOINing表ON a = b AND c = b?

时间:2017-06-01 18:37:21

标签: sql sql-server tsql join

我正在构建一个来自多个表的查询,并遇到了我不完全理解的JOIN语句的实现......

作为一个例子,我有这些表:

Applications AS a      - columns(ID, MemberName)
ContractAHistory AS ca - columns(ID, AppID, PolicyNumber)
ContractBHistory AS cb - columns(ID, AppID, PolicyNumber)
MemberPolicy AS m      - columns(ID, PolicyNumber)

为清楚起见,有两种不同的合同(A和B),每种合同都分配了一个PolicyNumber。

我希望能够像这样进行查询:

SELECT * FROM Applications a
FULL OUTER JOIN ContractAHistory ca ON a.ID=ca.AppID
FULL OUTER JOIN ContractBHistory cb ON a.ID=cb.AppID
FULL OUTER JOIN MemberPolicy m ON 
    (ca.PolicyNumber=m.PolicyNumber AND cb.PolicyNumber=m.PolicyNumber) 
WHERE m.PolicyNumber = 79301;

然而,这对我没有用,所以我已经解决了这个问题:

SELECT * FROM Applications a
FULL OUTER JOIN ContractAHistory ca ON a.ID=ca.AppID
FULL OUTER JOIN ContractBHistory cb ON a.ID=cb.AppID
FULL OUTER JOIN MemberPolicy m1 ON ca.PolicyNumber=m1.PolicyNumber
FULL OUTER JOIN MemberPolicy m2 ON ca.PolicyNumber=m2.PolicyNumber
WHERE m1.PolicyNumber = 79301 OR m2.PolicyNumber = 79301;

不幸的是,这意味着我的WHERE子句需要更详细:

... WHERE m1.PolicyNumber = 79301 OR m2.PolicyNumber = 79301;

而不是

... WHERE m.PolicyNumber = 79301;

在这种情况下是否有可能实现我的理想?最后,我希望能够在正确构造的SQL查询中使用所有这些干净的WHERE子句:

... WHERE a.MemberName = 'Annika Hansen';

... WHERE m.PolicyNumber = 79301;

... WHERE m.ID = 79;

... WHERE a.ID = 74656;

我正在使用SQL-Server,但我试图将此作为关于SQL的一般问题。如果我不清楚,请告诉我 - 提前谢谢你!

以下是一个不良结果的示例(感谢@TheEsisia对于大部分内容:)

declare @Applications table (ID int, MemberName varchar(30))
declare @ContractAHistory table (ID int, AppID int, PolicyNumber varchar(30))
declare @ContractBHistory table (ID int, AppID int, PolicyNumber varchar(30))
declare @MemberPolicy table (ID int, PolicyNumber varchar(30))

Insert Into @Applications Values
 (1, 'AA')
,(2, 'BB')
,(3, 'CC')
,(4, 'DD')

Insert Into @ContractAHistory Values
  (1, 1, 'A')
 ,(2, 2, 'B')

Insert Into @ContractBHistory Values
 (1, 3, 'H12007')
,(2, 4, 'GT31')

Insert Into @MemberPolicy Values
 (1, 'A')
,(2, 'B')
,(3, 'H12007')
,(4, 'GT31')

SELECT * FROM @Applications a
FULL OUTER JOIN @ContractAHistory ca ON a.ID=ca.AppID
FULL OUTER JOIN @ContractBHistory cb ON a.ID=cb.AppID
FULL OUTER JOIN @MemberPolicy m ON 
(ca.PolicyNumber=m.PolicyNumber AND cb.PolicyNumber=m.PolicyNumber)
WHERE m.PolicyNumber = 'GT31';

更新: @TheEsisia帮助我看到在最后一个查询中用OR替换AND可以达到我想要的效果。但回答@ ThorstenKettner的问题:我当然可能不需要所有的FULL OUTER JOIN - 但是,我确实希望根据特定的WHERE子句提取所有信息。我只能通过一个拼图获得信息请求,所以我想基于m.ID或AppID或PolicyNumber提取所有内容。为了提高效率,我希望使用相同的主查询,并且只适当地更改WHERE子句。此外,每个PolicyNumber匹配一个成员和一个合同(A或B)和一个a.ID.这就是复杂性的来源:我从来没有从PolicyNumber知道它是哪种合同。我希望这有助于澄清我的用例。谢谢大家。

3 个答案:

答案 0 :(得分:2)

我不知道你为什么说“这对我不起作用”,因为它对我有用:

declare @Applications table (ID int, MemberName varchar(30))
declare @ContractAHistory table (ID int, AppID int, PolicyNumber varchar(30))
declare @ContractBHistory table (ID int, AppID int, PolicyNumber varchar(30))
declare @MemberPolicy table (ID int, PolicyNumber varchar(30))

Insert Into @Applications Values
 (1, 'AA')
,(2, 'BB')
,(3, 'CC')
,(4, 'DD')

Insert Into @ContractAHistory Values
 (1, 1, 'A')
 ,(2, 2, 'B')


Insert Into @ContractBHistory Values
 (1, 2, 'H12007')
,(2, 3, 'GT31')

Insert Into @MemberPolicy Values
 (1, 'WW2007')
,(2, 'LL2009')
,(3, 'JJ2010')
,(4, 'RR2009')


SELECT * FROM @Applications a
FULL OUTER JOIN @ContractAHistory ca ON a.ID=ca.AppID
FULL OUTER JOIN @ContractBHistory cb ON a.ID=cb.AppID
FULL OUTER JOIN @MemberPolicy m ON 
(ca.PolicyNumber=m.PolicyNumber AND cb.PolicyNumber=m.PolicyNumber)

<强>更新

如果您的原始问题与此问题完全相同,那么您既不需要AND也不需要OR。您不需要具有该条件的左侧:

declare @Applications table (ID int, MemberName varchar(30))
declare @ContractAHistory table (ID int, AppID int, PolicyNumber varchar(30))
declare @ContractBHistory table (ID int, AppID int, PolicyNumber varchar(30))
declare @MemberPolicy table (ID int, PolicyNumber varchar(30))

Insert Into @Applications Values
 (1, 'AA')
,(2, 'BB')
,(3, 'CC')
,(4, 'DD')

Insert Into @ContractAHistory Values
  (1, 1, 'A')
 ,(2, 2, 'B')

Insert Into @ContractBHistory Values
 (1, 3, 'H12007')
,(2, 4, 'GT31')

Insert Into @MemberPolicy Values
 (1, 'A')
,(2, 'B')
,(3, 'H12007')
,(4, 'GT31')

SELECT * FROM @Applications a
FULL OUTER JOIN @ContractAHistory ca ON a.ID=ca.AppID
FULL OUTER JOIN @ContractBHistory cb ON a.ID=cb.AppID
FULL OUTER JOIN @MemberPolicy m ON 
cb.PolicyNumber=m.PolicyNumber
WHERE m.PolicyNumber = 'GT31';

注意:您的原始查询可能仍需要OR,但除非我们知道您要实现的目标,否则没有人可以告诉您。

点击此链接以使用查询:link

答案 1 :(得分:1)

实际上,它非常简单。创建两个相同的表A和B.

CREATE TABLE A (ID INT NULL, NAME VARCHAR(5) NULL)
CREATE TABLE B (ID INT NULL, NAME VARCHAR(5) NULL)

--TABLE A
INSERT A 
SELECT 1, 'John'
UNION ALL
SELECT 2, 'Henry'

--TABLE B
INSERT B 
SELECT 1, 'John'
UNION ALL
SELECT 2, 'Henry'

--scenario #1:
SELECT * 
FROM A
LEFT JOIN B
ON A.ID = B.ID AND B.NAME = 'Henry'

--scenario #2:
SELECT * 
FROM A
LEFT JOIN B
ON A.ID = B.ID
WHERE B.NAME = 'Henry'

根据方案#1,条件A.ID = B.IDB.NAME = 'Henry'将首先在执行左连接之前过滤表A和B.

enter image description here

根据方案#2,条件A.ID = B.ID将在加入之前执行过滤,而条件B.NAME = 'Henry'将在执行左连接后过滤完整数据集。

enter image description here

答案 2 :(得分:1)

以下是我们应该如何访问表格:

  1. 使用政策号码,我们会找到政策记录。
  2. 与该记录相关联的是A合同记录或B合同记录。所以我们必须外连接这些表。
  3. 然后我们想要找到合同申请。这可以通过内部联接来完成,因为我们总是期望匹配。 (我们不希望没有匹配的应用程序条目的合同。或者,如果我们这样做,将其切换到左外连接)
  4. 完整的查询:

    SELECT * 
    FROM @MemberPolicy m
    LEFT OUTER JOIN @ContractAHistory ca ON ca.PolicyNumber = m.PolicyNumber
    LEFT OUTER JOIN @ContractBHistory cb ON cb.PolicyNumber = m.PolicyNumber
    INNER JOIN @Applications a ON a.ID IN (ca.AppID, cb.AppID)
    WHERE m.PolicyNumber = 'GT31';
    

    如果合同非常相似,您也可以合并两个表。 (但在这种情况下,更改数据库设计可能会好得多,并且只为两个合同类型开始使用这一个表。)在这种情况下,我们不需要任何外部联接;我们选择政策,合同及其申请:

    SELECT * 
    FROM @MemberPolicy m
    INNER JOIN 
    (
      SELECT PolicyNumber, title, description, client, a_only_col, null as b_only_col
      FROM @ContractAHistory
      UNION ALL
      SELECT PolicyNumber, title, description, client, null as a_only_col, b_only_col
      FROM @ContractBHistory
    ) c ON c.PolicyNumber = m.PolicyNumber
    INNER JOIN @Applications a ON a.ID = c.AppID
    WHERE m.PolicyNumber = 'GT31';