从INPUT字符串中动态地将condtion添加到plsql查询中

时间:2017-05-03 09:28:52

标签: sql oracle plsql

CREATE OR REPLACE PROCEDURE STRING_CHECK
    ( QUERY_COND IN VARCHAR2, RESP_CODE OUT VARCHAR2, RESP_MSG OUT VARCHAR2 )
IS
    EM_NAME VARCHAR2(50);
BEGIN
    SELECT EMAIL INTO EM_NAME FROM EMPLOYEES WHERE EMPLOYEE_ID=110 QUERY_COND;
END;

在这个简单的过程中,QUERY_COND有一个我希望在where条件之后传递的字符串,如下所示:

DECLARE 
    RESP_CODE VARCHAR2(20) := '000';
    RESP_MSG VARCHAR2(50) := 'SUCCESS';
    QUERY_COND VARCHAR2(100) := 'AND HIRE_DATE BETWEEN ''15-JUN-2003'' AND ''25-MAY-2005''';
BEGIN
    DBMS_OUTPUT.PUT_LINE(QUERY_COND);
    STRING_CHECK(QUERY_COND,RESP_CODE,RESP_MSG);
END;

我只是想知道是否可以这样做。如果完成,我该如何实现呢?

2 个答案:

答案 0 :(得分:1)

您可以使用动态SQL执行此操作;例如:

CREATE OR REPLACE PROCEDURE STRING_CHECK(QUERY_COND IN VARCHAR2,RESP_CODE OUT VARCHAR2,RESP_MSG OUT VARCHAR2)
IS
    EM_NAME VARCHAR2(50);
    vSQL    varchar2(1000);
BEGIN
    vSQL := 'SELECT EMAIL FROM EMPLOYEES WHERE EMPLOYEE_ID=110 ' || QUERY_COND;
    --
    execute immediate vSQL into EM_NAME;
END;


DECLARE 
    RESP_CODE VARCHAR2(20):='000';
    RESP_MSG VARCHAR2(50):='SUCCESS';
    QUERY_COND VARCHAR2(100):='AND HIRE_DATE BETWEEN date ''2003-06-15'' AND date ''2005-05-25''';
BEGIN
    DBMS_OUTPUT.PUT_LINE(QUERY_COND);
    STRING_CHECK(QUERY_COND,RESP_CODE,RESP_MSG);
END;

请注意,我更改了处理日期的方式,以避免在不提供格式的情况下进行转换。

另外,我不推荐这样的方法来构建基于某个参数的查询;使用一些日期参数构建静态查询会更好,避免使用动态SQL。

例如:

CREATE OR REPLACE PROCEDURE STRING_CHECK_2(dateStart IN     date, 
                                           dateEnd   IN     date,
                                           RESP_CODE    OUT VARCHAR2,
                                           RESP_MSG     OUT VARCHAR2
                                           )
IS
    EM_NAME VARCHAR2(50);
BEGIN
    SELECT EMAIL
    into EM_NAME
    FROM EMPLOYEES
    WHERE EMPLOYEE_ID=110
      and HIRE_DATE BETWEEN dateStart and dateEnd;
    --
END;

当然,必须对其进行细化以处理错误,并且在输入中没有给出两个日期的情况下,但我更喜欢使用布尔逻辑而不是动态布局逻辑的静态方法。

答案 1 :(得分:0)

通常,您不应该使用该方法,因为它会将代码打开到各种SQL注入漏洞:

DECLARE 
  RESP_CODE VARCHAR2(20):='000';
  RESP_MSG VARCHAR2(50):='SUCCESS';
  QUERY_COND VARCHAR2(100):=' AND EXISTS( SELECT 1 FROM your_password_table WHERE user_id = ''007'') --';
BEGIN
    DBMS_OUTPUT.PUT_LINE(QUERY_COND);
    STRING_CHECK(QUERY_COND,RESP_CODE,RESP_MSG);
END;

如果user_id 007的表中有密码,则会返回结果(这几乎肯定不是您希望用户能够对此查询执行的操作)。

相反,您应该知道要过滤哪些参数并接受它们:

CREATE OR REPLACE PROCEDURE STRING_CHECK(
  date_start IN  EMPLOYEES.HIRE_DATE%TYPE,
  date_end   IN  EMPLOYEES.HIRE_DATE%TYPE,
  RESP_CODE  OUT VARCHAR2,
  RESP_MSG   OUT VARCHAR2
)
IS
  EM_NAME VARCHAR2(50);
BEGIN
  SELECT EMAIL
  INTO   EM_NAME
  FROM   EMPLOYEES
  WHERE  EMPLOYEE_ID = 110
  AND    ( date_start IS NULL OR date_start <= hire_date )
  AND    ( date_end   IS NULL OR hire_date  <= date_end  );

  -- generate responses
END;
/