0

Ich bin mit Oracle 10g und ich habe die folgende gespeicherte Prozedur:Dynamische Vergleichsoperator in einer where-Klausel ungültig ist (PL/SQL Oracle)

CREATE OR REPLACE PACKAGE BODY RF_PKG_STFCA_PositivePay 
AS 
PROCEDURE RF_SP_STFCA_PositivePay(IN_DATE IN NUMBER, IN_DATE_OPERATOR IN NVARCHAR2, OUT_DATA OUT CUR_DATA) 
IS 
/* this procedure returns a Ref Cursor with all the requested parameters 
calling the stored procedure from an asp page (and anywhere else) 
does not require posting a predefined number of records */ 
    PaymentBatchNumber  NVARCHAR2(4); 
    CurrencyCode   NVARCHAR2(3); 
    TransactionCode   NVARCHAR2(3); 
    Transit_BranchNumber  NVARCHAR2(5); 
    BankAccountNumber  NVARCHAR2(7); 
    ChequeNumber   NVARCHAR2(8); 
    ChequeAmount   NVARCHAR2(10); 
    ClientReference   NVARCHAR2(19); 
    IssueDate   NVARCHAR2(8); 
    PayeeName1   NVARCHAR2(60); 
    AddressLine1   NVARCHAR2(60); 
    AddressLine2   NVARCHAR2(60); 
    AddressLine4   NVARCHAR2(60); 
    AddressLine5   NVARCHAR2(60); 
    DateCreated   NVARCHAR2(25); 
    DateVoided   NVARCHAR2(25); 
BEGIN 
OPEN OUT_DATA FOR 
    SELECT LPAD(NVL(CD.PAYMENT_BATCH_NO, '0'), 4, '0') AS PaymentBatchNumber, 
    SUBSTR(NVL(CD.CURRENCY_ID, ' '), 1, 1) AS CurrencyCode, 
    NVL(CD.STATUS, ' ') AS TransactionCode, 
    LPAD(NVL(BA.BRANCH_ID, '0'), 5, '0') AS Transit_BranchNumber, 
    LPAD(NVL(BA.ACCOUNT_NO, '0'), 7, '0') AS BankAccountNumber, 
    LPAD(NVL(CD.CHECK_NO, '0') , 8, '0') AS ChequeNumber, 
    LPAD(TO_CHAR(NVL(CD.AMOUNT, 0)), 10, '0') AS ChequeAmount, 
    LPAD(NVL(CD.CONTROL_NO, '0'), 19, '0') AS ClientReference, 
    TO_CHAR(NVL(CD.CHECK_DATE, LPAD(' ', 8, ' ')), 'YYYYMMDD') AS IssueDate, 
    RPAD(NVL(CD.NAME, ' '), 60, ' ') AS PayeeName1, 
    RPAD(NVL(CD.ADDR_1, ' '), 60, ' ') AS AddressLine1, 
    RPAD(NVL(CD.ADDR_2, ' '), 60, ' ') AS AddressLine2, 
    RPAD(NVL(CD.CITY, '') || CASE WHEN CD.CITY IS NULL OR CD.STATE IS NULL THEN ' ' ELSE ', ' END || NVL(CD.STATE, ''), 60, ' ') AS AddressLine4, 
    RPAD(NVL(CD.ZIPCODE, ' '), 60, ' ') AS AddressLine5, 
    TO_CHAR(CD.CREATE_DATE, 'YYYYMMDDHH24MISS') AS DateCreated, 
    CASE WHEN CD.VOID_DATE IS NULL THEN ' ' ELSE TO_CHAR(CD.VOID_DATE, 'YYYYMMDDHH24MISS') END AS DateVoided 
    INTO PaymentBatchNumber, CurrencyCode, TransactionCode, Transit_BranchNumber, BankAccountNumber, ChequeNumber, 
    ChequeAmount, ClientReference, IssueDate, PayeeName1, AddressLine1, AddressLine2, AddressLine4, AddressLine5, 
    DateCreated, DateVoided 
    FROM BANK_ACCOUNT BA 
    INNER JOIN CASH_DISBURSEMENT CD ON BA.ID = CD.BANK_ACCOUNT_ID 
    WHERE BA.ACCOUNT_NO IS NOT NULL AND CD.CHECK_NO > 0 AND CD.STATUS != 'X' AND CD.AMOUNT != 0 AND ((TO_NUMBER(TO_CHAR(CD.CREATE_DATE, 'YYYYMMDDHH24MISS')) || IN_DATE_OPERATOR || IN_DATE) OR 
    (CASE WHEN CD.VOID_DATE IS NULL THEN 0 ELSE TO_NUMBER(TO_CHAR(CD.VOID_DATE, 'YYYYMMDDHH24MISS')) END || IN_DATE_OPERATOR || IN_DATE)) 
    ORDER BY BA.BRANCH_ID, BA.ACCOUNT_NO; 
END RF_SP_STFCA_PositivePay; 
END RF_PKG_STFCA_PositivePay; 

Und ich bekomme die folgende Fehlermeldung, wenn diese in SQL-Eingabe Plus :

ungültig relationalen Operator

Was zu tun, ich versuche: ich diese gespeicherte Prozedur, die mit dem REF CURSOR eine secordset meiner asp.net Anwendung zurückgibt. Ich gebe es 2 Eingabeparameter. 1 ist ein Datum (IN_DATE) und 1 ist ein Operator (IN_DATE_OPERATOR). Das Programm funktioniert wenn "|| IN_DATE_OPERATOR ||" ist ersetzt mit entweder = oder> = genau so wie ich es will. Das Problem basiert darauf, was in der .Net-Anwendung passiert. Ich möchte, dass der Operator, den es in der where-Klausel verwendet, entweder "> =" oder "=" ist und ich bis zur Laufzeit nicht wissen werde.

Ich weiß, dass ich das falsch mache, aber ich weiß nicht, wie man oracle bekommt, um zu bestätigen, dass IN_DATE_OPERATOR ein relationaler Operator ist. Ich bin offen für andere Methoden, um einen dynamischen Operator zu haben (ich habe versucht, CASE WHEN IN_DATE_OPERATOR = '=' THEN '=' ELSE '> =' ENDE ohne Erfolg), aber ich möchte nicht eine ganze separate gespeicherte Prozedur erstellen I Zusätzlich zu dieser oder einer komplett dynamischen Where-Klausel muss zusätzlich noch Meine ideale Lösung würde so wenig Änderungen wie möglich an dieser Abfrage vornehmen. Irgendwelche Vorschläge?

Edit: ok Ich habe meine Anfrage bearbeitet seien die folgenden:

CREATE OR REPLACE PACKAGE BODY RF_PKG_STFCA_PositivePay 
AS 
PROCEDURE RF_SP_STFCA_PositivePay(IN_DATE IN NUMBER, IN_DATE_OPERATOR IN VARCHAR2, OUT_DATA OUT CUR_DATA) 
IS 
/* this procedure returns a Ref Cursor with all the requested parameters 
calling the stored procedure from an asp page (and anywhere else) 
does not require posting a predefined number of records */ 
    SQL_Statement  VARCHAR2(8000); 
BEGIN 
    SQL_Statement := 'SELECT LPAD(NVL(CD.PAYMENT_BATCH_NO, ''0''), 4, ''0'') AS PaymentBatchNumber, ' || 
    ' SUBSTR(NVL(CD.CURRENCY_ID, '' ''), 1, 1) AS CurrencyCode, ' || 
    ' NVL(CD.STATUS, '' '') AS TransactionCode, ' || 
    ' LPAD(NVL(BA.BRANCH_ID, ''0''), 5, ''0'') AS Transit_BranchNumber, ' || 
    ' LPAD(NVL(BA.ACCOUNT_NO, ''0''), 7, ''0'') AS BankAccountNumber, ' || 
    ' LPAD(NVL(CD.CHECK_NO, ''0'') , 8, ''0'') AS ChequeNumber, ' || 
    ' LPAD(TO_CHAR(NVL(CD.AMOUNT, 0)), 10, ''0'') AS ChequeAmount, ' || 
    ' LPAD(NVL(CD.CONTROL_NO, ''0''), 19, ''0'') AS ClientReference, ' || 
    ' TO_CHAR(NVL(CD.CHECK_DATE, LPAD('' '', 8, '' '')), ''YYYYMMDD'') AS IssueDate, ' || 
    ' RPAD(NVL(CD.NAME, '' ''), 60, '' '') AS PayeeName1, ' || 
    ' RPAD(NVL(CD.ADDR_1, '' ''), 60, '' '') AS AddressLine1, ' || 
    ' RPAD(NVL(CD.ADDR_2, '' ''), 60, '' '') AS AddressLine2, ' || 
    ' RPAD(NVL(CD.CITY, '''') || CASE WHEN CD.CITY IS NULL OR CD.STATE IS NULL THEN '' '' ELSE '', '' END || NVL(CD.STATE, ''''), 60, '' '') AS AddressLine4, ' || 
    ' RPAD(NVL(CD.ZIPCODE, '' ''), 60, '' '') AS AddressLine5, ' || 
    ' TO_CHAR(CD.CREATE_DATE, ''YYYYMMDDHH24MISS'') AS DateCreated, ' || 
    ' CASE WHEN CD.VOID_DATE IS NULL THEN '' '' ELSE TO_CHAR(CD.VOID_DATE, ''YYYYMMDDHH24MISS'') END AS DateVoided ' || 
    ' FROM BANK_ACCOUNT BA ' || 
    ' INNER JOIN CASH_DISBURSEMENT CD ON BA.ID = CD.BANK_ACCOUNT_ID ' || 
    ' WHERE BA.ACCOUNT_NO IS NOT NULL AND CD.CHECK_NO > 0 AND CD.STATUS != ''X'' AND CD.AMOUNT != 0 ' || 
    ' AND ((TO_NUMBER(TO_CHAR(CD.CREATE_DATE, ''YYYYMMDDHH24MISS'')) ' || IN_DATE_OPERATOR || ' :1) ' || 
    ' OR (CASE WHEN CD.VOID_DATE IS NULL THEN 0 ELSE TO_NUMBER(TO_CHAR(CD.VOID_DATE, ''YYYYMMDDHH24MISS'')) END ' || IN_DATE_OPERATOR || ' :2)) ' || 
    ' ORDER BY BA.BRANCH_ID, BA.ACCOUNT_NO ';  
    OPEN OUT_DATA FOR SQL_Statement USING IN_DATE, IN_DATE;  
END RF_SP_STFCA_PositivePay; 
END RF_PKG_STFCA_PositivePay;/ 

aber ich bekomme die folgende Fehlermeldung:

LINE/COL ERROR


32/3 PL/SQL: Anweisung ignoriert 32/21 PLS-00382: Ausdruck ist vom falschen Typ

+0

In Ihrer Bearbeitung fügen Sie den String 'IN_DATE' hinzu. Beachten Sie, dass ich das in meinem Beispiel durch Bindevariablen (': 1' und': 2') ersetze und dann das 'IN_DATE' als Bindevariablenwert übergebe, wenn Sie den Cursor öffnen. Der Parameter 'IN_DATE' ist für die von Ihnen erstellte dynamische SQL-Anweisung nicht sichtbar. Während Sie debuggen, ist es wahrscheinlich eine gute Idee, eine 'dbms_output.put_line (sql_statement)' vor dem 'OPEN' hinzuzufügen. Dadurch wird die von Ihnen erstellte SQL-Anweisung ausgedruckt. Sie können die Zeichenfolge dann entweder visuell auf Fehler prüfen oder sie manuell ausführen, um bessere Fehler zu erhalten. –

+0

Entschuldigung, '|| IN_DATE_OPERATOR || 'IN_DATE)' sollte lauten: '|| IN_DATE_OPERATOR || "" || IN_DATE || 'und so weiter – Adamantine

+0

Sie möchten auch' IN_DATE_OPERATOR 'nicht als Bindevariable übergeben. Das muss tatsächlich Teil der SQL-Anweisung sein, die Sie erstellen.Es sieht so aus, als würden Sie die SQL-Anweisung korrekt erstellen, Sie müssten nur '' USING' 'zweimal' IN_DATE 'angeben (vorausgesetzt, Sie ersetzen 'IN_DATE', wenn Sie die SQL-Anweisung mit Bind-Variablen-Platzhaltern erstellen). –

Antwort

1

Sie w Sie müssen die SQL-Anweisung dynamisch in einer Zeichenfolge zusammenstellen und diese Zeichenfolge dann zum Öffnen des Cursors verwenden. Sie benötigen etwas in Anlehnung an die folgende Prozedur get_cur, in der Sie die SQL-Anweisung in einer lokalen VARCHAR2-Variable mit den Platzhaltern für die Bindevariablen zusammenfassen und anschließend den Cursor mit der SQL-Anweisung und der übergebenen Bindevariablen öffnen.

SQL> create or replace procedure get_cur(p_date in date, p_operator in varchar2, p_cur out sys_refcursor) 
    2 as 
    3 l_sql_stmt varchar2(1000); 
    4 begin 
    5 l_sql_stmt := 'select * from emp where hiredate ' || p_operator || ' :1'; 
    6 open p_cur for l_sql_stmt using p_date; 
    7 end; 
    8/

Procedure created. 

SQL> var rc refcursor; 
SQL> exec get_cur(date '2001-01-01', '>=', :rc); 

PL/SQL procedure successfully completed. 

SQL> print rc; 

no rows selected 

SQL> exec get_cur(date '2001-01-01', '<=', :rc); 

PL/SQL procedure successfully completed. 

SQL> print rc; 

    EMPNO ENAME  JOB    MGR HIREDATE   SAL  COMM 
---------- ---------- --------- ---------- --------- ---------- ---------- 
    DEPTNO 
---------- 
     7369 SMITH  CLERK   7902 17-DEC-80  801 
     20 

     7499 ALLEN  SALESMAN  7698 20-FEB-81  1601  300 
     30 

     7521 WARD  SALESMAN  7698 22-FEB-81  1251  500 
     30 


    EMPNO ENAME  JOB    MGR HIREDATE   SAL  COMM 
---------- ---------- --------- ---------- --------- ---------- ---------- 
    DEPTNO 
---------- 
     7566 JONES  MANAGER   7839 02-APR-81  2976 
     20 

     7654 MARTIN  SALESMAN  7698 28-SEP-81  1251  1400 
     30 

     7698 BLAKE  MANAGER   7839 01-MAY-81  2851 
     30 


    EMPNO ENAME  JOB    MGR HIREDATE   SAL  COMM 
---------- ---------- --------- ---------- --------- ---------- ---------- 
    DEPTNO 
---------- 
     7782 CLARK  MANAGER   7839 09-JUN-81  2451 
     10 

     7788 SCOTT  ANALYST   7566 19-APR-87  3001 
     20 

     7839 KING  PRESIDENT   17-NOV-81  5001 
     10 


    EMPNO ENAME  JOB    MGR HIREDATE   SAL  COMM 
---------- ---------- --------- ---------- --------- ---------- ---------- 
    DEPTNO 
---------- 
     7844 TURNER  SALESMAN  7698 08-SEP-81  1501   0 
     30 

     7876 ADAMS  CLERK   7788 23-MAY-87  1101 
     20 

     7900 JAMES  CLERK   7698 03-DEC-81  951 
     30 


    EMPNO ENAME  JOB    MGR HIREDATE   SAL  COMM 
---------- ---------- --------- ---------- --------- ---------- ---------- 
    DEPTNO 
---------- 
     7902 FORD  ANALYST   7566 03-DEC-81  3001 
     20 

     7934 MILLER  CLERK   7782 23-JAN-82  1301 
     10 


14 rows selected. 

Meine Vermutung ist, dass Sie so etwas wie dies wollen (natürlich, da ich Ihre Tabellen oder Typen nicht haben, kann ich nicht testen, dass diese kompiliert tatsächlich so werden Sie wahrscheinlich Fehler korrigieren müssen)

CREATE OR REPLACE PACKAGE BODY RF_PKG_STFCA_PositivePay 
AS 
PROCEDURE RF_SP_STFCA_PositivePay(IN_DATE IN NUMBER, IN_DATE_OPERATOR IN NVARCHAR2, OUT_DATA OUT CUR_DATA) 
IS 
/* this procedure returns a Ref Cursor with all the requested parameters 
calling the stored procedure from an asp page (and anywhere else) 
does not require posting a predefined number of records */ 
    l_sql_stmt VARCHAR2(4000); 
BEGIN 
    l_sql_stmt := q'[SELECT LPAD(NVL(CD.PAYMENT_BATCH_NO, '0'), 4, '0') AS PaymentBatchNumber, ]' || 
    q'[SUBSTR(NVL(CD.CURRENCY_ID, ' '), 1, 1) AS CurrencyCode, ]' || 
    q'[NVL(CD.STATUS, ' ') AS TransactionCode, ]' || 
    q'[LPAD(NVL(BA.BRANCH_ID, '0'), 5, '0') AS Transit_BranchNumber, ]' || 
    q'[LPAD(NVL(BA.ACCOUNT_NO, '0'), 7, '0') AS BankAccountNumber, ]' || 
    q'[LPAD(NVL(CD.CHECK_NO, '0') , 8, '0') AS ChequeNumber, ]' || 
    q'[LPAD(TO_CHAR(NVL(CD.AMOUNT, 0)), 10, '0') AS ChequeAmount, ]' || 
    q'[LPAD(NVL(CD.CONTROL_NO, '0'), 19, '0') AS ClientReference, ]' || 
    q'[TO_CHAR(NVL(CD.CHECK_DATE, LPAD(' ', 8, ' ')), 'YYYYMMDD') AS IssueDate, ]' || 
    q'[RPAD(NVL(CD.NAME, ' '), 60, ' ') AS PayeeName1, ]' || 
    q'[RPAD(NVL(CD.ADDR_1, ' '), 60, ' ') AS AddressLine1, ]' || 
    q'[RPAD(NVL(CD.ADDR_2, ' '), 60, ' ') AS AddressLine2, ]' || 
    q'[RPAD(NVL(CD.CITY, '') || CASE WHEN CD.CITY IS NULL OR CD.STATE IS NULL THEN ' ' ELSE ', ' END || NVL(CD.STATE, ''), 60, ' ') AS AddressLine4, ]' || 
    q'[RPAD(NVL(CD.ZIPCODE, ' '), 60, ' ') AS AddressLine5, ]' || 
    q'[TO_CHAR(CD.CREATE_DATE, 'YYYYMMDDHH24MISS') AS DateCreated, ]' || 
    q'[CASE WHEN CD.VOID_DATE IS NULL THEN ' ' ELSE TO_CHAR(CD.VOID_DATE, 'YYYYMMDDHH24MISS') END AS DateVoided ]' || 
    q'[FROM BANK_ACCOUNT BA ]' || 
    q'[INNER JOIN CASH_DISBURSEMENT CD ON BA.ID = CD.BANK_ACCOUNT_ID ]' || 
    q'[WHERE BA.ACCOUNT_NO IS NOT NULL AND CD.CHECK_NO > 0 ]' || 
    q'[AND CD.STATUS != 'X' ]' || 
    q'[AND CD.AMOUNT != 0 ]' || 
    q'[AND ((TO_NUMBER(TO_CHAR(CD.CREATE_DATE, 'YYYYMMDDHH24MISS'))]' || IN_DATE_OPERATOR || ':1') OR ' || 
    q'[(CASE WHEN CD.VOID_DATE IS NULL THEN 0 ELSE TO_NUMBER(TO_CHAR(CD.VOID_DATE, 'YYYYMMDDHH24MISS')) END]' || IN_DATE_OPERATOR || ':2')) ' || 
    q'[ORDER BY BA.BRANCH_ID, BA.ACCOUNT_NO ]'; 
    OPEN out_data 
    FOR l_sql_stmt 
    USING in_date, in_date; 
END RF_SP_STFCA_PositivePay; 
END RF_PKG_STFCA_PositivePay; 
+0

Wie würde das mit meinem derzeitigen Verfahren funktionieren? Es sieht so aus, als würden Sie vorschlagen, dass ich die gesamte Abfrage in ein Varchar2 umwandle und dann den Befehl open darauf verwende? Ich schätze den Code, den Sie zur Verfügung gestellt haben, aber er wäre viel nützlicher in einem Arbeitsbeispiel mit dem von mir bereitgestellten Code, einschließlich der lokalen Variablen, des Ausgabekursors und so weiter (es gibt hier viel mehr Arbeit als eine einfache select-Anweisung). – Adamantine

+0

@Adamantine - Meine Antwort wurde mit dem Versuch aktualisiert, die Zeichenfolge zu erstellen, die Sie benötigen. Es gibt eine gute Chance, dass ich es nicht komplett richtig gebaut habe, da ich es nicht wirklich testen kann. –

+0

Was macht ": =" das "=" nicht? – Adamantine