简体   繁体   English

您如何在具有“ OR”条件的表中执行联接?

[英]How do you perform a join to a table with “OR” conditions?

I have a SQL statement that I can't seem to solve for... I'm not sure how to perform an "OR" on my join. 我有一条似乎无法解决的SQL语句...我不确定如何在联接上执行“或”操作。 In fact, I'm not sure if I should even be doing a join at all... Here is what I have so far: 实际上,我不确定我是否应该进行联接……这是到目前为止的结果:

SELECT o.* FROM dbo.Orders o
    INNER JOIN dbo.Transactions t1 ON t1.OrderId = o.OrderId
                                  AND t1.Code = 'TX33'
    INNER JOIN dbo.Transactions t2 ON t2.OrderId = o.OrderId
                                  AND t2.Code = 'TX34'
WHERE o.PurchaseDate NOT NULL

I haven't ran this yet, but I assume that this will get me all orders that have a purchase date that also have BOTH TX33 and TX34 transactions. 我还没有执行此操作,但是我认为这将使我所有具有购买日期且同时具有TX33和TX34交易的订单。 Any orders without both of those transactions won't show up (due to the INNER JOINs). 没有这两项交易的任何订单都不会显示(由于“内部联接”)。 The part I'm stuck at is this: 我遇到的问题是:

I need to be able to also ensure that the order also contains either: 我还需要确保该订单还包含以下任一内容:

  • TX35 AND TX36 TX35和TX36
  • TX37 TX37
  • TX38 AND TX39 TX38和TX39

Only one of those additional conditions is necessary. 这些附加条件中仅一项是必要的。 I know I can't simply INNER JOIN because that means it's required to be there. 我知道我不能简单地加入INNER JOIN,因为这意味着它必须存在。 If I do a regular JOIN I could possibly make it work if one of the 'OR' conditions wasn't itself an 'AND' condition (I'm not sure how to do TX35 AND TX36 as one JOIN condition, nor TX38 AND TX39 . 如果我进行常规JOIN,如果“ OR”条件之一本身不是“ AND”条件(我不确定如何将TX35 AND TX36作为一个JOIN条件,或者TX35 AND TX36怎么做) TX35 AND TX36 TX38 AND TX39

The selection logic needs to be in the WHERE clause. 选择逻辑必须在WHERE子句中。 Perhaps something like this: 也许是这样的:

SELECT o.* FROM dbo.Orders AS o, dbo.Transactions AS t1, dbo.Transactions AS t2
WHERE t1.OrderId = o.OrderId AND t2.OrderId = o.OrderId
AND o.PurchaseDate NOT NULL
AND (
  (t1.Code = 'TX33' AND t2.Code = 'TX34') OR 
  (t1.Code = 'TX35' AND t2.Code = 'TX36') OR 
  (t1.Code = 'TX37') OR 
  (t1.Code = 'TX38' AND t2.Code = 'TX39') 
)

In the case where you need four independent selection criteria, you will need to JOIN the table four times, for example: 在需要四个独立的选择条件的情况下,您将需要四次JOIN表,例如:

SELECT o.* FROM dbo.Orders AS o, dbo.Transactions AS t1, dbo.Transactions AS t2
WHERE t1.OrderId = o.OrderId AND t2.OrderId = o.OrderId 
AND t3.OrderId = o.OrderId AND t4.OrderId = o.OrderId
AND o.PurchaseDate NOT NULL
AND (t1.Code = 'TX33' AND t2.Code = 'TX34')
AND ( 
  (t3.Code = 'TX35' AND t4.Code = 'TX36') OR 
  (t3.Code = 'TX37') OR 
  (t3.Code = 'TX38' AND t4.Code = 'TX39') 
)

You can have complex condition in an ON clause. 您可以在ON子句中具有复杂的条件。 Using a LEFT OUTER JOIN allows you to handle the odd case (TX37) in the WHERE clause. 使用LEFT OUTER JOIN可以处理WHERE子句中的奇数大小写(TX37)。

Note that references to R in the WHERE clause must handle NULLs to avoid converting the outer join to an inner join. 请注意,在WHERE子句中对R引用必须处理NULL,以避免将外部联接转换为内部联接。

select L.*
  from dbo.Orders as L left outer join
    dbo.Orders as R on R.OrderId = L.OrderId and (
      ( L.Code = 'TX33' and R.Code = 'TX34' ) or
      ( L.Code = 'TX35' and R.Code = 'TX36' ) or
      ( L.Code = 'TX38' and R.Code = 'TX39' ) )
  where L.PurchaseDate is not NULL and ( L.Code = 'TX37' or R.Code is not NULL )

If you really want only orders that contain TX33, TX34 and one or more of the other patterns then it is a little more complicated. 如果您真的只想要包含TX33,TX34 以及其他一个或多个其他模式的订单,那就稍微复杂一点。 Using group by L.OrderId with a count( L.OrderId ) lets you find orders that have, say, two or more matches among the patterns. group by L.OrderIdcount( L.OrderId ) ,可以查找在模式之间具有两个或多个匹配项的订单。 It begins to approach something like this: 它开始处理如下内容:

declare @Orders as Table ( Id Int Identity, OrderId Int, Code VarChar(4), PurchaseDate Date )
insert into @Orders ( OrderId, Code, PurchaseDate ) values
  ( 1, 'TX37', GetDate() ),
  ( 2, 'TX37', GetDate() ), ( 2, 'FOO', GetDate() ),
  ( 3, 'TX33', GetDate() ), ( 3, 'TX34', GetDate() ),
  ( 4, 'TX33', GetDate() ), ( 4, 'TX34', GetDate() ), ( 4, 'TX37', GetDate() ),
  ( 5, 'TX33', GetDate() ), ( 5, 'TX34', GetDate() ), ( 5, 'TX35', GetDate() ),
    ( 5, 'TX36', GetDate() ),
  ( 6, 'TX33', GetDate() ), ( 6, 'TX34', GetDate() ), ( 6, 'TX35', GetDate() ),
    ( 6, 'TX36', GetDate() ), ( 6, 'TX37', GetDate() ),
  ( 7, 'TX38', GetDate() ), ( 7, 'TX39', GetDate() ), ( 7, 'TX35', GetDate() ),
    ( 7, 'TX36', GetDate() ), ( 7, 'TX37', GetDate() )

select * from (
  select L.OrderId,
    Max( case when L.Code = 'TX33' and R.Code = 'TX34' then 1 else 0 end ) as Mandatory,
    Count( L.OrderId ) as Matches
    from @Orders as L left outer join
      @Orders as R on R.OrderId = L.OrderId and (
        ( L.Code = 'TX33' and R.Code = 'TX34' ) or
        ( L.Code = 'TX35' and R.Code = 'TX36' ) or
        ( L.Code = 'TX38' and R.Code = 'TX39' ) )
    where L.PurchaseDate is not NULL and ( L.Code = 'TX37' or R.Code is not NULL )
    group by L.OrderId ) as Arnold
  where Mandatory = 1 and Matches > 1
SELECT o.* 
FROM dbo.Orders o
WHERE EXISTS ( SELECT *   FROM dbo.Transactions t1 
               WHERE t1.OrderId = o.OrderId   AND t1.Code = 'TX33'
             )
  AND EXISTS ( SELECT *   FROM dbo.Transactions t2 
               WHERE t2.OrderId = o.OrderId   AND t2.Code = 'TX34'
             )
  AND
    (     EXISTS ( SELECT *   FROM dbo.Transactions t1 
                   WHERE t1.OrderId = o.OrderId   AND t1.Code = 'TX35'
                 )
      AND EXISTS ( SELECT *   FROM dbo.Transactions t2 
                   WHERE t2.OrderId = o.OrderId   AND t2.Code = 'TX36'

    OR  EXISTS ( SELECT *   FROM dbo.Transactions t 
                 WHERE t.OrderId = o.OrderId    AND t.Code = 'TX37'
               )

    OR    EXISTS ( SELECT *   FROM dbo.Transactions t1 
                   WHERE t1.OrderId = o.OrderId   AND t1.Code = 'TX38'
                 )
      AND EXISTS ( SELECT *   FROM dbo.Transactions t2 
                   WHERE t2.OrderId = o.OrderId   AND t2.Code = 'TX39'
                 )
    ) ;

You could also write it like this: 您也可以这样写:

SELECT o.* 
FROM dbo.Orders o
  JOIN
    ( SELECT OrderId
      FROM dbo.Transactions
      WHERE Code IN ('TX33', 'TX34', 'TX35', 'TX36', 'TX37', 'TX38', 'TX39')
      GROUP BY OrderId
      HAVING COUNT(DISTINCT CASE WHEN Code = 'TX33' THEN Code END) = 1
         AND COUNT(DISTINCT CASE WHEN Code = 'TX34' THEN Code END) = 1
         AND ( COUNT(DISTINCT 
                     CASE WHEN Code IN ('TX35', 'TX36') THEN Code END) = 2
            OR COUNT(DISTINCT CASE WHEN Code = 'TX37' THEN Code END) = 1
            OR COUNT(DISTINCT 
                     CASE WHEN Code IN ('TX38', 'TX39') THEN Code END) = 2
             ) 
    ) t
    ON t.OrderId = o.OrderId ;

After fiddling with it for a while, I think I have achieved your goal with the following query: 经过一段时间的摆弄之后,我认为我已经通过以下查询实现了您的目标:

SELECT * FROM (
    SELECT o.*, t3.Code as t3 FROM dbo.Orders o
        INNER JOIN dbo.Transactions t1 ON t1.OrderId = o.OrderId
                                      AND t1.Code = 'TX33'
        INNER JOIN dbo.Transactions t2 ON t2.OrderId = o.OrderId
                                      AND t2.Code = 'TX34'
        INNER JOIN dbo.Transactions t3 ON t3.OrderId = o.OrderId
                                      AND (
                                           t3.Code = 'TX35' OR 
                                           t3.Code = 'TX37' OR
                                           t3.Code = 'TX38'
                                          )
    ) WHERE t3 = 'TX37'
        OR (t3 = 'TX36' AND EXISTS (SELECT t.Code FROM dbo.Transactions t WHERE t.OrderId = o.OrderId AND t.Code = 'TX36'))
        OR (t3 = 'TX38' AND EXISTS (SELECT t.Code FROM dbo.Transactions t WHERE t.OrderId = o.OrderId AND t.Code = 'TX39'))

The inner SELECT should return only Orders linked to Transactions with codes TX34, TX35, and either TX35, TX37 or TX38. 内部SELECT应该仅返回链接到代码为TX34,TX35以及TX35,TX37或TX38的交易的订单。 We keep a copy of this last code in the results. 我们将最后一个代码的副本保留在结果中。

Then we have to further narrow down the list, by keeping Orders whose 3rd code was either TX37 (no further conditions needed) or orders which have the remaining code associated to them. 然后,我们必须通过保留第三个代码为TX37的订单(不需要其他条件)或具有与之关联的其余代码的订单来进一步缩小列表的范围。

I think this approach should perform better than joining in the Transactions table four times without filtering it first: it should require O*(T+T+T)*(T+T) = 6*O*T^2 iterations, whereas the four-unfiltered-joins approach would require O*T*T*T*T = O*T^4 iterations. 我认为这种方法应比不首先过滤它而在事务表中加入四次更好:它应该需要O*(T+T+T)*(T+T) = 6*O*T^2次迭代,而四未经过滤的联接方法将需要O*T*T*T*T = O*T^4次迭代。

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

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