【问题标题】:How do I specify date literal when writing SQL query from SQL Server that is linked to Oracle?从链接到 Oracle 的 SQL Server 编写 SQL 查询时,如何指定日期文字?
【发布时间】:2016-01-18 15:31:49
【问题描述】:

我有一个链接到 Oracle 12.1 数据库的 SQL Server 12.0 数据库。

我想在 SQL Server 数据库中创建一个视图,该视图返回按日期过滤的 Oracle 表中的数据。 Oracle 表在日期列上有一个索引。

一个成功的查询是:

select * from ORADB..SCHEMA.MYTABLE where MYDATE >= '20140701';

但是这运行非常缓慢。我认为这是因为比较是在 SQL Server 中进行的,所以每行都被返回。

如果我去:

DECLARE @earliest date = '20140701';
select * from ORADB..SCHEMA.MYTABLE where MYDATE >= @earliest;

然后它跑得很快,大概是因为条件被传递给Oracle所以正在使用表上的Oracle索引。

我的问题是我想创建一个视图。我找不到使用第二版代码创建视图的方法。如果我只是这样做:

create myview as select * from ORADB..SCHEMA.MYTABLE where MYDATE >= '20140701';

然后它运行缓慢。

SQL Server 将传递给 Oracle 的日期文字是否有另一种格式,或者是否有其他解决方案?我还想知道这是否与创建 Oracle 链接时使用的参数有关。供参考:

USE [master]
GO
EXEC master.dbo.sp_addlinkedserver @server = N'ORADB', @srvproduct=N'Oracle', @provider=N'OraOLEDB.Oracle', @datasrc=N'DPDB'
EXEC master.dbo.sp_addlinkedsrvlogin @rmtsrvname=N'ORADB',@useself=N'False',@locallogin=NULL,@rmtuser=N'MYUSER',@rmtpassword='#######'
GO
EXEC master.dbo.sp_serveroption @server=N'ORADB', @optname=N'collation compatible', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'ORADB', @optname=N'data access', @optvalue=N'true'
GO
EXEC master.dbo.sp_serveroption @server=N'ORADB', @optname=N'dist', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'ORADB', @optname=N'pub', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'ORADB', @optname=N'rpc', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'ORADB', @optname=N'rpc out', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'ORADB', @optname=N'sub', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'ORADB', @optname=N'connect timeout', @optvalue=N'0'
GO
EXEC master.dbo.sp_serveroption @server=N'ORADB', @optname=N'collation name', @optvalue=null
GO
EXEC master.dbo.sp_serveroption @server=N'ORADB', @optname=N'lazy schema validation', @optvalue=N'false'
GO
EXEC master.dbo.sp_serveroption @server=N'ORADB', @optname=N'query timeout', @optvalue=N'0'
GO
EXEC master.dbo.sp_serveroption @server=N'ORADB', @optname=N'use remote collation', @optvalue=N'true'
GO
EXEC master.dbo.sp_serveroption @server=N'ORADB', @optname=N'remote proc transaction promotion', @optvalue=N'true'
GO

编辑:我刚刚发现了一个非常相似的问题:Forcing a SQL Remote Query to filter remotely instead of locally

【问题讨论】:

  • 试试ISO格式'2014-01-01'YYYY-MM-DD
  • 谢谢@lad2025,我试过了,但还是很慢。
  • 既然不能创建带参数的视图,为什么不直接创建一个函数呢?你可以像视图一样使用它,加入它,并拥有参数。
  • 感谢@iDevlop,我试图让事情变得简单。这可能是我的下一个方法。另一种是将视图过滤移回Oracle,但我试图避免这种情况。
  • 您遇到的问题与日期文字的格式无关...您必须找到将日期作为参数传递的解决方案。

标签: sql sql-server oracle date-formatting


【解决方案1】:

如果查询在 Oracle 数据库上运行,那么我建议使用 ANSI 日期文字,它使用固定格式 YYYY-MM-DD

例如,

DATE '2015-10-20'

在 Oracle 中,'20140701'字符串 而不是 DATE。您可能很幸运地看到了隐式数据类型转换并根据客户的区域特定 NLS 设置获得结果。您应该始终避免使用它,并显式将字符串转换为日期以进行日期比较。

【讨论】:

  • 谢谢 Lalit,我知道 '20140701' 是 Oracle 中的一个字符串。我不能使用语法 DATE '2015-10-20' 因为 SQL Server 不理解它。如果我直接在 Oracle 中编写查询,这就是我要使用的。我尝试了多种日期格式。我在网上找到的关于 SQL Server 中日期文字的所有建议都建议使用 20140701 而不是 2014-07-01。
  • 但是(这很奇怪...)可以将其转换为DATE,但是如果您将其转换为DATETIME,则它与文化无关。 . 试试SET LANGUAGE GERMAN; SELECT CAST('2015-10-20' AS DATE); 然后把它改成DATETIME
【解决方案2】:

我建议使用@lad2025 建议的完整 ISO 8601 格式:

'2017-10-06T14:57:23'

这优于@Shnugo 建议的 ODBC 格式。

至少在 SQL Server 2014 中,ODBC 格式不适用于 1753-01-01 之前的日期(例如,旧 DATETIME 数据类型范围之外的日期),而 ISO 8601 格式则适用。

要自行测试,请尝试以下查询:

--This will work
DECLARE @DateISO DATE = '0001-01-01T00:00:00';
SELECT @DateISO;

--This will also work
DECLARE @DatetimeISO DATETIME2 = '0001-01-01T00:00:00';
SELECT @DatetimeISO;

--This will not work
DECLARE @DateODBC DATE = {D '0001-01-01'};
SELECT @DateODBC;

--This will also not work
DECLARE @DatetimeODBC DATETIME2 = {ts '0001-01-01 00:00:00'};
SELECT @DatetimeODBC;

即使您认为您正在使用的日期不会早于 1753 年,这也是一个好习惯。我在查看设置日历表以在查询中引用时遇到了这个问题。

【讨论】:

    【解决方案3】:

    我更喜欢 ODBC 格式:

    --DateTime
    SELECT {ts'2015-09-20 12:30:00'}
    --Time (however this comes with "today"-time)
    SELECT {t'12:30:00'}
    --Date
    SELECT {d'2015-09-20'}
    GO
    

    简单的日期文字与文化无关...

    SET LANGUAGE ENGLISH;
    SELECT CAST('2014-09-13' AS DATETIME);
    GO
    SET LANGUAGE GERMAN;
    SELECT CAST('2014-09-13' AS DATETIME);--ERROR: there's no month "13"
    GO
    

    但它适用于 - 然而 - 与目标类型 DATE (这种差异相当奇怪......):

    SET LANGUAGE ENGLISH;
    SELECT CAST('2014-09-13' AS DATE);
    GO
    SET LANGUAGE GERMAN;
    SELECT CAST('2014-09-13' AS DATE);--ERROR: there's no month "13"
    GO
    

    感谢 lad2025,为了完整性,我想添加“完整”的 ISO 8601,它可以正常工作:

    SET LANGUAGE ENGLISH;
    SELECT CAST('2014-09-13T12:30:00' AS DATETIME);
    GO
    SET LANGUAGE GERMAN;
    SELECT CAST('2014-09-13T12:30:00' AS DATETIME);
    GO
    

    【讨论】:

    • 谢谢Shnugo。我试过了,但它并没有解决我的问题,仍然很慢。
    • @Shungo 也许我对 ISO 格式 ISO 8601 Format The advantage in using the ISO 8601 format is that it is an international standard. datetime values that are specified by using this format are unambiguousAlso, this format is not affected by the SET DATEFORMAT or SET LANGUAGE settings. 不够具体,但你必须使用带时间部分的完整规范。跨度>
    • @lad2025。您对隐式转换很正确,但只需尝试第二个代码块。演员抛出异常,因为“13”被视为月份......将“13”更改为较小的数字,演员将返回两个不同的日期......
    • 我不够具体。检查完整的日期时间demo'2014-09-13T00:00:00' 与文化无关。没有错误。
    • @lad2025,好的,加上“THH:MM:SS”部分,它工作正常。但是您的评论仅指出“YYYY-MM-DD”部分......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-23
    • 2013-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多