【问题标题】:Comparing date stored in date and varchar columns比较存储在 date 和 varchar 列中的日期
【发布时间】:2017-11-26 21:32:25
【问题描述】:

我有一个简单的表,我必须将日期信息存储在 DATE 和 VARCHAR2 列中:

CREATE TABLE date_test(date_in_varchar VARCHAR2(30), date_in_date DATE);

INSERT INTO date_test values ('06/21/2017 01:30:00 AM', to_date('06/21/2017 01:01:03 AM', 'MM/DD/YYYY HH:MI:SS AM'));

INSERT INTO date_test values ('06/22/2017 02:45:00 AM', to_date('06/22/2017 05:06:01 AM', 'MM/DD/YYYY HH:MI:SS AM'));

INSERT INTO date_test values ('06/23/2017 05:51:20 AM', to_date('06/23/2017 12:00:05 AM', 'MM/DD/YYYY HH:MI:SS AM'));

如您所见,数据格式为“MM/DD/YYYY HH:MI:SS AM”。我必须准备比较以变量 l_attr_val 格式传递的日期 'MM/DD/YYYY' 和存储在表中的日期。现在我的问题是:为过滤器编写查询最好和最安全的方法是什么:equals、like、greater、lower,可以肯定的是,我不会面临隐式转换?我假设,对于存储为日期的值和存储为 varchar 的值,该查询会有所不同。

CASE 1:日期存储在DATE 列中,它有关于day, month, year, hour, minutes, seconds. 的信息我还有一个变量(VARCHAR2 类型),它只有day, month, year 的信息。 (l_attr_val = '06/21/2017')。现在我想在这个变量和存储在表中的值之间创建一个相等过滤器,仅基于日期,没有时间(我得到 2017 年 6 月 21 日的变量,我想查看 2017 年 6 月 21 日的所有记录,无论时间)。

SELECT * FROM date_test WHERE date_in_date = TO_DATE('06/22/2017', 'MM/DD/YYYY');

由于时代原因,它给了我 0 行。

案例 2,与案例 1 不同: 案例 1:日期存储在 VARCHAR2 列中,它有关于 day, month, year, hour, minutes, seconds. 的信息我还有一个变量(VARCHAR2 类型),它的信息只有 day, month, year(l_attr_val = '06/21/2017')。现在我想在这个变量和存储在表中的值之间创建一个相等过滤器,仅基于日期,没有时间(我得到 2017 年 6 月 21 日的变量,我想查看 2017 年 6 月 21 日的所有记录,无论时间)。

SELECT * FROM date_test WHERE TO_DATE(date_in_varchar, 'MM/DD/YYYY HH:MI:SS AM' = TO_DATE('06/22/2017', 'MM/DD/YYYY');

那么,在上述情况下比较日期最安全的方法是什么?

【问题讨论】:

  • 为什么要处理这个问题?只需使用date 将日期存储在数据库中。当你返回值时,你可以使用to_char() 来设置你喜欢的格式。
  • 为什么必须将日期存储为字符串?只需将实际日期存储为正确的数据类型,并根据需要让客户端格式化为字符串。并且只使用实际日期进行过滤,就像您的 PL/SQL 块正在做的那样。 (如果真的有必要,您可以添加一个虚拟列 (11g+),将日期格式化为字符串,或在表格顶部添加一个视图,但仍不过滤该字符串值)。
  • @GordonLinoff 不幸的是,在我正在处理的项目中,某些日期存储在 varchar 列中,我必须处理它。是的,如果所有日期都存储在 DATE 列中会简单得多。这就是我问这个问题的原因。
  • I think it would work properly for date stored in date (if I am wrong please tell me why) - 如果 date_in_date 列中的所有日期都设置为午夜,您的查询将起作用;如果你可以在那里有实际时间,你需要检查一个范围,例如date_in_date >= TO_DATE(l_attr_val, 'MM/DD/YYYY') and date_in_date < TO_DATE(l_attr_val, 'MM/DD/YYYY') + 1
  • 如果 date 和 varchar 不匹配,你会使用哪个?我认为您需要编辑您的问题以解释您真正在做什么以及您需要更清楚地执行哪些规则。

标签: sql oracle date datetime plsql


【解决方案1】:

案例 1:正确存储的日期(日期类型)

您可以将 date_in_date 列转换为删除时间部分,但这不会利用此列上的非功能索引,因此我建议不要这样做。相反,如果06/22/2017 是您的VARCHAR2 类型变量,您也可以动态修改它以增加一个增加1 天的值。这将利用 date_in_date 函数上的普通索引,查询看起来像:

SELECT * FROM date_test
WHERE date_in_date >= TO_DATE('06/22/2017', 'MM/DD/YYYY')
  AND date_in_date < TO_DATE('06/23/2017', 'MM/DD/YYYY')

您还可以在第二部分之后添加 +1 并使用相同的变量来节省您自己对变量进行数学运算的时间。

案例 2:日期存储为 varchars

您需要将您的 date_in_varchar 转换为没有时间部分的日期(或使用它,然后部分应用为案例 1 建议的逻辑),并以相同的方式转换您的输入变量。如果您决定将其转换为没有时间部分的日期,那么下面的查询将利用功能索引(但不是普通索引):

SELECT * FROM date_test
WHERE TO_DATE(SUBSTR(date_in_varchar, 0, 12), 'MM/DD/YYYY') = TO_DATE('06/22/2017', 'MM/DD/YYYY')

如果您决定将其转换为带时间部分的日期,则查询类似于案例 1,但格式正确。如果您的查询要准确到一天,我建议您在此转换为日期时创建一个功能索引,并将 varchar 修剪为仅包含日期的字符。

【讨论】:

  • 在第一个查询中,第一个条件必须是&gt;=,否则它将错过任何恰好在午夜的值。在您的第二个查询中,您将获得带有示例数据的 ORA-01830,因此您确实需要提供整个格式,然后将其截断回午夜或使用相同的范围逻辑。
  • @AlexPoole 感谢您的意见。我对甲骨文的了解还很远。 :-) 我用不同的解决方案更新了我的答案。
【解决方案2】:

如果这些列上有索引,我会使用:

案例一:

select * from date_test 
  where date_in_date >= to_date('06/22/2017', 'MM/DD/YYYY')  
    and date_in_date <  to_date('06/22/2017', 'MM/DD/YYYY') + 1;

案例 2:

 select * from date_test where date_in_varchar like '06/22/2017%'

【讨论】:

    【解决方案3】:

    经过更多研究,我使用了以下方法:

    案例 1:正确存储的日期(日期类型)

    SELECT * FROM date_test 
    WHERE TRUNC(date_in_date)= to_date('06/22/2017', 'MM/DD/YYYY', 'nls_date_language = ENGLISH');
    

    案例 2:日期存储为 varchars

    SELECT * FROM date_test
    WHERE TRUNC(TO_DATE(date_in_varchar, 'MM/DD/YYYY HH:MI:SS AM')) = to_date('06/22/2017', 'MM/DD/YYYY', 'nls_date_language = ENGLISH');
    

    【讨论】:

      猜你喜欢
      • 2015-08-14
      • 2014-12-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多