Oracle PLSQL动态游标如何动态指定查询使用的表名?
现有实现的可行性
你的拼接写法在修正两处语法问题后可以正常运行:
- 声明块中
table_name缺少类型声明,且PLSQL赋值符为:=而非=,需要补全声明:
table_name VARCHAR2(100) := 'employees202110';
- 动态SQL拼接逻辑本身没有语法错误,运行时会自动替换表名执行查询。
但该写法存在明显的SQL注入风险:如果table_name的值来源于外部输入,恶意输入如employees202110; drop table users;--会直接执行恶意语句,导致数据丢失。同时没有表名校验逻辑,拼写错误的表名会直接触发运行时异常。
更推荐的实现方案
方案1:增加表名校验逻辑
拼接前先校验表名合法性,仅允许符合规则的表名执行动态SQL,示例如下:
DECLARE TYPE name_salary_rt IS RECORD ( name VARCHAR2 (1000), salary NUMBER ); TYPE name_salary_aat IS TABLE OF name_salary_rt INDEX BY PLS_INTEGER; l_employees name_salary_aat; l_cursor SYS_REFCURSOR; table_name VARCHAR2(100) := 'employees202110'; v_table_exists NUMBER; BEGIN -- 校验表名是否为合法的员工月度表 SELECT COUNT(*) INTO v_table_exists FROM user_tables WHERE table_name = UPPER(table_name) AND table_name LIKE 'EMPLOYEES________'; -- 匹配EMPLOYEES加6位年月的格式 IF v_table_exists = 0 THEN RAISE_APPLICATION_ERROR(-20001, '非法的表名:'||table_name); END IF; OPEN l_cursor FOR 'select first_name || '' '' || last_name, salary from ' || table_name || ' order by salary desc'; FETCH l_cursor BULK COLLECT INTO l_employees; CLOSE l_cursor; FOR indx IN 1 .. l_employees.COUNT LOOP DBMS_OUTPUT.put_line (l_employees (indx).name); END LOOP; END; /
方案2:使用Oracle自带校验函数规避注入
可以直接调用DBMS_ASSERT.SQL_OBJECT_NAME函数校验输入是否为合法的Oracle对象名,自动拦截注入内容:
OPEN l_cursor FOR 'select first_name || '' '' || last_name, salary from ' || DBMS_ASSERT.SQL_OBJECT_NAME(table_name) || ' order by salary desc';
如果输入非法对象名,会直接抛出ORA-44002: 无效的对象名异常,无需自己写校验逻辑。
方案3:替换为分区表实现(最优)
如果你的表是按月份拆分的员工表,且所有分表结构完全一致,建议将分表合并为按月份字段分区的单张employees表,之后直接使用静态游标即可:
-- 静态游标无需动态拼接,性能更高,无注入风险 OPEN l_cursor FOR select first_name || ' ' || last_name, salary from employees where month = '202110' -- 月份用绑定变量传入即可 order by salary desc;
方案4:固定表名范围使用静态分支
如果仅需要适配少量固定的表,可以用条件分支走静态游标,完全避免动态SQL:
IF table_name = 'employees202110' THEN OPEN l_cursor FOR select first_name || ' ' || last_name, salary from employees202110 order by salary desc; ELSIF table_name = 'employees202111' THEN OPEN l_cursor FOR select first_name || ' ' || last_name, salary from employees202111 order by salary desc; ELSE RAISE_APPLICATION_ERROR(-20001, '不支持的表名'); END IF;
内容的提问来源于stack exchange,提问作者Mary
相关产品推荐
相关产品推荐

