使用 SqlDataAdapter 时获取 SQL 服务器 DATEDIFF 列作为 TimeSpan
Get SQL Server DATEDIFF column as a TimeSpan when using SqlDataAdapter
在使用 SqlDataAdapter
填充数据表时,是否可以将 SQL 服务器中 DATEDIFF 函数的结果作为 TimeSpan 获取?
作为一个非常简单的例子:
var table = new DataTable();
SqlCommand cmd = new SqlCommand(@"select DATEDIFF(mi, '2016-01-01', '2016-02-02') as [foo];", conn);
var da = new SqlDataAdapter(cmd);
da.Fill(table);
Console.WriteLine(table.Columns[0].DataType);
这会打印出 System.Int32
而不是 TimeSpan
,并且在 table 已经被 table.Columns[0].DataType = typeof(TimeSpan);
填充后我无法更改数据类型,因为它会抛出异常。
我可以创建一个全新的 DataTable 并将数据复制到其中,但我宁愿不这样做。
SQL 服务器没有自动映射到 .Net 的数据类型 TimeSpan
。您通常必须将跨度存储为 Int(或 BigInt),并在从适配器读取时将其转换为 TimeSpan
。
查看 this post 中的一些示例。
在select之前定义即可:
var table = new DataTable();
SqlCommand cmd = new SqlCommand(@"select DATEDIFF(mi, '2016-01-01', '2016-02-02') as [foo];", conn);
var da = new SqlDataAdapter(cmd);
table.Columns.Add("foo", typeof(TimeSpan));
da.Fill(table);
Console.WriteLine(table.Columns[0].DataType);
编辑
但要小心。您正在使用带有 mi 参数的 DATEDIFF。但是当您将 foo 映射到 TimeSpan 时,这意味着将创建时间跨度,并将此分钟数解释为 Ticks。
所以要纠正它你需要做这样的事情
select DATEDIFF(mcs, '2016-01-01', '2016-02-02')*10 as [foo]
因为刻度是 100 纳秒单位。
但在大多数情况下,它会导致 SqlException: The datediff function resulted in an overflow.
也许这会有所帮助。这是我的 AGE 函数的修改版本,其中 returns 年、月、日、小时、分钟和秒。
TimeSpan 函数已缩减为天、小时、分钟、秒和毫秒。
它可能看起来有点矫枉过正,但它非常准确,而且作为单语句 TVF,它非常快。
作为 TVF,您可以独立使用,也可以在 Join 中使用,甚至可以交叉应用
例如:
Select * from [dbo].[udf-Date-TimeSpan] ('2016-07-29','2016-07-30 02:03:12.345')
Returns
TimeSpan Days Hours Minutes Seconds Millisecond
1.02:03:12.348 1 2 3 12 348
需要的功能
ALTER FUNCTION [dbo].[udf-Date-TimeSpan] (@D1 DateTime,@D2 DateTime)
Returns Table
Return (
with cteBN(N) as (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cteRN(R) as (Select Row_Number() Over (Order By (Select NULL))-1 From cteBN a,cteBN b,cteBN c,cteBN d,cteBN e), -- Max 100K Days or 273 Years
cteDD(N,D) as (Select Max(R),Max(DateAdd(DD,R,@D1))From cteRN R Where DateAdd(DD,R,@D1)<=@D2),
cteHH(N,D) as (Select Max(R),Max(DateAdd(HH,R,D)) From (Select Top 24 R From cteRN Order By 1) R, cteDD P Where DateAdd(HH,R,D)<=@D2),
cteMI(N,D) as (Select Max(R),Max(DateAdd(MI,R,D)) From (Select Top 60 R From cteRN Order By 1) R, cteHH P Where DateAdd(MI,R,D)<=@D2),
cteSS(N,D) as (Select Max(R),Max(DateAdd(SS,R,D)) From (Select Top 60 R From cteRN Order By 1) R, cteMI P Where DateAdd(SS,R,D)<=@D2),
cteMS(N,D) as (Select Max(R),Max(DateAdd(MS,R,D)) From (Select Top 999 R From cteRN Order By 1) R, cteSS P Where DateAdd(MS,R,D)<=@D2)
Select TimeSpan = concat(cteDD.N,'.')+Format(cteHH.N,'00:')+Format(cteMI.N,'00:')+Format(cteSS.N,'00')+'.'+Format(cteMS.N-1,'000')
,[Days] = cteDD.N
,[Hours] = cteHH.N
,[Minutes] = cteMI.N
,[Seconds] = cteSS.N
,[Millisecond] = cteMS.N-1
From cteDD,cteHH,cteMI,cteSS,cteMS
)
--Select * from [dbo].[udf-Date-TimeSpan] ('2016-07-29','2016-07-30 02:03:12.345')
编辑 - 也许更好的插图
Declare @Table table (Date1 Datetime,Date2 DateTime)
Insert Into @Table values
('2016-01-01 00:00:00.200','2016-01-05 12:05:01.500'),
('2016-01-01 10:00:00.300','2016-01-05 12:30:30.500'),
('2016-01-01 10:00:00.800','2016-01-05 12:30:30.500')
Select A.*
,B.TimeSpan
From @Table A
Cross Apply [dbo].[udf-Date-TimeSpan] (A.Date1,A.Date2) B
Returns
Date1 Date2 TimeSpan
2016-01-01 00:00:00.200 2016-01-05 12:05:01.500 4.12:05:01.300
2016-01-01 10:00:00.300 2016-01-05 12:30:30.500 4.02:30:30.200
2016-01-01 10:00:00.800 2016-01-05 12:30:30.500 4.02:30:29.700
在使用 SqlDataAdapter
填充数据表时,是否可以将 SQL 服务器中 DATEDIFF 函数的结果作为 TimeSpan 获取?
作为一个非常简单的例子:
var table = new DataTable();
SqlCommand cmd = new SqlCommand(@"select DATEDIFF(mi, '2016-01-01', '2016-02-02') as [foo];", conn);
var da = new SqlDataAdapter(cmd);
da.Fill(table);
Console.WriteLine(table.Columns[0].DataType);
这会打印出 System.Int32
而不是 TimeSpan
,并且在 table 已经被 table.Columns[0].DataType = typeof(TimeSpan);
填充后我无法更改数据类型,因为它会抛出异常。
我可以创建一个全新的 DataTable 并将数据复制到其中,但我宁愿不这样做。
SQL 服务器没有自动映射到 .Net 的数据类型 TimeSpan
。您通常必须将跨度存储为 Int(或 BigInt),并在从适配器读取时将其转换为 TimeSpan
。
查看 this post 中的一些示例。
在select之前定义即可:
var table = new DataTable();
SqlCommand cmd = new SqlCommand(@"select DATEDIFF(mi, '2016-01-01', '2016-02-02') as [foo];", conn);
var da = new SqlDataAdapter(cmd);
table.Columns.Add("foo", typeof(TimeSpan));
da.Fill(table);
Console.WriteLine(table.Columns[0].DataType);
编辑
但要小心。您正在使用带有 mi 参数的 DATEDIFF。但是当您将 foo 映射到 TimeSpan 时,这意味着将创建时间跨度,并将此分钟数解释为 Ticks。
所以要纠正它你需要做这样的事情
select DATEDIFF(mcs, '2016-01-01', '2016-02-02')*10 as [foo]
因为刻度是 100 纳秒单位。
但在大多数情况下,它会导致 SqlException: The datediff function resulted in an overflow.
也许这会有所帮助。这是我的 AGE 函数的修改版本,其中 returns 年、月、日、小时、分钟和秒。
TimeSpan 函数已缩减为天、小时、分钟、秒和毫秒。
它可能看起来有点矫枉过正,但它非常准确,而且作为单语句 TVF,它非常快。
作为 TVF,您可以独立使用,也可以在 Join 中使用,甚至可以交叉应用
例如:
Select * from [dbo].[udf-Date-TimeSpan] ('2016-07-29','2016-07-30 02:03:12.345')
Returns
TimeSpan Days Hours Minutes Seconds Millisecond
1.02:03:12.348 1 2 3 12 348
需要的功能
ALTER FUNCTION [dbo].[udf-Date-TimeSpan] (@D1 DateTime,@D2 DateTime)
Returns Table
Return (
with cteBN(N) as (Select 1 From (Values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) N(N)),
cteRN(R) as (Select Row_Number() Over (Order By (Select NULL))-1 From cteBN a,cteBN b,cteBN c,cteBN d,cteBN e), -- Max 100K Days or 273 Years
cteDD(N,D) as (Select Max(R),Max(DateAdd(DD,R,@D1))From cteRN R Where DateAdd(DD,R,@D1)<=@D2),
cteHH(N,D) as (Select Max(R),Max(DateAdd(HH,R,D)) From (Select Top 24 R From cteRN Order By 1) R, cteDD P Where DateAdd(HH,R,D)<=@D2),
cteMI(N,D) as (Select Max(R),Max(DateAdd(MI,R,D)) From (Select Top 60 R From cteRN Order By 1) R, cteHH P Where DateAdd(MI,R,D)<=@D2),
cteSS(N,D) as (Select Max(R),Max(DateAdd(SS,R,D)) From (Select Top 60 R From cteRN Order By 1) R, cteMI P Where DateAdd(SS,R,D)<=@D2),
cteMS(N,D) as (Select Max(R),Max(DateAdd(MS,R,D)) From (Select Top 999 R From cteRN Order By 1) R, cteSS P Where DateAdd(MS,R,D)<=@D2)
Select TimeSpan = concat(cteDD.N,'.')+Format(cteHH.N,'00:')+Format(cteMI.N,'00:')+Format(cteSS.N,'00')+'.'+Format(cteMS.N-1,'000')
,[Days] = cteDD.N
,[Hours] = cteHH.N
,[Minutes] = cteMI.N
,[Seconds] = cteSS.N
,[Millisecond] = cteMS.N-1
From cteDD,cteHH,cteMI,cteSS,cteMS
)
--Select * from [dbo].[udf-Date-TimeSpan] ('2016-07-29','2016-07-30 02:03:12.345')
编辑 - 也许更好的插图
Declare @Table table (Date1 Datetime,Date2 DateTime)
Insert Into @Table values
('2016-01-01 00:00:00.200','2016-01-05 12:05:01.500'),
('2016-01-01 10:00:00.300','2016-01-05 12:30:30.500'),
('2016-01-01 10:00:00.800','2016-01-05 12:30:30.500')
Select A.*
,B.TimeSpan
From @Table A
Cross Apply [dbo].[udf-Date-TimeSpan] (A.Date1,A.Date2) B
Returns
Date1 Date2 TimeSpan
2016-01-01 00:00:00.200 2016-01-05 12:05:01.500 4.12:05:01.300
2016-01-01 10:00:00.300 2016-01-05 12:30:30.500 4.02:30:30.200
2016-01-01 10:00:00.800 2016-01-05 12:30:30.500 4.02:30:29.700