简体   繁体   English

一对一左连接

[英]One to one left join

How to make a left join with only one-to-one row in each category. 如何在每个类别中仅使用一对一行进行左连接。 Here are category id's and product prices. 这是类别ID和产品价格。 Note that I do not want emerge repetitions in 5th category that would occur if I used LEFT JOIN. 请注意,如果我使用LEFT JOIN,我不希望在第5类中出现重复重复。

外适用

(1) The most suitable join is when both category and price in each tables match. (1)最合适的连接是每个表中的类别和价格都匹配。 This is the case of the 2nd category (note that the rows are in different order in table A and B) 这是第二类的情况(注意表A和B中的行顺序不同)
(2) If only the category match, then I want to show field of whatever row (like I did in the 1st category with info that it is one of many rows). (2)如果只有类别匹配,那么我想显示任何行的字段(就像我在第一类中所做的那样,信息是它是多行之一)。
(3) If neither category nor price match, I want to get NULL. (3)如果类别和价格都不匹配,我想得到NULL。

I used the following query but it is too slow for me. 我使用了以下查询,但对我来说太慢了。

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

Update. 更新。 I add sample data: 我添加了样本数据:

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');

Sounds like using two left joins would work just fine: 听起来像使用两个左连接可以正常工作:

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

This would usually be tricky, because it could cause you trouble with row duplication, but it shouldn't hurt in your case. 这通常很棘手,因为它可能会导致行重复出现问题,但在您的情况下不应该受到影响。

If you need the One of many text as well, just add it to the coalesce, eg coalesce(B1.Field, 'One of many: ' + B2.Field) - make sure you have the proper types, though. 如果您还需要One of many文本中的One of many ,只需将其添加到coalesce,例如coalesce(B1.Field, 'One of many: ' + B2.Field) - 确保您拥有正确的类型。

EDIT: 编辑:

Oh, you do care about duplication. 哦,你关心重复。 In that case, a subquery might be a better choice: 在这种情况下,子查询可能是更好的选择:

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

You can have two left join with Table2 (one for exact match and one for one to many match) and use ROW_NUMBER() for managing association of rows for exact match 您可以与Table2进行两次left join (一次用于完全匹配,一次用于一对多匹配)并使用ROW_NUMBER()管理完全匹配的行关联

Something like this. 像这样的东西。 SQL Fiddle SQL小提琴

Sample Data 样本数据

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');

Query 询问

;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

Output 产量

| 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 |

Simply use this... 只需使用此...

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM