在 Oracle SQL 中将用户提示作为日期(甚至字符串)传递

Passing a user prompt as a date (or even a string) in Oracle SQL

我正在使用 Business Objects,它 运行 在 Oracle SQL 数据库之上。我无权访问 PL 或任何类型的 SQL 命令行,也无权写入数据库。我只能 运行 作为单个命令进行查询,需要输出一组定义的列。

我能够从用户提示中获取数据,这些提示在 SQL 中显示为:

@variable('Prompt1')

例如我可以说:

SELECT
    A.SomeDate
FROM
    A
WHERE
    A.SomeDate BETWEEN @variable('Start') AND @variable('End Date')

这很简单。它运行s;要求用户输入一些日期;然后 returns 它们之间的所有匹配项(包括)。

但是,问题是用户将使用 Business Objects 的 "Infoview" 系统来 运行 查询,并且提示系统会显示日期选择器 - 默认情况下包括时间部分日期(“01/01/2016 12:00:00 AM”)。

如果用户不删除时间部分,如果 SomeDate 值落在所选时间之外,可能会导致记录丢失。例如,如果我想获取今天的所有记录,那么从技术上讲,我想要 00:00:00(午夜)和 23:59:59.

之间的所有内容

我真正希望能够做的是围绕查询变量使用 TRUNC,如下所示:

WHERE
    A.SomeDate BETWEEN TRUNC(@variable('Start')) AND TRUNC(@variable('End Date'))

... 然而这会导致编译错误:"inconsistent datatypes: expected DATE got NUMBER"。我不知道为什么 Oracle 在编译之前会将提示视为数字数据类型。

有谁知道我如何获取@variable 值并将其转换成我能够运行转换为日期值的值?

因此,我正在想办法解决这个问题。我想到的一件事是,是否可以使用 TO_DATE

获取提示变量并将其显式转换为日期

编辑:有人向我指出 TRUNC 不会有任何效果,因为“12:00:00 AM”已经是午夜了。因此我认为我误解了TRUNC。它似乎 t运行 将其归类到午夜:而我认为它只是完全删除了日期的时间部分,这意味着匹配将在 00:00:00 和 23:59:59 之间的任何时间返回.

我真正想要的是:如果 SomeDate 有一个时间部分,例如 11:03 那么我如何确保在结束日期提示仅指定日期时将其包括在内?

尝试同时使用 TO_CHAR()TO_DATE() :

WHERE
A.SomeDate > TO_DATE(TO_CHAR(@variable('Prompt1'),'ddmmyyyy'),'ddmmyyyy')

如果您想要匹配开始时 00:00:00 和结束时 23:59:59 之间的 SomeDate 值,您可以调整结束日期以使用该时间而不是默认的午夜,或者使用范围而不是 between:

WHERE
    A.SomeDate >= @variable('Start')
AND
    A.SomeDate < @variable('End Date') + 1

+ 1 使用 Oracle date arithmetic 为您提供变量值之后的日期,因此如果用户选择“01/01/2016 12:00:00 AM”作为开始和他们将分别评估为 2016-01-01 00:00:00 和 2016-01-02 00:00:00 的结束日期。如果愿意,可以使用 interval 语法。

通过使用 less-than 作为上限,您将获得 SomeDate 大于或等于 开始日期 2016-01-01 [=] 的所有记录38=] 和 小于 调整后的结束日期 2016-01-02 00:00:00 - 这与截至 2016-01-01 23:59:59 的说法相同。 (或者,如果您有一个具有亚秒级精度的时间戳列,最高可达 23:59:59.999...)。

如果解析器假设该变量是一个字符串但它实际上是一个日期 - 导致 'inconsistent datatypes' 错误 - 那么你可以将它转换为一个日期以满足解析器:

WHERE
    A.SomeDate >= CAST(@variable('Start') AS DATE)
AND
    A.SomeDate < CAST(@variable('End Date') AS DATE) + 1

或者如果它实际上以您显示的格式作为字符串传递,您可以显式转换它:

WHERE
    A.SomeDate >= TO_DATE(@variable('Start'), 'DD/MM/YYYY HH:MI:SS AM')
AND
    A.SomeDate < TO_DATE(@variable('End Date'), 'DD/MM/YYYY HH:MI:SS AM') + 1

...确保您的格式正确;根据您的示例,它可能是 DD/MM/YYYY 或 MM/DD/YYYY.

首先,你的问题不是来自提示值中的时间值,而是SomeDate中的时间值。摆脱它(使日期等于午夜)将解决问题。

如果您可以选择修改 Universe,那么您最好的选择是创建另一个对象。我假设您有一个名为 SomeDate 的对象,其 SQL 是 a.somedate。创建另一个对象,我们将其命名为 SomeDateOnly,定义为 trunc(a.somedate)* **.

由于 SomeDateOnly 始终是午夜值,您可以在提示中使用等于,这将产生 SQL,例如:

trunc(a.somedate) = @variable('Prompt1')

当由 WebI 呈现时,将产生:

trunc(a.somedate) = '16-08-2016 00:00:00'

这将 return 在 2016 年 8 月 16 日 00:00:00 和 2016 年 8 月 16 日 23:59:59 之间 a.somedate 的所有记录 23:59:59。

当然,您可以使用 BETWEEN 来 select 日期范围:

 trunc(a.somedate) between @variable('Start Date') and @variable('End Date')

即使你没有权限访问宇宙,你仍然可以通过修改WebI生成的SQL来使用上面的语法。 (无论如何,我假设这就是您一直在做的事情)。

如果以上内容适合您,那么以下内容无关紧要,但我还是想解决它:

您收到 "invalid number" 错误的原因是 WebI 格式化 SQL 日期的方式。如果您的查询中有此字符串:

A.SomeDate = TRUNC(@variable('Prompt1'))

然后 WebI 会将 @variable(...) 替换为日期 string,并在将其发送给 Oracle 之前将其呈现为以下内容:

A.SomeDate = TRUNC('16-08-2016 00:00:00')

当然,这对 TRUNC() 函数没有任何意义,因为没有什么可以告诉它它实际上是一个日期值。

可以 to_date先提示,但你必须使用正确的日期格式。 WebI 将每个会话的 nls_date_format 设置为非默认格式,因此您必须使用:

A.SomeDate = TRUNC(to_date(@variable('Prompt1')),'dd-mm-yyyy hh24:mi:ss')

但同样,这无关紧要,因为您需要截断 somedate,而不是提示响应值。


*更好的是,将 SomeDate 重命名为 SomeDateTime,并将新对象命名为 SomeDate

**这很常见 - 同一个源字段有多个对象。有时您需要 date/time 值(用于列出特定交易),但有时您只需要日期(用于按日期计算交易)。所以两者都可用是非常有用的。