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