SQL Server 和 Oracle 之间的差异_函数输出之间

问题描述 投票:0回答:1

我正在开发一个项目,需要计算 SQL Server(使用 Synapse)和 Oracle 中两个日期之间的月数。我在 SQL Server 中创建了一个用户定义的函数来复制 Oracle 的months_ Between 函数,但我在输出中遇到了轻微的差异,这些差异随着时间的推移而累积,并通过扩展在计算指标时产生巨大的差异。

我的尝试:

CREATE FUNCTION [TFM].[UDF_MONTHS_BETWEEN] (
    @start_date_p [DATE]
    ,@end_date_p [DATE]
    )
RETURNS FLOAT
AS
BEGIN
    DECLARE @count_v FLOAT;
    DECLARE @daysinmonth_v FLOAT;
    DECLARE @dayofmonth_v FLOAT;

    -- Count whole months 
    SET @count_v = DATEDIFF(MONTH, DATEADD(MONTH, 1, DATEFROMPARTS(YEAR(@start_date_p), MONTH(@start_date_p), 1)), DATEFROMPARTS(YEAR(@end_date_p), MONTH(@end_date_p), 1));
    -- Days in start month 
    SET @daysinmonth_v = DAY(EOMONTH(@start_date_p));
    -- Get fractional start month 
    SET @dayofmonth_v = DAY(@start_date_p);
    SET @count_v = @count_v + ((@daysinmonth_v - (@dayofmonth_v - 1)) * (1.0 / @daysinmonth_v));
    -- Days in end month 
    SET @daysinmonth_v = DAY(EOMONTH(@end_date_p));
    -- Get fractional end month 
    SET @dayofmonth_v = DAY(@end_date_p);
    SET @count_v = @count_v + ((@dayofmonth_v - 1) * (1.0 / @daysinmonth_v));

    RETURN ABS(@count_v);
END

测试用例 输入日期 Sql 服务器输出 Oracle 输出
测试1 '2024年6月28日', '2024年7月27日' 0.938 0.9677419354838709677419354838709677419355
测试2 “2024 年 4 月 28 日”、“2024 年 7 月 27 日” 2.9387 2.96774193548387096774193548387096774194

我需要它们尽可能接近。

要测试的代码:

sql server : SELECT TFM.UDF_MONTHS_BETWEEN('4/28/2024','7/27/2024') 
oracle : SELECT ABS(MONTHS_BETWEEN('28-APR-24','27-JUL-24')) FROM DUAL;
oracle azure-synapse
1个回答
0
投票

正如 @AlexPoole 所指出的,MONTHS_BETWEEN

 的 Oracle 
文档指出:

MONTHS_BETWEEN

 返回日期 
date1
date2
 之间的月数。月份和该月的最后一天由参数 
NLS_CALENDAR
 定义。如果 
date1
 晚于 
date2
,则结果为正。如果 
date1
 早于 
date2
,则结果为负。如果 
date1
date2
 是该月的同一天或都是该月的最后一天,则结果始终是整数。否则,Oracle 数据库将基于 31 天的月份计算结果的小数部分,并考虑时间分量 
date1
date2
 的差异。

因此,您需要将所有月份的天数设置为 31(而不是该月的实际天数),并删除

ABS

,因为当 
date1
date2
 之后时,结果应该为负数:

CREATE FUNCTION [TFM].[UDF_MONTHS_BETWEEN] ( @start_date_p [DATE] ,@end_date_p [DATE] ) RETURNS FLOAT AS BEGIN DECLARE @count_v FLOAT; DECLARE @daysinmonth_v FLOAT; DECLARE @dayofmonth_v FLOAT; SET @daysinmonth_v = 31; -- Count whole months SET @count_v = DATEDIFF(MONTH, DATEADD(MONTH, 1, DATEFROMPARTS(YEAR(@start_date_p), MONTH(@start_date_p), 1)), DATEFROMPARTS(YEAR(@end_date_p), MONTH(@end_date_p), 1)); -- Days in start month -- Get fractional start month SET @dayofmonth_v = DAY(@start_date_p); SET @count_v = @count_v + ((@daysinmonth_v - (@dayofmonth_v - 1)) * (1.0 / @daysinmonth_v)); -- Days in end month -- Get fractional end month SET @dayofmonth_v = DAY(@end_date_p); SET @count_v = @count_v + ((@dayofmonth_v - 1) * (1.0 / @daysinmonth_v)); RETURN @count_v; END
对于样本数据:

CREATE TABLE tests ( test_case VARCHAR(20), start_date DATE, end_date DATE, oracle_output NUMERIC(30,26) ); INSERT INTO tests (test_case, start_date, end_date, oracle_output) VALUES ( 'Test 1', '2024-06-28', '2024-07-27', 0.967741935483870967741935 ), ( 'Test 2', '2024-04-28', '2024-07-27', 2.967741935483870967741935 ), ( 'Test 3', '2024-07-27', '2024-04-28', -2.967741935483870967741935 ), ( 'Test 4', '2024-07-31', '2024-04-30', -3 );
然后:

SELECT test_case, start_date, end_date, [TFM].[UDF_MONTHS_BETWEEN](start_date, end_date) AS sql_server_output, oracle_output FROM tests;
输出:

测试用例开始日期结束日期sql_服务器_输出oracle_输出测试12024-06-282024-07-270.9677419354838710.96774193548387096774193500测试22024-04-282024-07-272.967741935483872.96774193548387096774193500测试32024-07-272024-04-28-2.96774193548387-2.96774193548387096774193500测试42024-07-312024-04-30-3.03225806451613-3.00000000000000000000000000

注意:测试 4 显示当开始日期和结束日期都是该月的最后一天时 Oracle 的行为,然后 Oracle MONTHS_BETWEEN

 将始终返回整数值。 OP 的代码中没有捕获此行为,并且实现它作为读者的练习。其他情况下的行为应该匹配。

小提琴

© www.soinside.com 2019 - 2024. All rights reserved.