我有以下过程,没有错误消息:
create or replace procedure insert_or_upd_movement_baselines_planned_weight_proc(
p_id IN VARCHAR2,
p_date IN DATE,
p_planned_col_name IN VARCHAR2,
p_planned_value IN NUMBER
) as
begin
declare
plsql_block NVARCHAR2(8000);
begin
plsql_block := 'merge into MOVEMENT_BASELINES mb using dual on (mb.MOVEMENT_ID = ' || p_id || ' and mb.MOVEMENT_DATE = ' || p_date || ')
when not matched then insert (mb.MOVEMENT_ID, mb.MOVEMENT_DATE, mb.' || p_planned_col_name || ')
values ( ' || p_id || ', ' || p_date || ', ' || p_planned_value || ')
when matched then update set '
|| p_planned_col_name || ' = ' || p_planned_value || ';';
execute immediate plsql_block;
end;
end insert_or_upd_movement_baselines_planned_weight_proc;
当我尝试使用输入参数的值执行它时,出现编译器错误:
Connecting to the database localDB.
ORA-00933: SQL command not properly ended
ORA-06512: at "RTT.INSERT_OR_UPD_MOVEMENT_BASELINES_PLANNED_WEIGHT_PROC", line 17
ORA-06512: at line 12
Process exited.
我是Oracle新手,想打印动态sql以检查出什么问题,但是print语句似乎不起作用。 我猜问题出在插入语句中的动态列名-知道什么地方错了吗? 谢谢
答案 0 :(得分:2)
使用动态SQL时应始终保持谨慎。首先,最好检查静态SQL语句是否工作正常,然后尝试通过修改动态部分来对其进行转换。另外,在dbms_output
之前的execute immediate
可以帮助您了解所准备的sql在语法上是否正确。其次,连接值很容易发生 SQL注入,应该避免。首选的方法是将绑定变量与USING
的{{1}}选项一起使用。
由于EXECUTE IMMEDIATE
被定义为数字,这意味着您计划更新/插入的所有列的数据类型将为整数。在演示示例中,我已经相应地使用了它。如果不是这种情况,则您将不得不重新考虑如何定义该过程的参数,以使其适用于p_planned_value
数据类型等其他情况。
DATE
答案 1 :(得分:0)
这部分绝对是鱼腥味
|| p_date ||
因为它实际上和
一样|| to_char(p_date) ||
因此日期的未引用值将成为语句的一部分,这将不会导致有效的sql语句。尝试以下方法:
values ( ' || p_id || ', to_date(''' || to_char(p_date) || '''), ' || p_planned_value || ')
答案 2 :(得分:0)
这是Kaushik答案的附录,他们在其中指出(如果不是很多话,很正确),您的语句完全容易受到SQL注入的攻击。</ p>
我将按照以下步骤编写您的程序:
CREATE OR REPLACE PROCEDURE insert_or_upd_movement_baselines_planned_weight_proc(p_id IN VARCHAR2,
p_date IN DATE,
p_planned_col_name IN VARCHAR2,
p_planned_value IN NUMBER) AS
v_sql CLOB;
v_planned_col_name VARCHAR2(32);
BEGIN
v_planned_col_name := dbms_assert.simple_sql_name(p_planned_col_name);
v_sql := 'MERGE INTO movement_baselines tgt'||CHR(10)||
'USING (SELECT :p_id movement_id,'||CHR(10)||
' :p_date movement_date,'||CHR(10)||
' :p_planned_value planned_value'||CHR(10)||
' FROM dual) src'||CHR(10)||
'ON (tgt.movement_id = src.movement_id AND tgt.movement_date = src.movement_date)'||CHR(10)||
'WHEN NOT MATCHED THEN'||CHR(10)||
' INSERT (tgt.movement_id, tgt.movement_date, tgt.'||v_planned_col_name||')'||CHR(10)||
' VALUES (src.movement_id, src.movement_date, src.movement_date)'||CHR(10)||
'WHEN MATCHED THEN'||CHR(10)||
' UPDATE'||CHR(10)||
' SET tgt.'||v_planned_col_name||' = src.planned_value';
dbms_output.put_line('merge statement: ' || chr(10) || v_sql);
EXECUTE IMMEDIATE v_sql
USING p_id, p_date, p_planned_value;
END;
/
请注意使用dbms_assert
来清理您的输入-在这种情况下,我们正在检查您传递给p_planned_col_name的值是否符合其为有效标识符的要求,这意味着它绝对不能用于SQL注入。
此外,我将参数移到了子查询中,这意味着execute立即数的using
子句现在更短了,而且我认为更清晰,更易于维护。