连接两个表,使其从右表中只返回一行,并从每个匹配中返回左表

时间:2014-11-25 18:03:04

标签: sql-server join

我有一个SQL查询,它根据给定的条件执行简单的左连接,如下所示

SELECT
a.Product,a.Grade,a.Term,a.Bid,
a.Offer,b.Ltrd

FROM CCS AS a 
left  JOIN LTR AS b ON  b.Product=a.Product
and b.Grade=a.Grade
and b.Term=a.Term 

我有两个表CCS和LTR以及以下数据

CCS Table

Id Product Grade  Term  Bid Offer
1  Xyz      A     Jan   20   30
2  XYz      A     Jan   25   35
3  abc      B     Feb   25   30

LTR Table

Id Product Grade  Term  Ltrd
1  Xyz      A     Jan   500
2  XYz      A     Jan   400

运行上面的查询时,它看起来与产品,等级,期限匹配,如果三者相等,则执行左连接并给出以下结果

Product Grade Term Bid Offer Ltrd
Xyz     A      Jan   20  30   500 
Xyz     A      Jan   20  30   400
XYz     A      Jan   25  35   500
XYz     A      Jan   25  35   400
abc     B      Feb   25   30  NULL
上面的

返回了5行,我尝试只获得表CCS中的三行,其中Ltrd(ie 400)表中列LTR的值最小,如下所示

  Product Grade Term Bid Offer Ltrd
    Xyz     A    Jan   20  30   400
    Xyz     A    Jan   25  35   NULL
    abc     B    Feb   25  30   NULL

Ltrd列中的上述结果中,我只想从LTR表中的匹配中获取最低值,并将其分配给第一行中的Ltrd并生成另一个{{1} (在上面的第二行中)和第三行当然是NULL因为NULL表中没有匹配

2 个答案:

答案 0 :(得分:2)

一种方法是使用CTE。它本质上是相同的查询,但我们使用ROW_NUMBER()来a)根据产品成绩成绩,和b)根据 Ltrd 在这些组中排序。排序将每个增长中的最低 Ltrd 值推送到每个组的第一行,然后我们只选择每个组的第一行。

DECLARE @CCS TABLE (Id INT, Product VARCHAR(20), Grade VARCHAR(5), Term VARCHAR(5),
                    Bid INT, Offer INT);
INSERT INTO @CCS VALUES (1, 'Xyz', 'A', 'Jan', 20, 30);
INSERT INTO @CCS VALUES (2, 'XYz', 'A', 'Jan', 25, 35);
INSERT INTO @CCS VALUES (3, 'abc', 'B', 'Feb', 25, 30);

DECLARE @LTR TABLE (Id INT, Product VARCHAR(20), Grade VARCHAR(5), Term VARCHAR(5),
                    Ltrd INT)
INSERT INTO @LTR VALUES (1, 'Xyz', 'A', 'Jan', 500);
INSERT INTO @LTR VALUES (2, 'XYz', 'A', 'Jan', 400);


;WITH cte AS
(
  SELECT b.Product, b.Grade, b.Term, b.Ltrd,
          ROW_NUMBER() OVER
          (PARTITION BY b.Product, b.Grade, b.Term ORDER BY b.Ltrd ASC)
            AS [RowNum]
  FROM @LTR b
)
SELECT a.Product, a.Grade, a.Term, a.Bid, a.Offer, 
       CASE WHEN ROW_NUMBER()
        OVER (PARTITION BY a.Product, a.Grade, a.Term ORDER BY a.Id ASC) = 1
                THEN b.Ltrd
                  ELSE NULL
       END AS [Ltrd]
FROM   @CCS a
LEFT JOIN cte b
       ON b.Product = a.Product
      AND b.Grade = a.Grade
      AND b.Term = a.Term
      AND b.RowNum = 1
ORDER BY a.Id ASC;

结果:

Product  Grade  Term    Bid  Offer  Ltrd
Xyz      A      Jan     20   30     400
XYz      A      Jan     25   35     NULL
abc      B      Feb     25   30     NULL

-

只是为了获得选项,另一种看起来更好的方法是使用OUTER APPLY

SELECT a.Product, a.Grade, a.Term, a.Bid, a.Offer,
       CASE WHEN ROW_NUMBER() OVER
               (PARTITION BY a.Product, a.Grade, a.Term ORDER BY a.Bid, a.Offer ASC)
                 = 1 THEN c.Ltrd
       ELSE NULL END AS [Ltrd]
FROM @CCS a
OUTER APPLY (SELECT TOP (1) b.Ltrd
             FROM @LTR b
             WHERE b.Product = a.Product
             AND b.Grade = a.Grade
             AND b.Term = a.Term
             ORDER BY b.Ltrd ASC
             ) c

结果是一样的。这可能会更糟糕,因为OUTER APPLY为外部表的每一行运行查询内部的查询(或者如果指定了函数而不是查询)。但有时很高兴看到这些东西如何工作,因为它可能有助于以后解决不同的问题。 CROSS APPLYOUTER APPLY条款非常有用

答案 1 :(得分:1)

您可以使用具有最小值的派生表作为左连接的源:

SELECT
  a.Product,
  a.Grade,
  a.Term,
  a.Bid,
  a.Offer,
  CASE 
    WHEN ROW_NUMBER() OVER (PARTITION BY a.Product ORDER BY a.Id) = 1 
    THEN b.Ltrd ELSE NULL 
  END AS LTRD
FROM CCS AS a 
LEFT JOIN (
    SELECT Product, Grade, Term, MIN(ltrd) ltrd
    FROM LTR 
    GROUP BY Product, Grade, term 
    ) AS b 
ON  b.Product = a.Product
AND b.Grade   = a.Grade
AND b.Term    = a.Term
ORDER BY a.Id 

Sample SQL Fiddle