【问题标题】:SQL Date transformationsSQL 日期转换
【发布时间】:2018-12-20 14:47:32
【问题描述】:

我在 Excel 中收到了数据,并且 DATE 列(名为 ALL_PERIODS)是 STRING 并且具有以下格式:

JFM 14 - W/E 04/05/14

2014 年 1 月、2 月、3 月,周末 04/05/14

我需要将此列分为三个新列:DATE_ (DATE)、YEAR_ (INT)、QUARTER_ (INT),所以我写了这些转换:

列日期:

ALTER TABLE MY_TABLE 
   ADD DATE_TEMP VARCHAR2 (128);
UPDATE MY_TABLE 
   SET DATE_TEMP = SUBSTR(ALL_PERIODS, -2, 2);
UPDATE MY_TABLE 
   SET DATE_TEMP = REPLACE(DATE_TEMP, '/',  '');
UPDATE MY_TABLE 
   SET DATE_TEMP = to_char(to_date(DATE_TEMP, 'mmddyyyy'), 'yyyy-mm-dd');
UPDATE MY_TABLE 
   SET DATE_TEMP = REPLACE(DATE_TEMP, '00',  '20');
UPDATE MY_TABLE 
   SET DATE_TEMP = TO_DATE(DATE_TEMP, 'YYYY-MM-DD');
ALTER TABLE MY_TABLE 
ADD DATE_ DATE;
UPDATE MY_TABLE 
   SET DATE_ = DATE_TEMP;
ALTER TABLE MY_TABLE 
   DROP COLUMN DATE_TEMP;

列年份:

ALTER TABLE MY_TABLE
    ADD YEAR_TEMP VARCHAR2(128 BYTE);
UPDATE MY_TABLE
    SET YEAR_TEMP =  SUBSTR(ALL_PERIODS, 8, 4);
ALTER TABLE MY_TABLE
    ADD YEAR_ NUMBER(4);
UPDATE MY_TABLE
    SET YEAR_ =  YEAR_TEMP;
UPDATE MY_TABLE
    SET YEAR_ = CONCAT('20', YEAR_);
ALTER TABLE MY_TABLE
    DROP COLUMN YEAR_TEMP;

列季度:

ALTER TABLE MY_TABLE 
    ADD QUARTER NUMBER(1);
UPDATE MY_TABLE
    SET QUARTER_ = 4
        WHERE DATE_ = '30-DEC-17';
...and the same with other 3 quarters

不知何故可行,但我想它不是有效的(我的SQL不是很高级,我还在学习),并且更新操作太慢了,所以我想改造一下ALL_PERIODS 列,同时将数据插入到具有三个所需列的新表中。

您能告诉我,INSERT 应该是什么样子吗? 谢谢 N.

【问题讨论】:

  • 你如何区分六月和七月和一月?
  • 数据按季度分发,始终为 JFM、AMJ、JAS 或 OND。但几个月对我的分析并不重要。我需要四分之一(整数或数字)。
  • 这可以通过简单的CASE 语句来处理。

标签: sql oracle date sql-insert transformation


【解决方案1】:

我一开始只是简单地插入记录来模拟您的 Excel 日期字符串。您可以使用此逻辑来查看如何完成转换。

设置

INSERT INTO myTable (baseDate)
SELECT 'JFM 14 - W/E 04/05/14' FROM dual
UNION ALL 
SELECT 'AMJ 14 - W/E 06/05/14' FROM dual
UNION ALL 
SELECT 'JAS 14 - W/E 09/05/14' FROM dual
UNION ALL 
SELECT 'OND 14 - W/E 12/05/14' FROM dual
UNION ALL 
SELECT 'JFM 15 - W/E 04/05/15' FROM dual
UNION ALL 
SELECT 'AMJ 15 - W/E 06/05/14' FROM dual
UNION ALL 
SELECT 'JAS 15 - W/E 09/05/14' FROM dual
UNION ALL 
SELECT 'JAS 49 - W/E 03/05/49' FROM dual
UNION ALL 
SELECT 'JAS 50 - W/E 04/05/50' FROM dual

接下来的SELECT 行验证您的服务器的默认日期格式。此值可能会影响字符串的日期解释。

SELECT value FROM v$nls_parameters WHERE parameter ='NLS_DATE_FORMAT';
|价值 | | :-------- | | DD-MON-RR |

插入值后,我可以根据该值更新表。

UPDATE myTable
SET
    DATE_ = 
        TO_DATE(
            SUBSTR(baseDate,INSTR(baseDate, ' ', -1)+1,8) -- Find last space to get end of string date value.
            , 'MM/DD/RR'
        )  /* TO_DATE already creates date object. RR mask rounds to 4-digit year. (49 = 1949, 50 = 2050) */
    , QUARTER_ = 
        CASE SUBSTR(baseDate, 1, 3)
            WHEN 'JFM' THEN 1 
            WHEN 'AMJ' THEN 2
            WHEN 'JAS' THEN 3
            WHEN 'OND' THEN 4
        END

对于DATE_,您不需要执行TO_CHAR,因为TO_DATE 会将您的字符串转换为数据库中日期列的日期值。您的数据库并不真正关心日期是否看起来像“yyyy-mm-dd”,因为无论如何它都不会那样看。我使用 RR 掩码将 2 位数年份值转换为 4 位数年份值。这会将49 AS 204950 转换为1950。如果您不需要19xx 日期,则可以使用YY。如果需要,您可以对字符串值进行额外验证,以确保可以将其转换为日期。

对于QUARTER_,只需一个简单的CASE 语句即可将您的季度字符串解释为数字。

然后,我根据DATE_ 中的日期值更新YEAR_。这确保了我的年份在行内保持一致。如果值是从基本字符串中提取的,也可以在初始更新中完成。

UPDATE myTable
SET YEAR_ = EXTRACT ( YEAR FROM DATE_ )

这给了我们:

基础 |日期_ |年_ |四分之一_ :-------------------- | :-------- | ----: | --------: JFM 14 - W/E 04/05/14 | 05-APR-14 | 2014 | 1 AMJ 14 - W/E 06/05/14 | 2014 年 6 月 5 日 | 2014 | 2 JAS 14 - W/E 09/05/14 | 05-SEP-14 | 2014 | 3 OND 14 - W/E 12/05/14 | 14 年 12 月 5 日 | 2014 | 4 JFM 15 - W/E 04/05/15 | 15 年 4 月 5 日 | 2015 | 1 AMJ 15 - W/E 06/05/14 | 2014 年 6 月 5 日 | 2014 | 2 JAS 15 - W/E 09/05/14 | 05-SEP-14 | 2014 | 3 JAS 49 - W/E 03/05/49 | 05-MAR-49 | 2049 | 3 JAS 50 - W/E 04/05/50 | 05-APR-50 | 1950 | 3

由于DATE_ 是一个实际的日期对象,在显示时,您可以使用任何您想要的掩码对其进行格式化。

db小提琴here

【讨论】:

    【解决方案2】:
    INSERT INTO my_table (COL1, COL2, ... DATE_, QUARTER_, YEAR_)
    SELECT sr.COL1, sr.COL2, .... 
    TO_DATE(TO_CHAR(TO_DATE(SUBSTR(sr.ALL_PERIODS, -8), 'mm/dd/yy'), 'YYYY-MM-DD'), 'YYYY-MM-DD') DATE_,
    CASE SUBSTR(sr.ALL_PERIODS, 1, 3)
    WHEN 'JFM' THEN 1 
    WHEN 'AMJ' THEN 2
    WHEN 'JAS' THEN 3
    WHEN 'OND' THEN 4
    END QUARTER_,
    '20'||SUBSTR(sr.ALL_PERIODS, 5,2) YEAR_,
    FROM SOURCE_TABLE sr
    [WHERE sr.COL99 = 'Y'];
    

    【讨论】:

      【解决方案3】:

      DDL 语句(添加/删除列)速度慢且不必要。插入数据时不要更改表定义!

      我会像这样插入 3 列(用真正的 ALL_PERIODS 值替换我使用的日期字符串):

      INSERT INTO MY_TABLE (DATE_, YEAR_, QUARTER_)
      TO_DATE(SUBSTR('JFM 14 - W/E 04/05/14', -8), 'dd/mm/yy'),
      EXTRACT(YEAR FROM TO_DATE(SUBSTR('JFM 14 - W/E 04/05/14', -2), 'yy')),
      -- OR SHORTER IF ALWAYS IN THE 2000: TO_NUMBER(SUBSTR('JFM 14 - W/E 04/05/14', -2)) + 2000
      DECODE(SUBSTR('JFM 14 - W/E 04/05/14',1,3), 'JFM', 1, 'AMJ', 2, 'JAS', 3, 'OND', 4);
      

      问候

      【讨论】:

        【解决方案4】:

        如果 ALL_PERIODS 已经加载到表中?
        并且要计算的额外列已经添加到表中?

        那么 1 次更新就足够了。

        UPDATE MY_TABLE
        SET 
         DATE_ = TO_DATE(SUBSTR(RTRIM(ALL_PERIODS),-8),'MM/DD/YY')
        ,YEAR_ = 2000+SUBSTR(ALL_PERIODS,5,2)
        ,QUARTER_ = DECODE(SUBSTR(ALL_PERIODS,1,3), 'JFM', 1, 'AMJ', 2, 'JAS', 3, 'OND', 4)
        WHERE ALL_PERIODS IS NOT NULL 
          AND DATE_ IS NULL
        

        测试 dbfiddle here

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-08-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多