【问题标题】:How to execute a SQL script with cx_oracle如何使用 cx_oracle 执行 SQL 脚本
【发布时间】:2019-09-10 18:04:27
【问题描述】:

我正在尝试在 Oracle 数据库上执行 SQL 脚本(从文件中读取)。我尝试了很多方法,但都没有奏效。

我有以下方法:

def connect_cx_oracle():

    dns_tns = cx_Oracle.makedsn(config.DB_HOST, config.DB_PORT, service_name=config.DB_DBASE)
    con = cx_Oracle.connect(user=config.DB_USERNAME, password=config.DB_PASSWORD, dsn=dns_tns)
    cur = con.cursor()

    fd = open('PurgeProcess_2.sql')
    full_sql = fd.read()
    cur.prepare(full_sql)
    cur.executemany(None, full_sql)
    con.commit()

对文件使用 cur.execute 会导致错误。我不能用分号分割文件,因为它是一个脚本。

sql文件的代码是这样的(不发布整个文件)

SET SERVEROUTPUT ON SIZE UNLIMITED;
DECLARE

    CURSOR c_task IS SELECT job_id, task_info_id FROM task WHERE job_id <= myjob_id;

BEGIN

  dbms_output.ENABLE(NULL);
  dbms_output.put_line('*******************************');
  dbms_output.put_line('* Purge job started.......     ');
  dbms_output.put_line('*******************************');

        IF (FROM_date < 30) THEN
            dbms_output.put_line(' ');

        end IF;
    EXCEPTION
        WHEN NO_DATA_FOUND THEN
            dbms_output.put_line(' ');
            dbms_output.put_line('*******************************');
            dbms_output.put_line('* Purge job completed.......   ');
            dbms_output.put_line('*******************************');
            RAISE NO_ITEMS_TO_PROCESS;
    END;

    FOR i IN 1..myjobIDnumber.COUNT
    LOOP
        BEGIN
            DELETE FROM job WHERE id = myjobIDnumber(i);
            IF SQL%ROWCOUNT > 0 THEN
                deletecount4 := deletecount4 + SQL%ROWCOUNT;
            END IF;
        END;
    END LOOP;

    dbms_output.put_line('Job table records deleted. Number of records deleted is ' || deletecount4);
    dbms_output.put_line(' ');

    ROLLBACK;
    --COMMIT;

    dbms_output.put_line('*******************************');
    dbms_output.put_line('* Purge job completed.......  *');
    dbms_output.put_line('*******************************');
EXCEPTION
    WHEN NO_ITEMS_TO_PROCESS THEN
    NULL;
    WHEN DATE_LIMIT_APPROACHED THEN
    NULL;
END;

如果我使用 cur.execute(fileName) 我会得到

cx_Oracle.DatabaseError: ORA-00922: missing or invalid option

使用 executemany 我得到了

TypeError: parameters should be a list of sequences/dictionaries or an integer specifying the number of times to execute the statement

我不是一个真正的 sql 人,任何帮助将不胜感激。问候

【问题讨论】:

    标签: python cx-oracle


    【解决方案1】:

    cx_Oracle 和其他类似的驱动程序一次只能execute one statement。即使executemany() 也执行一条语句(但有许多数据值)。

    做你想做的最简单的方法是:

    • 从您的 SQL 文件中删除所有 SQL*Plus 特定命令,例如 SET,因为如果 cx_Oracle 尝试执行它们,DB 将无法理解它们。它们对 cx_Oracle 毫无意义。

    • 始终使用斜线(而不是分号)来终止 SQL 命令(以及 PL/SQL,其中当然总是需要斜线)

    • 编写一个类似RunSqlScript 的方法,从您的 SQL 文件中读取每个语句并执行它。

    一个更明智的解决方案是远离 SQL 文件,并在 cx_Oracle 中编写所有语句。

    【讨论】:

      【解决方案2】:

      在玩了很多库之后,我最终使用 Python 的子进程来解决这个问题。发布代码,因为它可能会在未来帮助某人。

      def connect_to_db_and_execute(timeout):
          proc = subprocess.Popen(f'sqlplus {config.DB_USERNAME}/{config.DB_PASSWORD}@{config.DB_HOST}:{config.DB_PORT}/'
                                  f'{config.DB_DBASE} @C:/pathtoSql/PurgeProcess_2.sql', stdout=PIPE, stderr=PIPE, universal_newlines=True)
          timer = Timer(timeout, proc.kill)
          try:
              timer.start()
              stdout,stderr = proc.communicate()
          finally:
              timer.cancel()
          (output, err) = proc.communicate()
          print('output is ', output)
          if err:
              # iF there is an error, print and sent it via email
              print(err)
              send_email(email, pword, to_recipients, subject, err)```
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-12-11
        • 2012-03-20
        • 1970-01-01
        • 2019-06-23
        • 1970-01-01
        • 1970-01-01
        • 2010-10-13
        • 1970-01-01
        相关资源
        最近更新 更多