在不使用游标的情况下处理句点和日期

时间:2010-10-22 12:23:43

标签: tsql date fetch cursors period

我想解决这个问题,避免使用游标(FETCH)。

问题出现了......

1st Table/quantity
------------------
periodid periodstart periodend quantity

1        2010/10/01 2010/10/15    5


2st Table/sold items
-----------------------
periodid periodstart periodend solditems

14343    2010/10/05 2010/10/06    2

现在我想获得以下视图或仅查询结果

Table Table/stock
-----------------------
periodstart periodend itemsinstock

2010/10/01 2010/10/04      5

2010/10/05 2010/10/06      3

2010/10/07 2010/10/15      5

如果不使用游标,或者不使用单个日期而不使用句点,似乎无法解决此问题。

我将不胜感激。

由于

5 个答案:

答案 0 :(得分:2)

DECLARE @t1 TABLE (periodid INT,periodstart DATE,periodend DATE,quantity INT)
DECLARE @t2 TABLE (periodid INT,periodstart DATE,periodend DATE,solditems INT)

INSERT INTO @t1 VALUES(1,'2010-10-01T00:00:00.000','2010-10-15T00:00:00.000',5)
INSERT INTO @t2 VALUES(14343,'2010-10-05T00:00:00.000','2010-10-06T00:00:00.000',2)

DECLARE @D1 DATE

SELECT @D1 = MIN(P) FROM (SELECT MIN(periodstart) P FROM @t1
                          UNION ALL
                          SELECT MIN(periodstart) FROM @t2) D

DECLARE @D2 DATE

SELECT @D2 = MAX(P) FROM (SELECT MAX(periodend) P FROM @t1
                          UNION ALL
                          SELECT MAX(periodend) FROM @t2) D

;WITH 
L0 AS (SELECT 1 AS c UNION ALL SELECT 1),
L1 AS (SELECT 1 AS c FROM L0 A CROSS JOIN L0 B),
L2 AS (SELECT 1 AS c FROM L1 A CROSS JOIN L1 B),
L3 AS (SELECT 1 AS c FROM L2 A CROSS JOIN L2 B),
L4 AS (SELECT 1 AS c FROM L3 A CROSS JOIN L3 B),
Nums AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS i FROM L4),
Dates AS(SELECT DATEADD(DAY,i-1,@D1) AS D FROM Nums where i <= 1+DATEDIFF(DAY,@D1,@D2))  , 
Stock As (
SELECT D ,t1.quantity - ISNULL(t2.solditems,0) AS itemsinstock
FROM Dates
LEFT OUTER JOIN @t1 t1 ON t1.periodend >= D and t1.periodstart <= D
LEFT OUTER JOIN @t2 t2 ON t2.periodend >= D and t2.periodstart <= D ),
NStock As (
select D,itemsinstock, ROW_NUMBER() over (order by D) - ROW_NUMBER() over (partition by itemsinstock order by D) AS G
from Stock)
SELECT MIN(D) AS periodstart, MAX(D) AS periodend, itemsinstock 
FROM NStock
GROUP BY G, itemsinstock
ORDER BY periodstart

答案 1 :(得分:1)

希望比马丁更容易阅读。我使用了不同的表格和样本数据,希望能够推断出正确的信息:

CREATE TABLE [dbo].[Quantity](
    [PeriodStart] [date] NOT NULL,
    [PeriodEnd] [date] NOT NULL,
    [Quantity] [int] NOT NULL
) ON [PRIMARY]

CREATE TABLE [dbo].[SoldItems](
    [PeriodStart] [date] NOT NULL,
    [PeriodEnd] [date] NOT NULL,
    [SoldItems] [int] NOT NULL
) ON [PRIMARY]

INSERT INTO Quantity (PeriodStart,PeriodEnd,Quantity)
SELECT '20100101','20100115',5

INSERT INTO SoldItems (PeriodStart,PeriodEnd,SoldItems)
SELECT '20100105','20100107',2 union all
SELECT '20100106','20100108',1

现在的实际查询是:

;WITH Dates as (
    select PeriodStart as DateVal from SoldItems union select PeriodEnd from SoldItems union select PeriodStart from Quantity union select PeriodEnd from Quantity
), Periods as (
    select d1.DateVal as StartDate, d2.DateVal as EndDate
    from Dates d1 inner join Dates d2 on d1.DateVal < d2.DateVal left join Dates d3 on d1.DateVal < d3.DateVal and d3.DateVal < d2.DateVal where d3.DateVal is null
), QuantitiesSold as (
    select StartDate,EndDate,COALESCE(SUM(si.SoldItems),0) as Quantity
    from Periods p left join SoldItems si on p.StartDate < si.PeriodEnd and si.PeriodStart < p.EndDate
    group by StartDate,EndDate
)
select StartDate,EndDate,q.Quantity - qs.Quantity
from QuantitiesSold qs inner join Quantity q on qs.StartDate < q.PeriodEnd and q.PeriodStart < qs.EndDate

结果是:

StartDate   EndDate (No column name)
2010-01-01  2010-01-05  5
2010-01-05  2010-01-06  3
2010-01-06  2010-01-07  2
2010-01-07  2010-01-08  4
2010-01-08  2010-01-15  5

说明:我使用了三种公用表表达式。第一个(日期)收集了我们正在谈论的所有日期,从所涉及的两个表中。第二个(期间)从日期CTE中选择连续值。然后第三个(QuantitiesSold)在SoldItems表中找到与这些时段重叠的项目,并将它们的总数相加。保留在外部选择中的所有内容都是从数量表

中存储的总数量中减去这些数量

答案 2 :(得分:0)

约翰,你能做的是一个WHILE循环。在循环之前声明并初始化2个变量,一个是开始日期,另一个是结束日期。你的循环看起来像这样:

WHILE(@StartEnd <= @EndDate)
BEGIN
  --processing goes here
  SET @StartEnd = @StartEnd + 1
END

您需要将期间定义存储在另一个表中,以便在需要临时表时检索这些并输出行。

如果您需要更详细的示例,或者我的错误结束,请告诉我们!

答案 3 :(得分:0)

达明,

我正在尝试完全理解您的解决方案并对大量数据进行测试,但我收到了以下错误代码。

Msg 102,Level 15,State 1,Line 20

“日期”附近的语法不正确。

Msg 102,Level 15,State 1,Line 22

','附近的语法不正确。

Msg 102,Level 15,State 1,Line 25

','附近的语法不正确。

答案 4 :(得分:0)

达明,

根据您的解决方案,我还希望为StockItems提供一个整洁的显示,而不会重叠日期。这个解决方案怎么样?

CREATE TABLE [dbo].[SoldItems](
    [PeriodStart] [datetime] NOT NULL,
    [PeriodEnd] [datetime] NOT NULL,
    [SoldItems] [int] NOT NULL
) ON [PRIMARY]


INSERT INTO SoldItems (PeriodStart,PeriodEnd,SoldItems)
SELECT '20100105','20100106',2 union all
SELECT '20100105','20100108',3 union all
SELECT '20100115','20100116',1 union all
SELECT '20100101','20100120',10


;WITH Dates as (
    select PeriodStart as DateVal from SoldItems
    union 
    select PeriodEnd from SoldItems 
    union
    select PeriodStart from Quantity
    union
    select PeriodEnd from Quantity

), Periods as (
    select d1.DateVal as StartDate, d2.DateVal  as EndDate
    from Dates d1 
    inner join Dates d2 on d1.DateVal < d2.DateVal 
    left join Dates d3 on d1.DateVal < d3.DateVal and 
    d3.DateVal < d2.DateVal where d3.DateVal is null

), QuantitiesSold as (
    select StartDate,EndDate,SUM(si.SoldItems) as Quantity
    from Periods p left join SoldItems si on p.StartDate < si.PeriodEnd and si.PeriodStart < p.EndDate
    group by StartDate,EndDate
)

select StartDate,EndDate, qs.Quantity
from QuantitiesSold qs
where qs.quantity is not null