【问题标题】:One to one left join一对一左连接
【发布时间】:2015-08-18 08:40:36
【问题描述】:

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

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

【问题讨论】:

  • 你使用的是哪个版本的sql server?
  • 请以文本形式提供示例数据以供重复使用 - 或者更好地作为插入数据
  • 我插入了插入数据:-)

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


【解决方案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
 ...

【讨论】:

    【解决方案2】:

    您可以将两个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 |
    

    【讨论】:

      【解决方案3】:

      只需使用这个...

      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
      

      【讨论】:

      • 能否请您写下您的提议的优势是什么?
      • 连接数较少(只有2个),并且没有使用子查询,这将提高性能..
      • 我同意@Luaan 至少需要 2 个左连接才能获得您的输出。
      猜你喜欢
      • 1970-01-01
      • 2013-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多