Tsql 不使用光标处理句点和日期
我想解决这个问题,避免使用游标获取 问题来了Tsql 不使用光标处理句点和日期,tsql,date,fetch,cursors,period,Tsql,Date,Fetch,Cursors,Period,我想解决这个问题,避免使用游标获取 问题来了 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 201
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
如果不使用游标,或者不使用单一日期而不是句点,似乎不可能解决这个问题
我将感谢任何帮助
谢谢约翰,你能做的就是做一个WHILE循环。在循环之前声明并初始化两个变量,一个是开始日期,另一个是结束日期。然后,您的循环将如下所示:
WHILE(@StartEnd <= @EndDate)
BEGIN
--processing goes here
SET @StartEnd = @StartEnd + 1
END
您需要将期间定义存储在另一个表中,以便在需要时检索这些定义并将行输出到临时表中
如果你需要任何更详细的例子,或者如果我搞错了,请告诉我 希望比马丁的书读起来容易一点。我使用了不同的表格和样本数据,希望能推断出正确的信息:
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
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
说明:我使用了三种常见的表表达式。第一个日期是收集我们正在讨论的所有日期,从涉及的两个表中。第二个周期从日期CTE中选择连续值。然后,第三个QuantitiesSald在SoldItems表中查找与这些期间重叠的项目,并将它们的总数相加。外部选择中剩下的就是从数量表中存储的总量中减去这些数量
我试图完全理解您的解决方案,并在大量数据上对其进行测试,但您的代码出现以下错误
Msg 102,15级,状态1,第20行
“日期”附近的语法不正确
Msg 102,15级,状态1,第22行
“,”附近的语法不正确
Msg 102,15级,状态1,第25行
“,”附近的语法不正确。Damien
基于你的解决方案,我还想得到一个整洁的显示库存项目没有重叠的日期。这个解决方案怎么样
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
如果您使用的是SQL Server,请参阅这篇MSDN文章,了解T-SQL中WHILE循环的详细用法+1,在我将所有日期数据类型更改为DATETIME后对我有效,因为我不在SQL Server 2008上是的-目前我们缺少的是,因为示例数据不是很广泛,是数量表中是否有多行,只需更改为第三个CTE即可,以及这两个表之间是否存在未显示的实际关系,例如,存在多个产品tracked@Martin-我的周期不完全符合运营要求,因为我的周期完全覆盖了范围,即我的第一个周期在5号结束,而不是4号结束。那是因为我习惯于将周期建模为[StartDate,EndDate a半开放式间隔您最好将此作为注释添加到我的答案中,或者将其编辑到您的问题中。对我的答案的注释会提醒我您的更新。仅适用于SQL 2005或更高版本。不确定您使用的是哪个版本,如果查询在批处理中稍后启动,则在with关键字之前需要;I a我使用SQL 2005,并且在with kwd之前已经尝试过;但总是会出现相同的错误。因为我不经常使用with,所以我尝试运行with语句的一些基本示例,但它们都返回相同的错误。??您的解决方案在Quantity表中使用多行时效果很好。但是,日期重叠的问题似乎是c无法使用您的解决方案解决此问题。另一方面,进行必要的更改以将EndDate从第5个调整到第4个,这将导致SoldItems中的单个日期周期被表示为例如2010-05-05-2010-05-05。因此,我相信这对于数据层来说是一个很好的解决方案。
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