我正在研究使用动态查询根据用户输入执行选择语句的应用程序,在与 DBA 讨论安全性之后,他们希望我将动态选择语句转换为存储过程。
我已经使用 MSSQL 构建了动态 sql,但我不知道如何将其转换为 Oracle SQL。
CREATE PROCEDURE GetCustomer
@FirstN nvarchar(20) = NULL,
@LastN nvarchar(20) = NULL,
@CUserName nvarchar(10) = NULL,
@CID nvarchar(15) = NULL as
DECLARE @sql nvarchar(4000),
SELECT @sql = 'C_FirstName, C_LastName, C_UserName, C_UserID ' +
'FROM CUSTOMER ' +
'WHERE 1=1 ' +
IF @FirstN IS NOT NULL
SELECT @sql = @sql + ' AND C_FirstName like @FirstN '
IF @LastN IS NOT NULL
SELECT @sql = @sql + ' AND C_LastName like @LastN '
IF @CUserName IS NOT NULL
SELECT @sql = @sql + ' AND C_UserName like @CUserName '
IF @CID IS NOT NULL
SELECT @sql = @sql + ' AND C_UserID like @CID '
EXEC sp_executesql @sql, N'@C_FirstName nvarchar(20), @C_LastName nvarchar(20), @CUserName nvarchar(10), @CID nvarchar(15)',
@FirstN, @LastN, @CUserName, @CID
Run Code Online (Sandbox Code Playgroud)
*请注意,我想防止 SQL 注入,我不想只是将字符串添加在一起
**我已经构建了一个单独的类来为我在 .net 中的应用程序创建这个动态查询我有将近 1000 行代码来处理所有事情并防止 sql 注入,但是 DBA 告诉我他们想要存储过程,以便他们可以控制输入和输出。
这可能会给你一个想法:
create table Customer (
c_firstname varchar2(50),
c_lastname varchar2(50),
c_userid varchar2(50)
);
insert into Customer values ('Micky' , 'Mouse', 'mm');
insert into Customer values ('Donald', 'Duck' , 'dd');
insert into Customer values ('Peter' , 'Pan' , 'pp');
create or replace function GetCustomer(
FirstN varchar2 := null,
LastN varchar2 := null,
CID varchar2 := null
) return sys_refcursor
as
stmt varchar2(4000);
ret sys_refcursor;
begin
stmt := 'select * from Customer where 1=1';
if FirstN is not null then
stmt := stmt || ' and c_firstname like ''%' || FirstN || '%''';
end if;
if LastN is not null then
stmt := stmt || ' and c_lastname like ''%' || LastN || '%''';
end if;
if CID is not null then
stmt := stmt || ' and c_userid like ''%' || CID || '%''';
end if;
dbms_output.put_line(stmt);
open ret for stmt;
return ret;
end;
/
Run Code Online (Sandbox Code Playgroud)
后来,在 SQL*Plus 中:
set serveroutput on size 100000 format wrapped
declare
c sys_refcursor;
fn Customer.c_firstname%type;
ln Customer.c_lastname %type;
id Customer.c_userid %type;
begin
c := GetCustomer(LastN => 'u');
fetch c into fn, ln, id;
while c%found loop
dbms_output.put_line('First Name: ' || fn);
dbms_output.put_line('Last Name: ' || ln);
dbms_output.put_line('user id: ' || id);
fetch c into fn, ln, id;
end loop;
close c;
end;
/
Run Code Online (Sandbox Code Playgroud)
编辑:评论是对的,程序受SQL注入的影响。因此,为了防止这种情况发生,您可以使用绑定变量,例如在此修改后的过程中:
create or replace function GetCustomer(
FirstN varchar2 := null,
LastN varchar2 := null,
CID varchar2 := null
) return sys_refcursor
as
stmt varchar2(4000);
ret sys_refcursor;
type parameter_t is table of varchar2(50);
parameters parameter_t := parameter_t();
begin
stmt := 'select * from Customer where 1=1';
if FirstN is not null then
parameters.extend;
parameters(parameters.count) := '%' || FirstN || '%';
stmt := stmt || ' and c_firstname like :' || parameters.count;
end if;
if LastN is not null then
parameters.extend;
parameters(parameters.count) := '%' || LastN || '%';
stmt := stmt || ' and c_lastname like :' || parameters.count;
end if;
if CID is not null then
parameters.extend;
parameters(parameters.count) := '%' || CID || '%';
stmt := stmt || ' and c_userid like :' || parameters.count;
end if;
if parameters.count = 0 then
open ret for stmt;
elsif parameters.count = 1 then
open ret for stmt using parameters(1);
elsif parameters.count = 2 then
open ret for stmt using parameters(1), parameters(2);
elsif parameters.count = 3 then
open ret for stmt using parameters(1), parameters(2), parameters(3);
else raise_application_error(-20800, 'Too many parameters');
end if;
return ret;
end;
/
Run Code Online (Sandbox Code Playgroud)
请注意,现在,无论输入如何,select 语句都变得select ... from ... where 1=1 and col1 like :1 and col2 :2 ...更安全了。
您不一定需要动态 SQL,因为某些 where 条件在不存在时不适用。
SELECT
C_FirstName, C_LastName, C_UserName, C_UserID
FROM
CUSTOMER
WHERE
(FirstN IS NULL OR C_FirstName LIKE FirstN)
AND (LastN IS NULL OR C_LastName LIKE LastN)
AND (CUserName IS NULL OR C_UserName LIKE CUserName)
AND (CID IS NULL OR C_UserID LIKE CID)
Run Code Online (Sandbox Code Playgroud)
将此代码放在包内的存储过程中是个好主意。
Oracle 提供了一些优秀的文档,可以让您快速了解存储过程和包。您可能希望从概念指南开始了解 Oracle 的工作原理,然后转到SQL 语言参考和PL/SQL 语言参考以获取与您当前任务相关的信息。