我想知道我是否可以在我试图创建的T-SQL函数上获得一些帮助:
以下是一些需要查询的示例数据:
简化表格:
ID|PersonID|ValueTypeID|ValueTypeDescription|Value
1|ZZZZZ000L6|ZZZZZ00071|Start Prison Date|3/28/2012
2|ZZZZZ000L6|ZZZZZ00071|Start Prison Date|10/10/2012
3|ZZZZZ000L6|ZZZZZ00072|End Prison Date |3/29/2012
4|ZZZZZ000MD|ZZZZZ00071|Start Prison Date|1/15/2012
5|ZZZZZ000MD|ZZZZZ00072|End Prison Date |2/15/2012
6|ZZZZZ000MD|ZZZZZ00071|Start Prison Date|4/1/2012
7|ZZZZZ000MD|ZZZZZ00072|End Prison Date |4/5/2012
8|ZZZZZ000MD|ZZZZZ00071|Start Prison Date|9/3/2012
9|ZZZZZ000MD|ZZZZZ00072|End Prison Date |12/1/2012
我需要的是一个接受PersonID
和年(@PID, @YR
)的T-SQL函数,并返回该人在那一年入狱的天数。
dbo.NumDaysInPrison(@PID, @YR) as int
示例:
dbo.NumDaysInPrison('ZZZZZ000L6', 2012) returns 84
dbo.NumDaysInPrison('ZZZZZ000MD', 2012) returns 124
到目前为止,我已经提出了这个问题,有时会给我答案。
DECLARE @Year int
DECLARE @PersonID nvarchar(50)
SET @Year = 2012
SET @PersonID = 'ZZZZZ000AA'
;WITH StartDates AS
(
SELECT
Value,
ROW_NUMBER() OVER(ORDER BY Value) AS RowNumber
FROM Prisoners
WHERE ValueTypeDescription = 'Start Prison Date' AND PersonID = @PersonID AND YEAR(Value) = @Year
), EndDates AS
(
SELECT
Value,
ROW_NUMBER() OVER(ORDER BY Value) AS RowNumber
FROM Prisoners
WHERE ValueTypeDescription = 'End Prison Date' AND PersonID = @PersonID AND YEAR(Value) = @Year
)
SELECT
SUM(DATEDIFF(d, s.Value, ISNULL(e.Value, cast(str(@Year*10000+12*100+31) as date)))) AS NumDays
FROM StartDates s
LEFT OUTER JOIN EndDates e ON s.RowNumber = e.RowNumber
如果没有结束日期,那么今年早些时候的记录就无法捕获: 例如,如果一个人只有两个记录:
ID|PersonID|ValueTypeID|ValueTypeDescription|Value
1|ZZZZZ000AA|ZZZZZ00071|Start Prison Date|3/28/2012
2|ZZZZZ000AA|ZZZZZ00071|Start Prison Date|10/10/2012
(2012年3月28日 - >年终) (10/10/2012 - >年终)
将返回360,而不是278。
答案 0 :(得分:0)
因此,您似乎拥有分离“开始日期”值和“结束日期”值所需的数据。你真的不需要循环任何东西,你可以根据你的人提取你的起始值然后你的结束值并进行比较。
重要的是要提取您需要的所有内容,然后比较适当的值。
以下是基于您上述数据的示例。需要进行大量调整才能使用生产数据;它对Value
数据做出假设。像我这里一样硬编码valuetypeid也是一个坏主意;如果你正在做一个功能,你想要处理它,我想。
DECLARE @pid INT, @yr INT;
WITH startdatecalc AS
(
SELECT personid, CAST([value] AS date) AS startdate, DATEPART(YEAR, CAST([value] AS date)) AS startyear
FROM incarctbl
WHERE valuetypeid = 'ZZZZZ00071'
),
enddatecalc AS
(
SELECT personid, CAST([value] AS date) AS enddate, DATEPART(YEAR, CAST([value] AS date)) AS endyear
FROM incarctbl
WHERE valuetypeid = 'ZZZZZ00072'
)
SELECT CASE WHEN startyear < @yr THEN DATEDIFF(day, CAST(CAST(@yr AS VARCHAR(4)) + '-01-01' AS date), ISNULL(enddatecalc.enddate, CURRENT_TIMESTAMP))
ELSE DATEDIFF(DAY, startdate, ISNULL(enddatecalc.enddate, CURRENT_TIMESTAMP)) END AS NumDaysInPrison
FROM startdatecalc
LEFT JOIN enddatecalc
ON startdatecalc.personid = enddatecalc.personid
AND enddatecalc.enddate >= startdatecalc.startdate
AND NOT EXISTS
(SELECT 1 FROM enddatecalc xref
WHERE xref.personid = enddatecalc.personid
AND xref.enddate < enddatecalc.enddate
AND xref.enddate >= startdatecalc.startdate
AND xref.endyear < @yr)
WHERE startdatecalc.personid = @pid
AND startdatecalc.startyear <= @yr
AND (enddatecalc.personid IS NULL OR endyear >= @yr);
编辑:添加了存在检查以尝试处理同一年内多次使用相同的personid。
答案 1 :(得分:0)
这是一个复杂的问题。它不是“要求一个功能”,而是处理两个相互竞争的问题。第一种是将基于交易的数据组织成具有监狱期间的开始和停止日期的记录。第二个是在另一个给定的时间跨度(一年)内总结这个时间。
我认为在开始编写函数之前,您需要花些时间调查数据以了解其中的异常。以下查询可以帮助您。它计算给定年份(即第一次CTE中的年份)所有囚犯的计算:
with vals as (
select 2012 as yr
),
const as (
select cast(CAST(yr as varchar(255))+'-01-01' as DATE) as periodstart,
cast(CAST(yr as varchar(255))+'-12-31' as DATE) as periodend
from vals
)
select t.personId, SUM(datediff(d, (case when StartDate < const.periodStart then const.periodStart else StartDate end),
(case when EndDate > const.PeriodEnd or EndDate is NULL then const.periodEnd, else EndDate end)
)
) as daysInYear
from (select t.*, t.value as StartDate,
(select top 1 value
from t t2
where t.personId = t2.personId and t2.Value >= t.Value and t2.ValueTypeDescription = 'End Prison Date'
order by value desc
) as EndDate
from t
where valueTypeDescription = 'Start Prison Date'
) t cross join
const
where StartDate <= const.periodend and (EndDate >= const.periodstart or EndDate is NULL)
group by t.PersonId;
此查询可以作为一种功能进行调整。但是,我会鼓励你在去那里之前调查数据。一旦你把功能包装起来,找到并理解异常就会困难得多 - 为什么有人在同一天进出?监狱中最长的时期如何?等等。
答案 2 :(得分:0)
这是我使用测试表和数据的实现。你必须在适当的地方改变。 注意:我在监狱里服用日期为+1,所以如果你星期一进入并在星期二离开,那就算是两天了。如果您希望将其计为一天,请删除“+ 1”
create table PrisonRegistry
(
id int not null identity(1,1) primary key
, PersonId int not null
, ValueTypeId int not null
, Value date
)
-- ValueTypeIDs: 1 = start prison date, 2 = end prison date
insert PrisonRegistry( PersonId, ValueTypeId, Value ) values ( 1, 1, '2012-03-28' )
insert PrisonRegistry( PersonId, ValueTypeId, Value ) values ( 1, 1, '2012-10-12' )
insert PrisonRegistry( PersonId, ValueTypeId, Value ) values ( 1, 2, '2012-03-29' )
insert PrisonRegistry( PersonId, ValueTypeId, Value ) values ( 2, 1, '2012-01-15' )
insert PrisonRegistry( PersonId, ValueTypeId, Value ) values ( 2, 2, '2012-02-15' )
insert PrisonRegistry( PersonId, ValueTypeId, Value ) values ( 2, 1, '2012-04-01' )
insert PrisonRegistry( PersonId, ValueTypeId, Value ) values ( 2, 2, '2012-04-05' )
insert PrisonRegistry( PersonId, ValueTypeId, Value ) values ( 2, 1, '2012-09-03' )
insert PrisonRegistry( PersonId, ValueTypeId, Value ) values ( 2, 2, '2012-12-1' )
go
create function dbo.NumDaysInPrison(
@personId int
, @year int
)
returns int
as
begin
declare @retVal int
set @retVal = 0
declare @valueTypeId int
declare @value date
declare @startDate date
declare @noDates bit
set @noDates = 1
set @startDate = DATEFROMPARTS( @year, 1, 1 )
declare prisonCursor cursor for
select
pr.ValueTypeId
, pr.Value
from
PrisonRegistry pr
where
DATEPART( yyyy, pr.Value ) = @year
and pr.ValueTypeId in (1,2)
and PersonId = @personId
order by
pr.Value
open prisonCursor
fetch next from prisonCursor
into @valueTypeId, @value
while @@FETCH_STATUS = 0
begin
set @noDates = 0
-- if end date, add date diff to retVal
if 2 = @valueTypeId
begin
--if @startDate is null
--begin
-- -- error: two end dates in a row
-- -- handle
--end
set @retVal = @retVal + DATEDIFF( dd, @startDate, @value ) + 1
set @startDate = null
end
else if 1 = @valueTypeId
begin
set @startDate = @value
end
fetch next from prisonCursor
into @valueTypeId, @value
end
close prisonCursor
deallocate prisonCursor
if @startDate is not null and 0 = @noDates
begin
set @retVal = @retVal + DATEDIFF( dd, @startDate, DATEFROMPARTS( @year, 12, 31 ) ) + 1
end
return @retVal
end
go
select dbo.NumDaysInPrison( 1, 2012 )
select dbo.NumDaysInPrison( 2, 2012 )
select dbo.NumDaysInPrison( 2, 2011 )