'Summing' SQL 中的日期字段 - 有什么想法吗?

'Summing' a date field in SQL - any ideas?

我正在创建一个应用程序,它本质上是两个数据库之间的完整性检查 - 一个是 MSSQL,一个是旧的提供程序 Btrieve。作为要求的一部分,每个 table 的所有列都需要进行比较以确保数据匹配。目前我们遍历每个 table,获取两个数据库中 table 的基本计数,然后深入研究列。对于数字字段,我们执行简单的 SUM,对于文本字段,我们对每一行的列长度求和。如果这些在两个数据库中都匹配,则表明数据已正确迁移。

一切正常,但我需要为日期时间字段开发类似的东西。显然我们不能真正对这些字段求和,所以我想知道是否有人对解决这个问题的最佳方法有想法。我在想也许自某个日期以来的秒数,但这个数字会很大。

还有其他想法吗?谢谢!

我认为您可以在 SQL 服务器端执行类似的操作来找到列的中心 ("average") 值。然后在 Btrieve 端使用该值以避免溢出问题,我猜你会受到更多限制。

-- January 1, 2000 value pulled out of the air as a stab in the dark
select
    dataadd(
        second,
        avg(cast(datediff(datediff(second, '20000101', <data>) as bigint)),
        '20000101'
    ) /* find the center */

如果您不得不借助 Btrieve 求助于浮点类型或将扫描划分为较小的范围以避免中间和变得太大,我不会感到惊讶。您可能希望使用游标并随机化行的顺序,这样您就不会按导致溢出的排序顺序命中它们。在这一点上,我只是推测,因为我没有看到任何数据,而且我对 Btrieve 的了解是如此古老和微不足道。

我也有一种感觉,其中一些努力是为了满足 non-technical 利益相关者的一些不安。我相信您可以想出更好的校验和和哈希值,但这个求和概念是他们可以掌握的,并且除了更容易快速实施之外,他们也会安心。

在数字字段的行上使用 SQL 中的 "Except",您可以比较两个表中的日期计数。对于旧源,您可以使用 excel 或在本机数据库中生成 select 语句并将其带到 SQL 服务器。出于演示目的,我使用了两个表并在下面显示了 Except 示例。

IF  EXISTS (SELECT * FROM sys.objects
           WHERE OBJECT_ID = OBJECT_ID(N'[dbo].[DateCompareOld]') AND
           TYPE IN (N'U'))
DROP TABLE [dbo].[DateCompareOld]
GO

CREATE TABLE dbo.DateCompareOld 
(
AsOf DATETIME 
)

INSERT INTO DateCompareOld
SELECT '01/01/2016' UNION ALL 
SELECT '01/01/2016' UNION ALL 
SELECT '01/01/2016' UNION ALL 
SELECT '01/02/2016' UNION ALL 
SELECT '01/02/2016' UNION ALL 
SELECT '01/02/2016' 


IF  EXISTS (SELECT * FROM sys.objects WHERE OBJECT_ID = OBJECT_ID(N'[dbo].[DateCompareNew]') AND TYPE IN (N'U'))
DROP TABLE [dbo].[DateCompareNew]
GO

CREATE TABLE dbo.DateCompareNew 
(
AsOf DATETIME 
)

INSERT INTO DateCompareNew
SELECT '01/01/2016' UNION ALL 
SELECT '01/01/2016' UNION ALL 
SELECT '01/01/2016' UNION ALL 
SELECT '01/02/2016' UNION ALL 
SELECT '01/02/2016' UNION ALL 
SELECT '01/02/2016' 

SELECT AsOf,COUNT(*) AsOfCount
FROM DateCompareOld
GROUP BY AsOf 
Except  
SELECT AsOf,COUNT(*) AsOfCount
FROM DateCompareNew
GROUP BY AsOf 

除非数据库中的行使用的日期范围是极端的(如天文恒星诞生和死亡的日期),否则将日期转换为整数应该同样有效。这可以通过多种方式中的任何一种来完成,并且略微 database-specific,但是将 2016-01-04 转换为 20,160,104 将可以正常工作。

甚至 SQL 服务器允许 ORD(date_field) 类表达式获取内部表示。但这也可以在便携式设备中完成,system-agnostic 表示类似

 datediff(day, 'January 1, 1901', date_field)

如果跟踪天数就足够了,或者

 datediff(second, 'January 1, 1901', date_field)

如果需要跟踪秒数。

对我来说最直接的答案是将日期或日期时间字段转换为具有相同格式的整数。只要您的格式使用前导零,YYYYMMDD 或 YYYYMMDDHHmmss 就可以正常工作。在 SQL 服务器中,您可以执行以下操作:

SELECT SUM(CAST(REPLACE(REPLACE(REPLACE(CONVERT(VARCHAR(20),DateTimeColumn,120),' ',''),':',''),'-','') AS BIGINT)) .....

或者,您可以将它们转换为从给定日期开始的天数 ('1970-01-01'),或者如果您使用时间,则可以将它们转换为从给定日期开始的秒数 ('1970-01-01 00:00:00')。

SELECT SUM(DATEDIFF(DAY,'19700101',DateColumn)) ....

不过,我对 Btrieve 不够熟悉,不知道有哪些函数可用于格式化日期。

也许帮助不大,也许有帮助:

declare @d1 datetime; set @d1 = '2016-01-05 12:09'
declare @d2 datetime; set @d2 = '1970-04-05 07:09'
declare @d3 datetime; set @d3 = '1999-12-12 23:05'
declare @d4 datetime; set @d4 = '1999-12-12 23:06'

declare @i1 bigint
declare @i2 bigint
declare @i3 bigint
declare @i4 bigint

select @i1 = convert( bigint, convert( timestamp, @d1 ) ) 
select @i2 = convert( bigint, convert( timestamp, @d2 ) )
select @i3 = convert( bigint, convert( timestamp, @d3 ) )
select @i4 = convert( bigint, convert( timestamp, @d4 ) )

select @i1
select @i2
select @i3
select @i4

select @i1 ^ @i2 ^ @i3 ^ @i4