我有一张桌子,上面列出了所有商品。这些记录适用于所发行文件的所有行。有些商品有日常销售而非其他商品。还有一些没有日常销售的人,有一定时期跟随销售。
我需要一个查询,根据每个项目向我显示销售的最长时间(以天数为单位)。这可能吗?
在Excel中可以使用按项目/日/数量表分组并应用以下公式:“如果天数= 0则返回0,否则,如果前一天的金额= 0则返回1否则返回前一天的金额+ 1“。最后,它只是检查此列的最大值。
在SQL中有任何帮助吗?谢谢!
这是一个小样本(1月1日到10日之间):
原始表:
SalesDate Doc ItemID Qty
01-jan 156 123456 10
01-jan 156 654321 5
01-jan 157 123456 3
02-jan 158 654321 4
02-jan 158 123456 7
03-jan 159 123456 8
04-jan 160 654321 3
04-jan 161 654321 8
05-jan 162 654321 3
06-jan 163 123456 7
06-jan 163 654321 2
06-jan 164 123456 9
07-jan 165 654321 4
08-jan 166 123456 5
09-jan 167 123456 6
10-jan 168 123456 3
10-jan 168 654321 5
10-jan 169 654321 1
中间表:
CalendarDate ItemID SumQty FollowedSalesDays
01-jan 123456 13 1
02-jan 123456 7 2
03-jan 123456 8 3
04-jan 123456 0 0
05-jan 123456 0 0
06-jan 123456 16 1
07-jan 123456 0 0
08-jan 123456 5 1
09-jan 123456 6 2
10-jan 123456 3 3
01-jan 654321 5 1
02-jan 654321 4 2
03-jan 654321 0 0
04-jan 654321 11 1
05-jan 654321 3 2
06-jan 654321 2 3
07-jan 654321 4 4
08-jan 654321 0 0
09-jan 654321 0 0
10-jan 654321 6 1
最终结果:
ItemID MaxFollowedSalesDays
123456 3
654321 4
答案 0 :(得分:0)
这是一个天真的实现。它的性能非常糟糕(二次时间复杂度),但它可以用来测试更好的实现。
SELECT ItemID, MAX(Filled)
FROM (
SELECT i1.ItemID,
DATEDIFF(day, i1.SalesDate, i2.SalesDate) AS Distance,
(
SELECT COUNT(DISTINCT i3.SalesDate)
FROM ItemsSold i3
WHERE i3.ItemID = i1.ItemID
AND i3.SalesDate BETWEEN i1.SalesDate AND i2.SalesDate
) AS Filled
FROM ItemsSold i1
INNER JOIN ItemsSold i2 ON i2.ItemID = i1.ItemID AND i2.SalesDate >= i1.SalesDate
) AS CartesianProduct
WHERE Distance + 1 = Filled
GROUP BY ItemID
要实现线性时间复杂度,您可以使用cursor。或者,将逻辑移动到应用程序服务器。
答案 1 :(得分:0)
这是一种基于cursor的方法。它应该以线性时间运行,使其比天真的实现快得多。
DECLARE @ItemID varchar(10) = NULL
DECLARE @SalesDate date
DECLARE @NextItemID varchar(10)
DECLARE @NextSalesDate date
DECLARE @StartDate date
DECLARE @Contiguity int
DECLARE @MaxContiguity int = 1
DECLARE @MaxSalesDateContiguityPerItem TABLE (
ItemID varchar(10) NOT NULL,
MaxSalesDateContiguity int NOT NULL
)
DECLARE SalesCursor CURSOR FOR
SELECT DISTINCT ItemID, SalesDate FROM ItemsSold ORDER BY ItemID, SalesDate
OPEN SalesCursor
DECLARE @more int = 3
WHILE @more > 0
BEGIN
FETCH NEXT FROM SalesCursor INTO @NextItemID, @NextSalesDate
SET @more = CASE
WHEN @@FETCH_STATUS <> 0 THEN 0 -- reached end of result set
WHEN @ItemID IS NULL OR @ItemID <> @NextItemID THEN 1 -- next item
WHEN DATEDIFF(day, @SalesDate, @NextSalesDate) > 1 THEN 2 -- date hole
ELSE 3
END
-- Calculate the length of the contiguity we just passed.
-- Compare with earlier contiguities; keep whatever is longest.
IF @more <= 2 AND @ItemID IS NOT NULL
BEGIN
SET @Contiguity = DATEDIFF(day, @StartDate, @SalesDate) + 1
IF @Contiguity > @MaxContiguity SET @MaxContiguity = @Contiguity
SET @StartDate = @NextSalesDate -- begin another contiguity
END
-- Flush the item we just passed to the temporary table.
IF @more <= 1 AND @ItemID IS NOT NULL
BEGIN
INSERT INTO @MaxSalesDateContiguityPerItem VALUES (@ItemID, @MaxContiguity)
SET @MaxContiguity = 1 -- start over with another item
END
SET @ItemID = @NextItemID
SET @SalesDate = @NextSalesDate
END
CLOSE SalesCursor
DEALLOCATE SalesCursor
SELECT * FROM @MaxSalesDateContiguityPerItem ORDER BY ItemID
注意:请将您varchar(10)
列的所有类型ItemID
替换为{{1}}。
编辑:进一步的性能考虑因素...
如果您处理记录集甚至使线性时间解决方案太慢,那么请注意有其他选择。在你的问题中,你已经提出了一个'中间表';您可以将此作为永久表,由定期作业定期更新,或立即由销售交易表上的trigger更新。后者保证实时信息,但它可以使交易更慢。