不是有效月份

时间:2016-10-20 15:15:20

标签: sql oracle

我无法弄清楚为什么我收到一个无效月份错误。我可以使用to_date返回每个字段而没有错误但是当我添加之间的过滤器时它会爆炸。我正在使用Oracle。奇怪的是并不是每一个记录炸弹都是

SELECT *
  FROM timedetail_dates
 WHERE empnum = '501331134'
   AND (TO_DATE ('10/14/2016 04:00', 'mm/dd/yyyy hh24:mi')
           BETWEEN TO_DATE (timein_date, 'mm/dd/yyyy hh24:mi')
               AND TO_DATE (timeout_date, 'mm/dd/yyyy hh24:mi')
       )
   AND tsdate BETWEEN '09-oct-2016' AND '22-oct-2016'
   AND timedetail_dates.DURATION > 0;

2 个答案:

答案 0 :(得分:1)

在TO_DATE的BETWEEN条款中包含日期,以及

Stud Name
AA
BB
CC
DD
EE
FF
GG
HH
II
JJ
KK
LL
MM
NN
OO
PP
QQ
RR
SS
TT
UU
VV

此外,如果您的tsdate列不是日期类型,您还需要将其包装在您将数据存储在列中的格式中。

答案 1 :(得分:0)

Cause of the error:

It sounds like you have an invalid date in your table. You receive this error when the string being parsed is incorrect. Here's an example query that produces it.

SELECT TO_DATE('22-10-2016 05:31', 'mm/dd/yyyy hh24:mi') FROM DUAL;

Prevent this from happening again:

As a_horse_with_no_name implies, using VARCHAR (or VARCHAR2 or any text type) is usually a poor data type for temporal data. This is part of the reason why: it's difficult to prevent invalid data from getting into the database.

You need to prevent bad data from getting into the database to start with:

  1. The ideal solution is to convert these columns to an actual temporal type (after you repair the existing data), such as TIMESTAMP (possibly WITH TIME ZONE) or DATE.
  2. If that isn't possible, then add a constraint or trigger that will throw an error on the wrong format. Note that you need to figure out what the "right" format is. Part of the problem here might be that you have non-American users (or maybe computer savvy people who like ISO 8601), and applications will need to either enforce a particular format or automatically parse to the standard you decide on
  3. If you really can't prevent different formats from coming in and can't convert to an actual date, then you could store an extra column with the format. You would still need to validate that the date actually matches whatever format is contained in the same row.
  4. You should also look at any applications that are adding data to this database. They likely lack validation of the input or contain bugs that allow invalid data to be created.

Fix the existing data:

To find the offending row, try this. First create a function:

CREATE OR REPLACE FUNCTION DATETIME_IS_VALID_FOR_FORMAT(
  TEXT_DATETIME VARCHAR2,
  DATETIME_FORMAT VARCHAR2
)
  RETURN VARCHAR2
  IS
    DUMMYVAR DATE;
  BEGIN
    -- The assignment is only here to get the function to compile
    DUMMYVAR := TO_DATE(TEXT_DATETIME, DATETIME_FORMAT);
    RETURN 'TRUE';
  EXCEPTION
    WHEN OTHERS THEN
      RETURN 'FALSE';
  END;
/

Now SELECT the rows where this is 'FALSE':

SELECT *
FROM timedetail_dates
WHERE
  DATETIME_IS_VALID_FOR_FORMAT(timein_date, 'mm/dd/yyyy hh24:mi') != 'TRUE' OR
  DATETIME_IS_VALID_FOR_FORMAT(timeout_date, 'mm/dd/yyyy hh24:mi') != 'TRUE'

If you can't create functions because of low privileges on the database, you'll have to leverage DBMS_OUTPUT instead. Replace ID_COLUMN in the anonymous block below, and you can use it to find the bad rows:

DECLARE
  FUNCTION DATETIME_IS_VALID_FOR_FORMAT(
    TEXT_DATETIME VARCHAR2,
    DATETIME_FORMAT VARCHAR2
  )
    RETURN VARCHAR2
    IS
      DUMMYVAR DATE;
    BEGIN
      -- The assignment is only here to get the function to compile
      DUMMYVAR := TO_DATE(TEXT_DATETIME, DATETIME_FORMAT);
      RETURN 'TRUE';
    EXCEPTION
      WHEN OTHERS THEN
        RETURN 'FALSE';
    END;
BEGIN
  FOR T_ROW IN (SELECT * FROM timedetail_dates) LOOP
    IF (
      DATETIME_IS_VALID_FOR_FORMAT(T_ROW.TIMEIN_DATE, 'mm/dd/yyyy hh24:mi') != 'TRUE' OR
      DATETIME_IS_VALID_FOR_FORMAT(T_ROW.TIMEOUT_DATE, 'mm/dd/yyyy hh24:mi') != 'TRUE'
    ) THEN
      -- Replace ID_COLUMN with your actual primary key
      DBMS_OUTPUT.PUT_LINE('Bad row: '||T_ROW.ID_COLUMN);
    END IF;
  END LOOP;
END;
/

Note that you'll probably have to do some preparation to make your client start capturing the output from DBMS_OUTPUT.PUT_LINE. (This is client dependent, but you have to turn it on in both SQL*Plus and Oracle SQL Developer.) Also note that none of the output will show up until the block completes.