一对一左连接

时间:2015-08-18 08:40:36

标签: sql sql-server join sql-server-2008-r2

如何在每个类别中仅使用一对一行进行左连接。这是类别ID和产品价格。请注意,如果我使用LEFT JOIN,我不希望在第5类中出现重复重复。

outer apply

(1)最合适的连接是每个表中的类别和价格都匹配。这是第二类的情况(注意表A和B中的行的顺序不同)
(2)如果只有类别匹配,那么我想显示任何行的字段(就像我在第一类中所做的那样,信息是它是多行之一)。
(3)如果类别和价格都不匹配,我想得到NULL。

我使用了以下查询但对我来说太慢了。

with 
A as (select A.id, A.price 
,ROW_NUMBER() over(partition BY id) as Row_id_A
,ROW_NUMBER() OVER(PARTITION BY id, price order by price asc) AS [Row_id_price_A]
from TableA as A)
,
B as (select B.id, B.price, B.field
,ROW_NUMBER() over(partition BY id) as Row_id_B
,ROW_NUMBER() OVER(PARTITION BY id, price order by price asc) AS [Row_id_price_B]
from TableB as B)

select A.id, A.price, A.Row_A, 
,ResultField=case 
    when A.Row_id_A=C.Row_id_B then C.field 
    when [Row_id_price_A]=[Row_id_price_B] then D.field
    else N'One of many: '+C.field
    end 
from A

outer apply (select top 1 * from B
where 
A.id=B.id
and Row_A=Row_B
) as C

outer apply (select top 1 * from B
where 
A.id=B.id
and Row_A=Row_B
and [Row_id_price_A]=[Row_id_price_B]
) as D

更新。我添加了样本数据:

CREATE TABLE dbo.TableA(
   id    INTEGER NOT NULL 
  ,price INTEGER NOT NULL
);
INSERT INTO TableA(id,price) VALUES (1,50);
INSERT INTO TableA(id,price) VALUES (2,20);
INSERT INTO TableA(id,price) VALUES (2,30);
INSERT INTO TableA(id,price) VALUES (2,50);
INSERT INTO TableA(id,price) VALUES (4,15);
INSERT INTO TableA(id,price) VALUES (4,5);
INSERT INTO TableA(id,price) VALUES (5,100);
INSERT INTO TableA(id,price) VALUES (5,100);

CREATE TABLE dbo.TableB(
   id    INTEGER NOT NULL 
  ,price INTEGER NOT NULL
  ,field VARCHAR(2) NOT NULL
);
INSERT INTO TableB(id,price,field) VALUES (1,1,'A1');
INSERT INTO TableB(id,price,field) VALUES (2,30,'A2');
INSERT INTO TableB(id,price,field) VALUES (2,50,'A3');
INSERT INTO TableB(id,price,field) VALUES (2,20,'A4');
INSERT INTO TableB(id,price,field) VALUES (5,5,'A5');
INSERT INTO TableB(id,price,field) VALUES (5,100,'A6');
INSERT INTO TableB(id,price,field) VALUES (5,100,'A7');
INSERT INTO TableB(id,price,field) VALUES (6,1,'A8');

3 个答案:

答案 0 :(得分:1)

使用两个左连接听起来很不错:

select
 ...
 coalesce (B1.Field, B2.Field) as Field,
 ...

left join TableB B1 on B1.id = TableA.id and B1.price = TableA.price
left join TableB B2 on B2.id = TableA.id

这通常很棘手,因为它可能会导致行重复出现问题,但在您的情况下不应该受到伤害。

如果您还需要One of many文字,只需将其添加到合并,例如coalesce(B1.Field, 'One of many: ' + B2.Field) - 确保你有合适的类型。

修改

哦,你确实关心重复。在这种情况下,子查询可能是更好的选择:

select
 ...
 coalesce(B1.Field, (select top 1 Field from TableB where id = TableA.id)) as Field
 ...

答案 1 :(得分:1)

您可以将两个left join与Table2(一个用于完全匹配,一个用于一对多匹配)并使用ROW_NUMBER()管理完全匹配的行关联

像这样的东西。 SQL Fiddle

示例数据

CREATE TABLE Table1
(
    ID INT NOT NULL,
    Price INT NOT NULL
);

CREATE TABLE Table2
(
    ID INT NOT NULL,
    Price INT NOT NULL,
    Field VARCHAR(20) NOT NULL
);

INSERT INTO Table1 VALUES(1,50),(2,20),(2,30),(2,50),(4,15),(4,5),(5,100),(5,100);
INSERT INTO Table2 VALUES
    (1,1,'A1'),(2,30,'A2'),(2,50,'A3'),(2,20,'A4'),
    (5,5,'A5'),(5,100,'A6'),(5,100,'A7'),(6,1,'A8');

<强>查询

;WITH CT1 AS
(
SELECT *,rn = ROW_NUMBER()OVER(PARTITION BY ID,Price ORDER BY Price)
FROM Table1
), CT2 AS
(
SELECT *,rn = ROW_NUMBER()OVER(PARTITION BY ID,Price ORDER BY Field),
cc = ROW_NUMBER()OVER(PARTITION BY ID ORDER BY Price ASC)
FROM Table2
)
SELECT T1.*,ISNULL(T2.Field,'One of Many: ' + T3.Field) as Field
FROM CT1 T1 
LEFT JOIN CT2 T2 
    ON T1.ID = T2.ID
    AND (T1.Price = T2.Price AND T1.rn = T2.rn)
LEFT JOIN CT2 T3
    ON T1.ID = T3.ID
        AND T3.cc = 1
    ORDER BY T1.Id,T1.Price

<强>输出

| ID | Price | rn |           Field |
|----|-------|----|-----------------|
|  1 |    50 |  1 | One of Many: A1 |
|  2 |    20 |  1 |              A4 |
|  2 |    30 |  1 |              A2 |
|  2 |    50 |  1 |              A3 |
|  4 |     5 |  1 |          (null) |
|  4 |    15 |  1 |          (null) |
|  5 |   100 |  1 |              A6 |
|  5 |   100 |  2 |              A7 |

答案 2 :(得分:0)

只需使用此...

BEGIN TRAN
    SELECT A.id, A.price, B.field
    INTO #X
    FROM TableA A
    LEFT JOIN TableB B ON A.id = B.id AND A.price = B.price
    GROUP BY A.id, A.price, B.field

    SELECT X.id, X.price, IIF(X.price = B.price, B.field, 'One Of Many ' + B.field)
    FROM #X X
    LEFT JOIN TableB B ON X.id= B.id
    WHERE X.price = IIF(X.field IS NULL, X.price, B.price)
    GROUP BY X.id, X.price, B.price, B.field
ROLLBACK