记录的顺序或顺序

时间:2018-02-08 17:58:43

标签: sql sql-server-2008 tsql

如何根据进入商店的产品顺序显示状态(OK,NOT),条件如下:
    在FRION之前的4或更少BULBS然后状态将是OK     如果在FRION之前有5个或更多BULBS,那么状态将不是

enter image description here

enter image description here

2 个答案:

答案 0 :(得分:1)

尝试这样的事情:

DECLARE @Table1 TABLE (
    Product VARCHAR(10),
    TheTime TIME
)

INSERT INTO @Table1 (Product, TheTime) VALUES
('Frion','1:00'),
('Frion','2:00'),
('Bulbs','3:00'),
('Bulbs','4:00'),
('Bulbs','5:00'),
('Frion','6:00'),
('Frion','7:00'),
('Bulbs','8:00'),
('Bulbs','9:00'),
('Bulbs','10:00'),
('Bulbs','11:00'),
('Bulbs','12:00'),
('Frion','13:00'),
('Frion','14:00'),
('Bulbs','15:00'),
('Frion','16:00'),
('Bulbs','17:00'),
('Frion','18:00'),
('Frion','19:00'),
('Frion','20:00'),
('Frion','21:00'),
('Frion','22:00'),
('Bulbs','23:00')

;WITH X AS (
    SELECT *, SUM(Y.Dif) OVER (ORDER BY Y.TheTime) AS Grp
    FROM (
        SELECT *, 
            CASE 
                WHEN Product<>LAG(Product) OVER (ORDER BY TheTime) 
                THEN 1 
                ELSE 0 
            END AS Dif
        FROM @Table1
    ) Y
)
SELECT X1.Product, X1.TheTime, 
    --X1.Dif, X1.Grp, Q2.Grp, Q2.Cnt, Q2.PrevCnt,
    CASE 
        WHEN X1.Product='Frion' AND Q2.PrevCnt>4 AND X1.Dif=1 
        THEN 'NOT' 
        ELSE 'OK' 
    END AS Status
FROM X X1 
INNER JOIN (
    SELECT *, LAG(Q1.Cnt) OVER (ORDER BY Q1.Grp) AS PrevCnt
    FROM (
        SELECT X2.Grp, COUNT(*) AS Cnt FROM X X2 GROUP BY X2.Grp
    ) Q1
) Q2 ON Q2.Grp = X1.Grp
ORDER BY TheTime

首先,在Y子查询中,我们标记产品与前一行中的产品(Dif列)不同的每一行。名为X的CTE通过计算在该行(Grp列)之前显示的不同行的数量来计算每行的组编号。

然后在Q1中我们计算每个组中的行数(Cnt列),在Q2中我们计算前一个组中的行数({{1}列)。

最后,我们使用CASE表达式来提供预期的结果。

答案 1 :(得分:1)

@dran,我认为以下内容适合您在SQL Server 2008中使用。

我在没有运行或调试它的情况下把它全部写在了我的头顶,所以请原谅任何小错误(我总是说这是为了掩盖自己),但我相信你可以看到这个方法的要点。

前两个步骤基本上是将按顺序排列的产品分段为连续的块 - 从另一个中减去一个连续的行号会产生一个唯一的“段号”(带有不规则的步进)。

一旦它们被分段,我们就会计算每行中有多少行。我们还对段自身进行排序以建立每个段的编号顺序(并提供顺序段号,而不是不规则步进,以便我们可以稍后通过从当前段号中减去1来回顾前一个段) 。

我们对每个段中的行进行编号,以确保以后的连接可以定位段中的单个行(为了检查该段的计数,并且没有成功的连接,导致左侧的行为根据右边的行数重复,通常是连接。)

WITH step1 AS
(
    SELECT
        product
        ,time
        ,ROW_NUMBER() OVER (ORDER BY time) AS rownum_by_time
        ,ROW_NUMBER() OVER (PARTITION BY product ORDER BY time) AS rownum_prod_by_time
    FROM [YOUR_TABLE]
)
,step2 AS
(
    SELECT
        *
        ,(rownum_by_time - rownum_prod_by_time) AS prod_sgmt
    FROM step1
)
,step3 AS
(
    SELECT
        *
        ,MIN(time) OVER (PARTITION BY product, prod_sgmt) AS prod_sgmt_earliest_time
    FROM step2
)
,analysed_data AS
(
    SELECT
        *
        ,COUNT(ALL product) OVER (PARTITION BY product, prod_sgmt) AS prod_sgmt_count
        ,ROW_NUMBER() OVER (PARTITION BY product, prod_sgmt ORDER BY time) AS prod_sgmt_rownum
        ,DENSE_RANK() OVER (ORDER BY prod_sgmt_earliest_time) AS prod_sgmt_rank
    FROM step3
)
SELECT
    ad.product
    ,ad.time
    ,CASE 
        WHEN adsj.product IS NOT NULL THEN
            'NOT'
        ELSE
            'OK'
        END AS status --if we have a join that met the criteria, it's not ok, otherwise ok

FROM 
    analysed_data AS ad

LEFT JOIN
    analysed_data AS adsj --self join so we can get the prior segment
    ON (ad.product = 'Frion') --only consider joining if we're dealing with a row in a Frion segment
    AND (ad.prod_sgmt_rownum = 1) --only for the first row in the Frion segment
    AND (adsj.product = 'Bulbs') --only consider joining to Bulb segments
    AND (adsj.prod_sgmt_rank = (ad.prod_sgmt_rank - 1)) --and only if the Bulb segment is the immediately prior segment
    AND (adsj.prod_sgmt_rownum = 1) --only join to the first row in the Bulb segment (since they all carry the total row count for their segment)
    AND (adsj.prod_sgmt_count > 4) --only join if the Bulb segment contains more than 4 rows