1

我有一条似乎无法解决的 SQL 语句……我不确定如何对我的联接执行“或”。事实上,我什至不确定我是否应该加入...这是我目前所拥有的:

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 JOIN)。我坚持的部分是:

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

  • TX35 和 TX36
  • TX37
  • TX38 和 TX39

只有这些附加条件之一是必要的。我知道我不能简单地 INNER JOIN 因为这意味着它必须在那里。如果我进行常规 JOIN,如果“OR”条件之一本身不是“AND”条件(我不确定如何TX35 AND TX36作为一个 JOIN 条件,也不知道TX38 AND TX39.

4

4 回答 4

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') 
)
于 2013-01-16T20:05:39.003 回答
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 ;
于 2013-01-16T21:38:49.660 回答
2

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

请注意,子句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.OrderIdwith acount( 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
于 2013-01-16T20:24:02.183 回答
-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迭代。

于 2013-01-16T20:03:55.227 回答