如何使用“OR”条件执行连接到表?

时间:2013-01-16 19:59:42

标签: sql sql-server-2008 tsql join inner-join

我有一个我似乎无法解决的SQL语句......我不确定如何在我的连接上执行“OR”。事实上,我不确定我是否应该加入一个联盟...这是我到目前为止所拥有的:

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

我还没有运行这个,但我认为这将使我获得所有购买日期也同时包含TX33和TX34交易的订单。没有这两个交易的任何订单都不会显示(由于INNER JOINs)。我坚持的部分是:

我还需要确保订单还包含:

  • TX35和TX36
  • TX37
  • TX38 AND TX39

只有其中一个附加条件是必要的。我知道我不能简单地INNER JOIN,因为这意味着它必须在那里。如果我做一个常规JOIN,我可能会使它工作,如果其中一个'OR'条件本身不是'AND'条件(我不知道如何TX35 AND TX36作为一个JOIN条件,也不{ {1}}。

4 个答案:

答案 0 :(得分:3)

选择逻辑需要在WHERE子句中。也许是这样的:

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

如果您需要四个独立的选择标准,则需要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') 
)

答案 1 :(得分:2)

您可以在ON子句中使用复杂条件。使用LEFT OUTER JOIN可以处理WHERE子句中的奇数情况(TX37)。

请注意,R子句中对WHERE的引用必须处理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 )

如果您真的只想要包含TX33,TX34 的一个或多个其他模式的订单,那么它会稍微复杂一些。将group by L.OrderIdcount( L.OrderId )一起使用可以查找在模式中有两个或更多匹配的订单。它开始接近这样的事情:

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

答案 2 :(得分:2)

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

您也可以这样写:

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 ;

答案 3 :(得分:-1)

在摆弄了一段时间之后,我想我已经通过以下查询实现了您的目标:

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

内部SELECT应仅返回链接到具有代码TX34,TX35以及TX35,TX37或TX38的交易的订单。我们在结果中保留了最后一个代码的副本。

然后我们必须进一步缩小列表范围,保留其第三个代码为TX37(无需其他条件)的订单或具有与之相关的剩余代码的订单。

我认为这种方法应该比在事务表中加入四次而不首先过滤它更好:它应该需要O*(T+T+T)*(T+T) = 6*O*T^2次迭代,而四次未过滤连接方法需要O*T*T*T*T = O*T^4次迭代。