填充维表时出现PL / SQL FOR循环错误

时间:2018-02-22 13:53:54

标签: oracle loops plsql case

我使用以下代码填充名为TIMES的维度表,其中包含来自名为SALES的OLTP表中的数据:

CREATE TABLE TIMES
(saleDay        DATE        PRIMARY KEY,
dayType         VARCHAR(50) NOT NULL);


BEGIN
    FOR rec IN 
        (SELECT saleDate, CASE WHEN h.hd IS NOT NULL THEN 'Holiday'
                WHEN to_char(saleDate, 'd') IN (1,7) THEN 'Weekend'
                ELSE 'Weekday' END dayType
        FROM SALES s LEFT JOIN 
            (SELECT '01.01' hd FROM DUAL UNION ALL
            SELECT '15.01' FROM DUAL UNION ALL
            SELECT '19.01' FROM DUAL UNION ALL
            SELECT '28.05' FROM DUAL UNION ALL
            SELECT '04.07' FROM DUAL UNION ALL
            SELECT '08.10' FROM DUAL UNION ALL
            SELECT '11.11' FROM DUAL UNION ALL
            SELECT '22.11' FROM DUAL UNION ALL
            SELECT '25.12' FROM DUAL) h
        ON h.hd = TO_CHAR(s.saleDate, 'dd.mm'))    
        LOOP
            INSERT INTO TIMES VALUES rec;
        END LOOP;
END;
/

当我运行它时,我收到错误ORA-00001(唯一约束违规)和ORA-06512。我相信这种情况正在发生,因为代码试图在我的TIMES维度表(saleDay)的PK中输入多个日期(其中一些是相同的)。我如何在这个循环中实现一个子句,这样它只会将每个saleDate的一个实例填充到saleDay PK中,这样就没有违规?

例如,如果SALES表中有三行saleDate是2015-10-10,则代码应仅填充2015-10-10的一个实例到{{ 1}} PK。我正在考虑我应该采用的方向是实现一个saleDay子句,但是我不能100%确定它是如何工作的,因为这段代码也使用WHILE来确定是否CASE 1}}是假日,工作日或周末,并将结果填入saleDay列。

2 个答案:

答案 0 :(得分:1)

根据问题下方的评论中的建议添加DISTINCT是解决问题的一种方法。

以下方法可能更有效:

for rec in (select distinct saledate from sales)
loop
    insert into times (saleday, daytype) values
        (rec.saledate,   CASE .......);
end loop;

即:将CASE表达式放在INSERT语句中,而不是放在(隐式)游标的定义中。没有理由在同一日期多次计算CASE表达式,这可能在SALES表中多次出现。 CASE表达式也没有理由成为游标的一部分。 CASE表达式可以使用IN条件(case when to_char(rec.saledate, 'dd.mm') in ('01.01', '15.01', ....) then 'Holiday' when .......

当然,除非作业问题明确指示你使用左外连接.......: - (

答案 1 :(得分:0)

添加DISTINCT已解决此问题。最初认为DISTINCT会对CASE产生负面影响,但事实并非如此。感谢I3rutt指出这一点。

BEGIN
    FOR rec IN 
        (SELECT DISTINCT saleDate, CASE WHEN h.hd IS NOT NULL THEN 'Holiday'
                WHEN to_char(saleDate, 'd') IN (1,7) THEN 'Weekend'
                ELSE 'Weekday' END dayType
        FROM SALES s LEFT JOIN 
            (SELECT '01.01' hd FROM DUAL UNION ALL
            SELECT '15.01' FROM DUAL UNION ALL
            SELECT '19.01' FROM DUAL UNION ALL
            SELECT '28.05' FROM DUAL UNION ALL
            SELECT '04.07' FROM DUAL UNION ALL
            SELECT '08.10' FROM DUAL UNION ALL
            SELECT '11.11' FROM DUAL UNION ALL
            SELECT '22.11' FROM DUAL UNION ALL
            SELECT '25.12' FROM DUAL) h
        ON h.hd = TO_CHAR(s.saleDate, 'dd.mm'))    
        LOOP
            INSERT INTO TIMES VALUES rec;
        END LOOP;
END;
/