ORACLE 中的动态 where 子句

Dynamic where clause in ORACLE

要求是从 STUDENTS table 中获取结果。 如果将零作为学生 ID 传入,则不需要对学生 ID 进行更多过滤。

PROCEDURE getResults (      
    inStudentId      IN  NUMBER,
    inSectionId      IN  NUMBER,
    inRowLimit       IN  NUMBER,
    outResultsData   OUT gphResultsData
  ) AS
    stStudentId  VARCHAR2(50) := '';
  BEGIN
    outResultsData := gphResultsData();
    IF inStudentId = '0' THEN
      stStudentId := '%';
    ELSE
      stStudentId := TO_CHAR(inStudentId);
    END IF;

    FOR rResults IN (
      SELECT
        RESULTS.STUDENT_ID,
        RESULTS.STUDENT_NAME
      FROM
        RESULTS
      WHERE
        RESULTS.STUDENT_ID LIKE stStudentId  AND -- not a good idea
        RESULTS.SECTION_ID  =   inSectionId  AND
        ROWNUM <= inRowLimit
    ) LOOP
      outResultsData.extend;
      outResultsData(outResultsData.last).studentId   :=  rResults.STUDENT_ID;
      outResultsData(outResultsData.last).studentName :=  rResults.STUDENT_NAME;
    END LOOP;

  EXCEPTION
    WHEN others THEN
      ... 

我想出了上面的解决方案——这绝对不理想,因为

inStudentId使用TO_CHAR转换为STRING,然后进行LIKE.

我想更好的方法是 动态生成并执行 where 子句。 即 -

如果 inStudentId = 0 ,

SELECT
    RESULTS.STUDENT_ID,
    RESULTS.STUDENT_NAME
FROM
    RESULTS
WHERE
    RESULTS.SECTION_ID  =   inSectionId  AND
    ROWNUM <= inRowLimit

如果 inStudentId 不为零,

SELECT
    RESULTS.STUDENT_ID,
    RESULTS.STUDENT_NAME
  FROM
    RESULTS
  WHERE
    RESULTS.STUDENT_ID  = inStudentId  AND
    RESULTS.SECTION_ID  = inSectionId  AND
    ROWNUM <= inRowLimit

任何关于如何最好地解决这个问题的建议都会很有帮助。

我认为最简单的方法如下:

PROCEDURE getResults (      
    inStudentId      IN  NUMBER,
    inSectionId      IN  NUMBER,
    inRowLimit       IN  NUMBER,
    outResultsData   OUT gphResultsData
  ) AS
  BEGIN
    outResultsData := gphResultsData();
    FOR rResults IN (
      SELECT
        RESULTS.STUDENT_ID,
        RESULTS.STUDENT_NAME
      FROM
        RESULTS
      WHERE
        (RESULTS.STUDENT_ID  = inStudentId OR inStudentId = 0)  AND 
        RESULTS.SECTION_ID  =   inSectionId  AND
        ROWNUM <= inRowLimit
    ) LOOP
      outResultsData.extend;
      outResultsData(outResultsData.last).studentId   :=  rResults.STUDENT_ID;
      outResultsData(outResultsData.last).studentName :=  rResults.STUDENT_NAME;
    END LOOP;

  EXCEPTION
    WHEN others THEN
      ... 

假设您有以下类型:

CREATE TYPE gphResult IS OBJECT(
  student_id INT,
  student_name VARCHAR2(50)
);
/

CREATE TYPE gphResultsData IS TABLE OF gphResult;
/

然后你可以像这样使用 BULK COLLECT INTO 来避免循环:

PROCEDURE getResults (      
    inStudentId      IN  NUMBER,
    inSectionId      IN  NUMBER,
    inRowLimit       IN  NUMBER,
    outResultsData   OUT gphResultsData
  ) AS
    stStudentId  VARCHAR2(50) := '';
  BEGIN
    SELECT gphResult( STUDENT_ID, STUDENT_NAME )
    BULK COLLECT INTO outResultsData
    FROM   RESULTS
    WHERE  ( inStudentId = 0 OR student_id = inStudentId )
    AND    SECTION_ID =  inSectionId
    AND    ROWNUM     <= inRowLimit;
  EXCEPTION
    WHEN others THEN
      ...