使用 c# 代码中的函数执行 oracle DDL 脚本

Execute oracle DDL script with functions from c# code

我使用 Oracle 数据库用 C# 代码编写了一些集成测试。测试项目有一个 CreateDatabase.sql,其中包含在每次测试执行时创建整个数据库的 DDL。

当我只有序列和表时,我将此文件的内容拆分为 ";" 字符并分别执行每个创建语句,但现在我有一些函数,它们的语句包含一些 ; 字符,所以我不能再使用这种方法了。

我已经检查了 .NET / Oracle: How to execute a script with DDL statements programmatically 问题,但没有帮助。

1) 如果我尝试在单个 OracleCommand 中执行整个文件内容,我会收到错误 ORA-00911: invalid character 因为 ; 个字符

2) 如果我尝试将文件内容包装在 "begin {0} end;" 中,我会得到一个错误 PLS-00103: Encountered the symbol "CREATE" when expecting one of the following: ...

3) 我可以尝试解析 SQL 文件并将每个语句放入 EXECUTE IMMEDIATE 中,但这会更难...

还有其他选择吗?

我正在使用 Oracle.DataAccess 版本 4.112.3.0 来执行命令。


编辑 @kevinsky 要求一个脚本,这里是一个简化的例子......整个脚本创建了数百个对象......

CREATE SEQUENCE SQ_ARAN_SQ_ARQUIVO_ANEXO;

CREATE OR REPLACE FUNCTION UFC_SPSW_DISP_COMPOSICAO(p_id_composicao IN NUMBER) RETURN NUMBER
IS
   retorno NUMBER:= 0;
   numeroItens NUMBER;
   temDefinicao BOOLEAN := false;

   CURSOR item_cur is
    select idc.itdc_sq_item_definicao_composi, idc.insu_sq_insumo, idc.comp_sq_composicao, idc.itdc_nr_coeficiente, idc.COMP_DS_COMPOSICAO, idc.comp_sq_composicao_pai
    from item_definicao_composicao idc;
  BEGIN  
    FOR item_rec IN item_cur LOOP
      temDefinicao := true;
      IF (item_rec.itdc_nr_coeficiente is null) THEN
        RETURN null;
      ELSE
        IF (item_rec.insu_sq_insumo is null) THEN
          numeroItens := UFC_SPSW_DISP_COMPOSICAO(nvl(item_rec.comp_sq_composicao_pai, item_rec.comp_sq_composicao));
        else
          retorno := retorno + 1;
        END IF;
      END IF;
    END LOOP;
    IF (temDefinicao = false) THEN
       RETURN 0;
    END IF;
    RETURN retorno;
  END;

CREATE SEQUENCE SQ_CALC_SQ_CALCULO;

这是一个想法。使前斜杠 /(单独在单独的一行中)成为终止脚本中每条语句的新标准方式,而不是依赖于分号。例如,您的示例脚本可能会变成:

CREATE SEQUENCE SQ_ARAN_SQ_ARQUIVO_ANEXO
/

CREATE OR REPLACE FUNCTION UFC_SPSW_DISP_COMPOSICAO(p_id_composicao IN NUMBER) RETURN NUMBER
IS
   retorno NUMBER:= 0;
   numeroItens NUMBER;
   temDefinicao BOOLEAN := false;

   CURSOR item_cur is
    select idc.itdc_sq_item_definicao_composi, idc.insu_sq_insumo, idc.comp_sq_composicao, idc.itdc_nr_coeficiente, idc.COMP_DS_COMPOSICAO, idc.comp_sq_composicao_pai
    from item_definicao_composicao idc;
  BEGIN  
    FOR item_rec IN item_cur LOOP
      temDefinicao := true;
      IF (item_rec.itdc_nr_coeficiente is null) THEN
        RETURN null;
      ELSE
        IF (item_rec.insu_sq_insumo is null) THEN
          numeroItens := UFC_SPSW_DISP_COMPOSICAO(nvl(item_rec.comp_sq_composicao_pai, item_rec.comp_sq_composicao));
        else
          retorno := retorno + 1;
        END IF;
      END IF;
    END LOOP;
    IF (temDefinicao = false) THEN
       RETURN 0;
    END IF;
    RETURN retorno;
  END;
/

CREATE SEQUENCE SQ_CALC_SQ_CALCULO
/

通过使用 /,如果您希望使用 SQL*Plus 运行 您的脚本仍然完全有效。但它现在的优点是在 C# 中按语句解析变得微不足道,因此您可以单独执行每个语句而不会出现分号问题。

我过去使用过这种技术并且效果很好。

(如果您不熟悉 Oracle SQL 脚本中斜线的使用,请阅读相关阅读:When do I need to use a semicolon vs a slash in Oracle SQL?。)

我使用正则表达式先行断言仅在 ; 后跟一些保留字(或在文件末尾)进行拆分。

例如:

var statements = Regex.Split(
    fileContent,
    @"\s*;\s*(?=(?:CREATE|ALTER|DROP|RENAME|TRUNCATE)\s|\s*$)",
    RegexOptions.IgnoreCase);

我遇到了同样的问题,已经解决了。 同时使用 'BEGIN END' 和 'EXECUTE IMMEDIATE'.

这是我的测试(成功案例)

begin
    EXECUTE IMMEDIATE 'create or replace procedure SP_JHKIM2 IS
        begin
            dbms_output.put_line(''ABC'');
        end;';
end;