【问题标题】:SQL Join Query - two FK, one fieldSQL 连接查询 - 两个 FK,一个字段
【发布时间】:2014-06-15 01:57:09
【问题描述】:

我遇到了一个不知道如何解决的小问题 - 我以前从未遇到过。我正在使用 MS SQL 并有一个 SQL JOIN 查询来查询两个表——保险和债权人。保险表包含所有保单详细信息,而债权人表包含公司详细信息(保险公司/经纪人名称等)。

Insurance 表包含两个名为 CreditorID 和 BrokerID 的外键。这两个外键都与同一个字段相关,即 Creditor 表中的 CreditorID 字段(因为一些债权人既是保险公司又是保险经纪人)。我不确定的是如何编写查询。

例如Insurance 表可能如下所示:

lInsuranceID   lCreditorID  lBrokerID  mLastPremium  dDatePaid  sPolicyNumber
1              1            null       1000.00       28/03/2014 12345
2              14           1          2000.00       17/03/2014 67891

Creditor 表可能如下所示:

lCreditorID   sCreditorName
1             Frank's Insurance COMPANY
14            Frank's Insurance BROKER

所以问题是,当我检索特定财产的信息时,我需要从 Creditor 表中提取信息两次 - 一次使用 Insurance.CreditorID = Creditor.CreditorID,再次使用 Insurance.CreditorID = Creditor.BrokerID。

但我不知道如何在一次加入中做到这一点。我试过了,但没有用(我收到一条错误消息,提示“Creditor.sCreditorName”无法绑定:

 SELECT Insurance.sPolicyNumber,
        Insurance.dDatePaid,
        Insurance.dRenewal,
        Insurance.mLastPremium,
        Creditor.sCreditorName
   FROM Insurance
        INNER JOIN Creditor cred ON Insurance.lCreditorID = cred.lCreditorID
        INNER JOIN Creditor cred1 ON Insurance.lBrokerID = cred1.lCreditorID
  WHERE Insurance.lOwnersCorporationID = '1'

有什么建议吗?

编辑 好的,谢谢所有的提示,偷看。我修改了查询,使其看起来像这样,它似乎正在工作(我是一个这样的 JOIN 菜鸟。即使它有效,我也不知道为什么)

SELECT Insurance.sPolicyNumber, Insurance.dDatePaid, Insurance.dRenewal, Insurance.mLastPremium, cred.sCreditorName as Company, cred1.sCreditorName as Broker
FROM Insurance
LEFT JOIN Creditor cred ON Insurance.lCreditorID = cred.lCreditorID
LEFT JOIN Creditor cred1 ON Insurance.lBrokerID = cred1.lCreditorID
WHERE Insurance.lOwnersCorporationID = '1'

【问题讨论】:

  • 哦,澄清一下,我需要查询这两个字段的原因是,有时财产是由“Fred's Insurance Company”投保的,但实际上是由“Dude's Brokerage Firm”代理的。所以我需要这两个字段来确保所有信息都在那里。而且,我没有设计这个数据库,也不能修改设计一个 iota,因为其他系统依赖于它的结构。
  • Creditor.sCreditorName 不能绑定的原因是因为你没有使用 Creditor 作为别名 - 你有 cred 和 cred1 所以 SQL 不知道它属于哪个表(即使它是同一张表)。

标签: sql sql-server join


【解决方案1】:

我想你想要的是这样的

 SELECT Insurance.sPolicyNumber,
        Insurance.dDatePaid,
        Insurance.dRenewal,
        Insurance.mLastPremium,
        cred.sCreditorName AS Creditor,
        cred1.sCreditorName AS Broker
   FROM Insurance
        INNER JOIN Creditor cred ON Insurance.lCreditorID = cred.lCreditorID
        INNER JOIN Creditor cred1 ON Insurance.lBrokerID = cred1.lCreditorID
  WHERE Insurance.lOwnersCorporationID = '1'

我添加了一个 SQLFiddle example 来演示(减去示例表中不存在的 select 语句中的列)。这将使用您的原始查询,但请参阅下面的另一个示例 LEFT OUTER JOIN。

编辑:

如下所述,LEFT JOIN 将很有用,因此您不会过滤掉 BrokerID 为 NULL 的行。更新了 SQLFiddle 上的示例以显示这一点。

【讨论】:

    【解决方案2】:

    也许左连接可能会有所帮助?因为您可能并非在所有情况下都拥有 Broker ID。

     SELECT Insurance.sPolicyNumber,
            Insurance.dDatePaid,
            Insurance.dRenewal,
            Insurance.mLastPremium,
            Creditor.sCreditorName
       FROM Insurance
            INNER JOIN Creditor cred ON Insurance.lCreditorID = cred.lCreditorID
             LEFT JOIN Creditor cred1 ON Insurance.lBrokerID = cred1.lCreditorID
      WHERE Insurance.lOwnersCorporationID = '1'
    

    【讨论】:

    • 为什么投反对票? @Matt 是正确的,在这种情况下可能需要 LEFT JOIN,其中示例数据在 BrokerID 中显示 NULL,否则该行将被过滤掉,并且 OP 可能没有意识到,因为他们的查询不起作用目前全部。
    • 谢谢马特。这个不太好用(无法绑定 Creditor.sCreditorName),但我通过 SELECT .... Insurance.mLastPremium, cred.sCreditorName, cred1.sCreditorName .... 等
    【解决方案3】:

    您只需要使用SELECT表达式列表中的表别名:

     SELECT i.sPolicyNumber,
            i.dDatePaid,
            i.dRenewal,
            i.mLastPremium,
            c.sCreditorName,
            b.sCreditorName
       FROM Insurance i
            LEFT JOIN Creditor  c ON c.lCreditorID = i.lCreditorID
            LEFT JOIN Creditor  b ON b.lCreditorID = i.lBrokerID
      WHERE Insurance.lOwnersCorporationID = '1'
    

    请注意,我切换到了 LEFT OUTER JOIN。那是因为您已经证明引用值可能是 NULLable。如果您不使用 OUTER JOIN,您将丢失 Insurance 表中的行,我认为这对您不利。

    【讨论】:

      【解决方案4】:
       SELECT Insurance.sPolicyNumber,
              Insurance.dDatePaid,
              Insurance.dRenewal, 
              Insurance.mLastPremium, 
              -- the changed columns
              cred.sCreditorName AS CREDITOR,
              cred1.sCreditorName AS BROKER,  
         FROM Insurance
              INNER JOIN Creditor cred ON Insurance.lCreditorID = cred.lCreditorID
              LEFT OUTER JOIN Creditor cred1 ON Insurance.lBrokerID = cred1.lCreditorID
        WHERE Insurance.lOwnersCorporationID = '1'
      

      当您使用别名(cred 和 cred1)引用表时,您必须在列列表中使用别名

      【讨论】:

        【解决方案5】:

        在您的原始查询中,您引用的是“Creditor.sCreditorName”,但是您在连接中使用了别名 cred。

        另外,您正在使用内部连接,如果 FK 为空,这可能会导致一些条目。例如,您的 lInsuranceID = 1 条目将被遗漏。

        一个正确的查询可能是

         SELECT ins.sPolicyNumber,
                ins.dDatePaid,
                ins.dRenewal,
                ins.mLastPremium,
                cred.sCreditorName as CreditorName,
                brok.sCreditorName as BrokerName
           FROM Insurance ins
                LEFT JOIN Creditor cred ON ins.lCreditorID = cred.lCreditorID
                LEFT JOIN Creditor brok ON ins.lBrokerID = brok.lCreditorID
          WHERE ins.lOwnersCorporationID = '1'
        

        MSSQL 中的一些测试:

        declare @Insurance table
        (
            lInsuranceID int,
            lCreditorID  int,
            lBrokerID  int,
            mLastPremium  money,
            dDatePaid  date,
            sPolicyNumber int
        )
        
         INSERT INTO @Insurance
                (lInsuranceID, lCreditorID, lBrokerID, mLastPremium, dDatePaid,
                 sPolicyNumber)
         SELECT 1, 1, null, 1000.00, '2014-03-28', 12345
          UNION ALL
         SELECT 2, 14, 1, 2000.00, '2014-03-17', 67891
        
        declare @Creditor table
        (
            lCreditorID int,
            sCreditorName varchar(64)
        )
        
         INSERT INTO @Creditor
                (lCreditorID, sCreditorName)
         SELECT 1, 'Frank''s Insurance COMPANY'
          UNION ALL
         SELECT 14, 'Frank''s Insurance BROKER'
        
        
         SELECT ins.sPolicyNumber,
                ins.dDatePaid,
                ins.mLastPremium,
                brok.sCreditorName as BrokerName,
                cred.sCreditorName as CreditorName
           FROM @Insurance ins
                LEFT JOIN @Creditor cred on cred.lCreditorID = ins.lCreditorID
                LEFT JOIN @Creditor brok on brok.lCreditorID = ins.lBrokerID
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-01-21
          • 2016-05-27
          • 1970-01-01
          • 2018-07-03
          • 2017-07-05
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多