oracle 如何编写PL/SQL来确定要执行的SQL DML操作

ibps3vxo  于 2023-04-11  发布在  Oracle
关注(0)|答案(2)|浏览(151)

我想写一个PL/SQL过程,它接受一个SQL字符串作为参数,并确定SQL语句是SELECT、INSERT、UPDATE或DELETE语句还是OTHER,如果以上都不是(例如,如果它是一个DDL语句)。或者它可能返回DDL,但仍然需要一个“OTHER”。
在任何情况下,因为SQL可以,当然,变得非常复杂,变化很大,我希望唯一的方法是利用Oracle内置的SQL解析引擎,如果这个功能是公开的。我一直在寻找通过DBMS_SQL库的方法,但我想我会看看是否有人在这方面有经验。

mbzjlibv

mbzjlibv1#

也许你可以使用解释计划表来得到它。像这样的东西:

CREATE OR REPLACE PROCEDURE GET_DML_OP(p_SQL IN VarChar2) AS 
BEGIN
    Declare
        mCmd        VarChar2(4000) := 'EXPLAIN PLAN SET STATEMENT_ID = ''proc_get_dml_op_'' FOR ' || p_SQL;
        mRow        NUMBER(3) := 0;
        CURSOR c IS 
          select PLAN_TABLE_OUTPUT from table(dbms_xplan.display('plan_table', 'proc_get_dml_op_'));
        mExplain    VarChar2(4000);
    Begin
        Begin
            Execute Immediate mCmd;
        Exception
            When OTHERS Then 
                DBMS_OUTPUT.PUT_LINE('OTHER  OR  *** SQLERRM: ' || SQLERRM || ' ***');
                DELETE FROM PLAN_TABLE WHERE STATEMENT_ID = 'proc_get_dml_op_';
        End;
        For r In c LOOP
            mRow := mRow + 1;
            If mRow <= 8 THEN
                mExplain := TRIM(SubStr(r.PLAN_TABLE_OUTPUT, InStr(r.PLAN_TABLE_OUTPUT, '|', 1, 2) + 1, InStr(r.PLAN_TABLE_OUTPUT, '|', 1, 3) - InStr(r.PLAN_TABLE_OUTPUT, '|', 1, 2) - 1  ));
                If SubStr(mExplain, 1, 6) IN('INSERT', 'UPDATE', 'DELETE', 'SELECT', 'MERGE ') THEN
                    DBMS_OUTPUT.PUT_LINE(mExplain);
                    DELETE FROM PLAN_TABLE WHERE STATEMENT_ID = 'proc_get_dml_op_';
                    GoTo EndIt;
                End If;
            End If;
        END LOOP;
        DELETE FROM PLAN_TABLE WHERE STATEMENT_ID = 'proc_get_dml_op_';
      <<EndIt>>
        Null;
    End;
END GET_DML_OP;

如果用一些sql语句调用过程。。

SET SERVEROUTPUT ON
BEGIN
    GET_DML_OP('SELECT * FROM A_TABLE_NAME');
    GET_DML_OP('WITH a AS (Select 1 "NMBR", ''A'' "LTTR" FROM DUAL) Select * From a');
    --
    GET_DML_OP('UPDATE A_TABLE_NAME SET PATH = ''999'' Where PATH Is Null');
    GET_DML_OP('INSERT INTO A_TABLE_NAME VALUES(''xxx'', ''yy'', ''zzz'')');
    GET_DML_OP('DELETE FROM A_TABLE_NAME WHERE PATH Is Null');
    --
    GET_DML_OP('ALTER TABLE A_TABLE_NAME MODIFY(PATH VarChar2(50))');
    GET_DML_OP('MERGE INTO emp e USING dept d ON (e.deptno = d.deptno) WHEN MATCHED THEN UPDATE SET e.COMM = null');
END;
/

R e s u l t :
anonymous block completed
SELECT STATEMENT
SELECT STATEMENT
UPDATE STATEMENT
INSERT STATEMENT
DELETE STATEMENT
OTHER  OR  *** SQLERRM: ORA-00900: invalid SQL statement ***
MERGE STATEMENT
btqmn9zl

btqmn9zl2#

我的开源程序PLSQL_LEXER是为了对SQL语句进行分类而构建的。下面是一个简单的例子:

select statement_classifier.get_command_name('/**/((select * from dual))') command_name
from dual;

COMMAND_NAME
------------
SELECT

虽然这个程序没有实现一个完整的解析器,但它使用了一个由有限状态机构建的词法分析器,这个词法分析器足够强大,可以可靠地解决这类语言问题。它可以处理所有数千个保留关键字以及SQL和PL/SQL的许多奇怪的语言特性。
该程序提供了多种使用文档或数据字典中的Oracle官方名称对语句进行分类的方法。下面是一个对Oracle语句进行完全分类的更复杂的示例。
如果你仍然担心可靠性,这个程序有几个hundred test cases,它已经成功地在许多生产系统上分类了数百万条语句。它也适用于一些没有文档的命令,尽管我确实需要升级它,以包含一些新的21 c命令
该程序还有其他一些包,如果你试图创建类似于自己的SQL小提琴的东西,这些包可能会很有用。例如,如果你正在构建一个用户输入命令并由系统运行的界面,这个例子展示了如何拆分语句,对它们进行分类,准备运行它们,以及根据语句类型获得适当的反馈消息。

相关问题