如何在这种情况下使用CROSS APPLY

时间:2014-04-21 23:43:32

标签: sql-server

我有一个ProductStatus表,如下所示。我需要列出最新状态为“SU”的所有产品。与此同时,我需要列出此产品的先前状态。

基于参考各种帖子,似乎CROSS APPLY将适用于此。我做了如下所列的尝试,但没有给出预期的结果。

SQL Server 2005(不使用子查询)中实现此目的的最佳方法是什么?

DECLARE @ProductStatus TABLE (ProductStatusID INT,  productCode VARCHAR(5), statusCode VARCHAR(2))

INSERT INTO @ProductStatus

SELECT 1,'10011','RE'  --Recevied
UNION
SELECT 2,'10011','SU'  --Suspended

UNION
SELECT 3,'10012','IT'  -- In Transit
UNION
SELECT 4,'10012','RE' -- Received
UNION
SELECT 10,'10012','PR' -- Produced
UNION
SELECT 12,'10012','SU'  -- Suspended

UNION
SELECT 14,'10013','RE'  -- Recevied

UNION
SELECT 16,'10014','SU'  -- Recevied
UNION
SELECT 18,'10014','RE'  -- Recevied

交叉申请尝试

SELECT *
FROM @ProductStatus P
CROSS APPLY 
(
   SELECT MAX(V.ProductStatusID) as maxVal
   FROM @ProductStatus V
   WHERE V.ProductCode = P.ProductCode
   AND V. ProductStatusID <  P.ProductStatusID 
   GROUP BY V.ProductCode   
)ML
WHERE P.statusCode = 'SU'

预期结果

enter image description here

3 个答案:

答案 0 :(得分:1)

Lijo,我已将其构建为CTE,因此您可以看到我是如何发展我的想法的。您可以将其重构为子查询,而不会影响它们的含义。

;with MostRecentStatus as (
select
    MAX(ProductStatusID) as ProductStatusID,
    productCode
from @ProductStatus as p1
group by productCode
)
,MostRecentIsSU as (
select
    p2.ProductStatusID,
    p2.productCode,
    p2.statusCode
from MostRecentStatus as mrs
inner join @ProductStatus as p2
    on p2.ProductStatusID = mrs.ProductStatusID
    and p2.statusCode = 'SU'
)
select
    m.ProductStatusID,
    m.productCode,
    m.statusCode,
    p3.statusCode as PrevStatus,
    p3.ProductStatusID as PrevProductStatusID
from MostRecentIsSU as m
left outer join @ProductStatus as p3
    on p3.productCode = m.productcode
    and p3.ProductStatusID = m.ProductStatusID - 1;

修改:..这里是ROW_NUMBER()版本,其中包含对@attila的赞誉。

;with InSquence as
(
    select
        ProductStatusID,
        productCode,
        statusCode,
        ROW_NUMBER() OVER(PARTITION BY productCode ORDER BY ProductStatusID desc) as Sequence
    from @ProductStatus
)
,FirstIsSU as
(
    select
        ProductStatusID,
        productCode
    from InSquence
    where Sequence = 1
    and statusCode = 'SU'
)
,PreviousCode as
(
    select
        ProductStatusID,
        productCode,
        statusCode
    from InSquence
    where Sequence = 2
)
select
    f.ProductStatusID,
    f.productCode,
    'SU' as CurrentStatus,
    p.statusCode as PrevStatus,
    p.ProductStatusID as PrevProductStatusID
from FirstIsSU as f
left outer join PreviousCode as p
    on p.productCode = f.ProductCode;

答案 1 :(得分:1)

您可以使用cross apply执行此操作,但我认为row_number()是一种更简单的方法:

select ProductCode,
       max(case when seqnum = 1 then statusCode end) as LastStatus,
       max(case when seqnum = 2 then statusCode end) as PrevStatus
from (select p.*,
             row_number() over (partition by ProductCode order by ProductStatusId desc) as seqnum
      from @ProductStatus p
     ) p
group by ProductCode
having max(case when seqnum = 1 then statusCode end) = 'SU';

答案 2 :(得分:0)

这是一个复杂的解决方案,主要用于说明这可能应该使用row_number()完成:)

SELECT 
F.productCode, F.statusCode, F.productStatusID, F.PriorProductStatusID, PriorStatus.statusCode
FROM
(
SELECT 
PCS.productCode, PCS.statusCode, PCS.productStatusID, MAX(PS.productStatusID) PriorProductStatusID
FROM 
(
SELECT productCode, MAX(productStatusID) productStatusID
FROM @ProductStatus
GROUP BY productCode
) LatestStatus
INNER JOIN
@ProductStatus PCS
ON PCS.productCode = LatestStatus.productCode
AND PCS.productStatusID = LatestStatus.productStatusID
AND PCS.statusCode = 'SU'
LEFT OUTER JOIN
@ProductStatus PS
ON PS.productCode = PCS.productCode
AND PS.productStatusID < PCS.productStatusID
GROUP BY PCS.productCode, PCS.statusCode, PCS.productStatusID
) F
LEFT OUTER JOIN
@ProductStatus PriorStatus
ON  F.productCode = PriorStatus.productCode
AND F.PriorProductStatusID = PriorStatus.ProductStatusID