【发布时间】:2021-10-27 10:14:40
【问题描述】:
我有一个测试用例,工作正常。
在这种情况下,有一段代码带有 COMMENT FAILED,我试图最终将行插入到表中,提示为 ignore_row_on_dupkey_index。
在尝试 INSERT 时,我收到了期待 SELECT 的错误,我不明白为什么会有 SELECT。我显然遗漏了一些东西,但我无法弄清楚如何纠正这个问题。
测试的设置有点冗长(我很抱歉),但您可以剪切和粘贴设置的所有内容,因为除了失败的评论开始的最后一部分之外,它们都可以正常工作。
如果有人能告诉我为什么 INSERT 不起作用并提供修复,我们将不胜感激。在此先感谢所有回复的人和您的专业知识。顺便说一句,如果你想模拟我的环境,我正在测试实时 SQL
ALTER SESSION SET NLS_DATE_FORMAT = 'MMDDYYYY HH24:MI:SS';
CREATE OR REPLACE TYPE nt_date IS TABLE OF DATE;
/
CREATE OR REPLACE FUNCTION generate_dates_pipelined(
p_from IN DATE,
p_to IN DATE
)
RETURN nt_date PIPELINED DETERMINISTIC
IS
v_start DATE := TRUNC(LEAST(p_from, p_to));
v_end DATE := TRUNC(GREATEST(p_from, p_to));
BEGIN
LOOP
PIPE ROW (v_start);
EXIT WHEN v_start >= v_end;
v_start := v_start + INTERVAL '1' DAY;
END LOOP;
RETURN;
END generate_dates_pipelined;
/
CREATE OR REPLACE FUNCTION CONVERT_TO_SECONDS(
i_date_string IN VARCHAR2
)
RETURN INTEGER DETERMINISTIC
AS
BEGIN
RETURN ( TO_DATE(i_date_string, 'HH24:MI:SS')
- TO_DATE('00:00:00', 'HH24:MI:SS')
) * 86400;
END;
/
Create table employees(
employee_id NUMBER(6),
first_name VARCHAR2(20),
last_name VARCHAR2(20),
card_num VARCHAR2(10),
work_days VARCHAR2(7)
);
INSERT INTO employees (
employee_id,
first_name,
last_name,
card_num,
work_days
)
WITH names AS (
SELECT 1, 'John', 'Doe', 'D564311','YYYYYNN' FROM dual UNION ALL
SELECT 2, 'Justin', 'Case', 'C224311','YYYYYNN' FROM dual UNION ALL
SELECT 3, 'Mike', 'Jones', 'J288811','YYYYYNN' FROM dual UNION ALL
SELECT 4, 'Jane', 'Smith', 'S564661','YYYYYNN' FROM dual
) SELECT * FROM names;
create table access_history(
seq_num integer GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
employee_id NUMBER(6),
card_num varchar2(10),
location_id number(4),
access_date date,
processed NUMBER(1) default 0
);
CREATE TABLE locations AS
SELECT level AS location_id,
'Door ' || level AS location_name,
CASE round(dbms_random.value(1,3))
WHEN 1 THEN 'G'
WHEN 2 THEN 'G'
WHEN 3 THEN 'G'
END AS location_type
FROM dual
CONNECT BY level <= 5;
ALTER TABLE locations
ADD ( CONSTRAINT locations_pk
PRIMARY KEY (location_id));
create table schedule_assignment(
seq_num NUMBER GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
schedule_id number(4),
schedule_date DATE,
employee_id NUMBER(6) DEFAULT 0,
constraint sa_chk check (schedule_date=trunc(schedule_date, 'dd')),
constraint sa_pk primary key (schedule_id, schedule_date,employee_id));
CREATE OR REPLACE PROCEDURE create_schedule_assignment (
p_schedule_id IN NUMBER,
p_start_date IN DATE,
p_end_date IN DATE,
p_employee_id IN NUMBER DEFAULT 0
)
IS
BEGIN
merge into schedule_assignment s
using (select p_schedule_id as schedule_id,
p_employee_id as employee_id,
column_value as schedule_date
from table(generate_dates_pipelined(p_start_date, p_end_date))
) x
on ( x.schedule_id = s.schedule_id
and x.schedule_date = s.schedule_date
and
x.employee_id =
s.employee_id
)
when not matched then insert (schedule_id, schedule_date, employee_id)
values (x.schedule_id, x.schedule_date,
x.employee_id);
END;
/
create table schedule(
seq_num NUMBER GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
schedule_id NUMBER(4),
location_id number(4),
base_date DATE,
start_date DATE,
end_date DATE,
constraint schedule_pk primary key (schedule_id, location_id, base_date),
CONSTRAINT start_min check (start_date=trunc(start_date,'MI')),
CONSTRAINT end_min check (end_date=trunc(end_date,'MI')),
CONSTRAINT end_gt_start CHECK (end_date >= start_date)
);
CREATE OR REPLACE PROCEDURE CREATE_SCHEDULE(
i_schedule_id IN PLS_INTEGER,
-- i_base_date IN DATE,
p_start_date in date,
p_end_date in date,
i_offset IN PLS_INTEGER DEFAULT 0,
i_incr IN PLS_INTEGER DEFAULT 10,
i_duration IN PLS_INTEGER DEFAULT 5
)
AS
l_offset interval day to second;
l_incr interval day to second;
l_duration interval day to second;
i_base_date date;
BEGIN
l_offset := NUMTODSINTERVAL(i_offset, 'SECOND') ;
l_incr := NUMTODSINTERVAL(i_incr, 'MINUTE') ;
l_duration := NUMTODSINTERVAL(i_duration, 'MINUTE') ;
for i in ( select column_value each_date from TABLE(generate_dates_pipelined(p_start_date, p_end_date)) )
loop
i_base_date := i.each_date;
MERGE INTO schedule dst
USING (
SELECT i_schedule_id AS schedule_id,
l.location_id,
i_base_date AS base_date,
i_base_date + l_offset + (l_incr * (ROWNUM - 1))
AS start_date,
i_base_date + l_offset + (l_incr * (ROWNUM - 1)) + l_duration
AS end_date
FROM locations l
where location_id in (
select location_id
from locations
where location_type = 'G'
)
) src
ON ( src.schedule_id = dst.schedule_id
AND src.location_id = dst.location_id
AND src.base_date = dst.base_date
)
WHEN NOT MATCHED THEN
INSERT (
schedule_id,
location_id,
base_date,
start_date,
end_date
) VALUES (
src.schedule_id,
src.location_id,
src.base_date,
src.start_date,
src.end_date
);
end loop;
END;
/
CREATE OR REPLACE PROCEDURE CREATE_ACCESS_HISTORY_INTERVAL
(
i_start_date IN DATE,
i_end_date IN DATE,
i_interval IN PLS_INTEGER DEFAULT 10
) AS
BEGIN
INSERT INTO access_history (employee_id, card_num, location_id, access_date)
WITH date_rows ( start_date, end_date ) AS (
SELECT i_start_date,
i_end_date
FROM DUAL
UNION ALL
SELECT start_date +
NUMTODSINTERVAL(i_interval, 'MINUTE'),
end_date
FROM date_rows
WHERE start_date +
NUMTODSINTERVAL(i_interval, 'MINUTE') < end_date
)
SELECT e.employee_id
, e.card_num
, l.location_id
, d.start_date
FROM employees e
CROSS JOIN locations l
CROSS JOIN date_rows d;
END;
/
CREATE table schedule_history(
seq_num INTEGER GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
schedule_id NUMBER(4),
base_date DATE,
employee_id NUMBER(6),
location_id NUMBER(4),
start_date DATE,
end_date DATE,
access_date DATE,
status VARCHAR2(1),
constraint schedule_history_pk primary key (schedule_id, start_date, end_date)
);
EXEC create_schedule_assignment(1,TRUNC(SYSDATE),TRUNC(SYSDATE)+2,1);
/
EXEC CREATE_ACCESS_HISTORY_INTERVAL(TRUNC(SYSDATE)+NUMTODSINTERVAL(CONVERT_TO_SECONDS('23:00:10'),'SECOND'),TRUNC(SYSDATE+1)+NUMTODSINTERVAL(CONVERT_TO_SECONDS('02:30:10'),'SECOND'))
EXEC CREATE_SCHEDULE(1,SYSDATE,SYSDATE+2,CONVERT_TO_SECONDS('23:00:00'));
/
WITH sch AS
(
SELECT s.schedule_id,
sa.schedule_date,
sa.employee_id,
s.location_id, s.start_date,
s.end_date
, ROW_NUMBER () OVER ( PARTITION BY s.schedule_id, s.location_id, TRUNC (s.start_date)
ORDER BY s.start_date
) AS rn
, TRUNC (s.start_date) AS this_day
, TRUNC (s.start_date) + 1 AS next_day
, s.start_date - INTERVAL '2' MINUTE AS early_date
,
s.end_date + INTERVAL '2' MINUTE AS late_date FROM schedule s
-- need JOIN to get employee_id
JOIN schedule_assignment sa
ON s.schedule_id = sa.schedule_id
AND TRUNC(s.start_date) = sa.schedule_date
), ah AS
(
SELECT a.employee_id, a.location_id, a.access_date
, ROW_NUMBER () OVER ( PARTITION BY a.employee_id, a.location_id, TRUNC (a.access_date)
ORDER BY a.access_date
) AS rn
FROM access_history a
)
select sch.schedule_id,
sch.schedule_date,
sch.employee_id,
sch.location_id, sch.start_date,
sch.end_date, ah.access_date,
CASE
WHEN ah.access_date < sch.early_date THEN 'E'
WHEN
ah.access_date >=
sch.early_date AND
ah.access_date <= sch.late_date THEN 'G'
WHEN ah.access_date > sch.late_date THEN 'L'
WHEN ah.access_date IS NULL THEN 'M'
END AS status
FROM sch
LEFT OUTER JOIN ah ON ah.employee_id = sch.employee_id
AND ah.location_id = sch.location_id
AND ah.access_date >= sch.this_day
AND ah.access_date < sch.next_day
ORDER BY
sch.employee_id, sch.start_date;
-- FAILED --
WITH sch AS
(
SELECT s.schedule_id,
sa.schedule_date,
sa.employee_id,
s.location_id, s.start_date,
s.end_date
, ROW_NUMBER () OVER ( PARTITION BY s.schedule_id, s.location_id, TRUNC (s.start_date)
ORDER BY s.start_date
) AS rn
, TRUNC (s.start_date) AS this_day
, TRUNC (s.start_date) + 1 AS next_day
, s.start_date - INTERVAL '2' MINUTE AS early_date
,
s.end_date + INTERVAL '2' MINUTE AS late_date FROM schedule s
-- need JOIN to get employee_id
JOIN schedule_assignment sa
ON s.schedule_id = sa.schedule_id
AND TRUNC(s.start_date) = sa.schedule_date
), ah AS
(
SELECT a.employee_id, a.location_id, a.access_date
, ROW_NUMBER () OVER ( PARTITION BY a.employee_id, a.location_id, TRUNC (a.access_date)
ORDER BY a.access_date
) AS rn
FROM access_history a
)
INSERT INTO SCHEDULE_HISTORY(
schedule_id
,base_date
,employee_id
,location_id
,start_date
,end_date
,access_date
,status
)
SELECT sch.schedule_id,
sch.schedule_date,
sch.employee_id,
sch.location_id, sch.start_date,
sch.end_date, ah.access_date,
CASE
WHEN ah.access_date < sch.early_date THEN 'E'
WHEN
ah.access_date >=
sch.early_date AND
ah.access_date <= sch.late_date THEN 'G'
WHEN ah.access_date > sch.late_date THEN 'L'
WHEN ah.access_date IS NULL THEN 'M'
END AS status
FROM sch
LEFT OUTER JOIN ah ON ah.employee_id = sch.employee_id
AND ah.location_id = sch.location_id
AND ah.access_date >= sch.this_day
AND ah.access_date < sch.next_day;
【问题讨论】:
-
将近 400 行代码。哪一行失败,确切的错误信息是什么?
-
您发布的几乎所有代码都与实际错误无关;当然,如果您知道这一点,那么您可能能够找出问题所在。但是您可以系统地简化失败的语句,直到它起作用 - 然后您知道您更改的最后一件事就是问题;或者您不能再简化它,然后只能发布简化的代码,这将需要更少的依赖项。请阅读有关为将来的问题提供minimal reproducible example 的信息,重点是minimal。在执行此操作的过程中,您通常会发现问题。