从Oracle调用Python

时间:2014-03-21 16:40:16

标签: python sql oracle pandas cx-oracle

是否可以在Oracle过程中调用Python?我已经阅读了很多关于相反案例的文献(从Python调用Oracle SQL),但不是相反。

我想要做的是让Oracle生成一个数据库表,然后我想调用Python并在DataFrame中将这个数据库表传递给它,以便我可以使用Python对它做一些事情并产生结果。我可能需要在Oracle过程中多次调用Python。有谁知道这是否可能,怎么可能呢?

7 个答案:

答案 0 :(得分:3)

嗯,答案很多,有一些非常好的选择,但让我尝试提出另一个答案。

让我们想象一下这种情况:

  • 我提到了一组python程序,它们以不同的方式与数据交互,您提到了数据框架。
  • 我有一个很大的Oracle过程,在运行时需要运行python脚本,因此基本上我需要在Oracle PL / SQL内部使用Python,除非您使用外部库或Java代码(已经提供了示例),否则这是不可能的

您始终可以做的就是使用PL/SQL的API从DBMS_SCHEDULER调用SHELL SCRIPTS。这些shell脚本可以调用您想要的任何东西,在本例中为Python程序。

我的情况如下:

  • 一个运行该函数的python程序获取sys_refcursor变量的结果集。
  • 一个Oracle过程通过通用shell脚本调用这些Python程序

让它起作用

SQL> create table t_python ( c1 number generated by default on null as identity ( start with 1 increment by 1 ) ,
                        c2 varchar2(10) ,
                        c3 date
                       ) ;             

Table created.

SQL> declare
begin
   for r in 1..10
   loop
      insert into t_python values ( null , dbms_random.string('A','5') , sysdate - round(dbms_random.value(1,100),0) );
          commit ;
   end loop;
end;
/  

PL/SQL procedure successfully completed.

SQL> select * from t_python
  2  ;

        C1 C2         C3
---------- ---------- ---------
         1 Anrio      14-JUL-20
         2 ouaTA      04-MAY-20
         3 Swteu      06-JUL-20
         4 kdsiZ      24-MAY-20
         5 PXxbS      14-MAY-20
         6 xQFYY      18-JUN-20
         7 oahQR      09-MAY-20
         8 ZjfXw      24-MAY-20
         9 AmMOa      26-JUL-20
        10 IQKpK      25-JUL-20

10 rows selected.

SQL>

因此,假设我在数据库中有一个函数,该函数返回一个SYS_REFCURSOR对象,即一个集合或数据集。

SQL> CREATE OR REPLACE FUNCTION get_result_table_f RETURN SYS_REFCURSOR
AS
   r_python SYS_REFCURSOR;
BEGIN
   OPEN r_python FOR 
   SELECT 
      c1,
      c2,
      c3
   FROM 
      t_python 
    ORDER BY 
         c1,   
         c2,
         c3;

   RETURN r_python;
END;
/

Function created

如果我在python程序中调用此函数,则效果很好。

import cx_Oracle
import pandas as pd

conn = cx_Oracle.connect('user/pwd@hostname:port/servicename')
cur = conn.cursor()

refCursor = cur.callfunc('get_result_table_f', cx_Oracle.CURSOR, [])
for row in refCursor:
    print(row)

Result

$ /usr/bin/python3.6 /home/myuser/testcursor.py
(1, 'Anrio', datetime.datetime(2020, 7, 14, 12, 38, 52))
(2, 'ouaTA', datetime.datetime(2020, 5, 4, 12, 38, 52))
(3, 'Swteu', datetime.datetime(2020, 7, 6, 12, 38, 52))
(4, 'kdsiZ', datetime.datetime(2020, 5, 24, 12, 38, 52))
(5, 'PXxbS', datetime.datetime(2020, 5, 14, 12, 38, 52))
(6, 'xQFYY', datetime.datetime(2020, 6, 18, 12, 38, 52))
(7, 'oahQR', datetime.datetime(2020, 5, 9, 12, 38, 52))
(8, 'ZjfXw', datetime.datetime(2020, 5, 24, 12, 38, 52))
(9, 'AmMOa', datetime.datetime(2020, 7, 26, 12, 38, 52))
(10, 'IQKpK', datetime.datetime(2020, 7, 25, 12, 38, 52))

那么,如何在oracle过程中调用此python程序?

好吧,我的选择是使用DBMS_SCHEDULER的API,该API仅需要一个shell脚本来调用python程序。为了设置DBMS_SCHEDULER,您只需要:

  • 创建一个凭证,调度程序将使用该凭证来运行您的Shell。它必须是OS用户(在下面的示例中是ftpcpl)。
  • 使用调度程序作业类型EXTERNAL SCRIPT
  • 使用Shell脚本调用python程序(该python脚本必须与数据库位于同一服务器上。是否可以在另一台服务器上执行该操作,但是由于需要安装Oracle Scheduler代理而更加复杂)

这就是它的样子

create or replace procedure run_python_program 
as
v_job_count  pls_integer;
v_owner      varchar2(30);
v_job        varchar2(120) := 'MY_PYTHON_SCRIPT';
begin
    select count(*) into v_job_count from dba_scheduler_jobs where job_name = v_job ;
    if v_job_count > 0
    then
        DBMS_SCHEDULER.drop_job (job_name=> v_job , force => true);
    end if;

    DBMS_SCHEDULER.create_job
    (
        job_name             =>  v_job,
        job_type             => 'EXTERNAL_SCRIPT',
        job_action           => '/home/myuser/my_shell_script.sh `date +%Y%m%d`',
        credential_name      => 'ftpcpl',
        enabled              =>  FALSE
    );
    DBMS_SCHEDULER.run_job (job_name=> v_job, use_current_session => true);
exception when others then raise;
end;
/

您的shell脚本看起来很简单

#/bin/bash 
odate=$1
logfile=/home/myuser/logfile_$odate.txt 
/usr/bin/python3.6 /home/myuser/testpython.py >> $logfile

运行过程

SQL> begin
     run_python_program; 
     end;
     /

 PL/SQL procedure successfully completed. 


 SQL> host cat /home/test/logfile_20200809.txt
    (1, 'Anrio', datetime.datetime(2020, 7, 14, 12, 38, 52))
    (2, 'ouaTA', datetime.datetime(2020, 5, 4, 12, 38, 52))
    (3, 'Swteu', datetime.datetime(2020, 7, 6, 12, 38, 52))
    (4, 'kdsiZ', datetime.datetime(2020, 5, 24, 12, 38, 52))
    (5, 'PXxbS', datetime.datetime(2020, 5, 14, 12, 38, 52))
    (6, 'xQFYY', datetime.datetime(2020, 6, 18, 12, 38, 52))
    (7, 'oahQR', datetime.datetime(2020, 5, 9, 12, 38, 52))
    (8, 'ZjfXw', datetime.datetime(2020, 5, 24, 12, 38, 52))
    (9, 'AmMOa', datetime.datetime(2020, 7, 26, 12, 38, 52))
    (10, 'IQKpK', datetime.datetime(2020, 7, 25, 12, 38, 52))

摘要

请记住,我做了一个非常简单的测试,只是向您展示了如何从PL / SQL调用python(嵌入到shell脚本中)。实际上,您可以使该过程运行多个外部脚本(python程序),并且可以通过多种方式与数据进行交互。

例如,您可以执行以下操作:

  1. Oracle中的过程创建数据并将此数据存储在表,集合或sys_refcursor对象中。我可以使用DBMS_SCHEDULER EXTERNAL_SCRIPT作业类型在PL / SQL中调用python程序,并与数据进行交互。
  2. python从原始数据集中生成输出数据。我可以将表加载到python程序中,也可以将csv保留为外部表,可以从该过程中再次读取。

依此类推。

实际上,我在Shell脚本中有很多程序,这些程序是使用Oracle Scheduler Chains分步执行的。这些步骤之一实际上是一个python程序。我发现DBMS_SCHEDULER的API在您需要从PL / SQL中运行技术时非常有用,只要可以使用Shell脚本(或Windows中的cmd)来调用它们即可。

答案 1 :(得分:2)

你可以write stored procedures in Java而你可以use Java to run Python code,这样你就可以将两者结合起来实现你想要的目标。

答案 2 :(得分:2)

在边缘,有可能如何克服PL / SQL限制。 您可以在Database和Python程序之间设计特定的接口。 我想你可以使用Python的一个库来从网上获取一些数据。 然后使用C库与Oracle交换数据。

使用c库调用python - >数据文件 - >外部表 - >数据

注意:将其作为概念证明,或者更深入探索的起点。另外,我强烈反对你不要在生产中使用它。打破PL / SQL监狱来调用系统程序至少可以认为是不安全的。

所以这是如何进行的可能方式:

- == Prerequisities == -

pip install quandl

- == quandl.py == -

#!/usr/bin/python
import quandl
# World Bank Education Statistics
# Population, tertiary, total - Czech Republic
data = quandl.get("WEDU/CZE_SP_TER_TOTL_IN")
data.to_csv("/u01/data/data.txt")

- == exec.c == -

//
// gcc -Wall -fPIC -c exec.c
// gcc -shared -o exec.so exec.o
// mkdir -p /u01/lib
// cp exec.so /u01/lib
//

#include <stdlib.h>

int execute() {
  system("/u01/bin/get_data.py");
  return 0; // We want to make the compiler happy
}

- == LISTENER CONFIGURATION == -

SID_LIST_LISTENER =
...
  (SID_DESC =
...
    (ENVS="EXTPROC_DLLS=ANY")
    (PROGRAM = extproc)
...

- == DDL PART == -

create or replace library c_exec is '/u01/lib/exec.so';

create or replace procedure exec as external
  name "execute"
  library c_exec
  language c;
/

create directory pydata as '/u01/data';

create table data (
  "date" varchar2(14),
  "value" varchar2(32)
) organization external (  
  type oracle_loader
  default directory pydata
  access parameters ( 
    records delimited by newline
    nobadfile nodiscardfile nologfile
    fields terminated by ','
   ) location (pydata:'data.txt')
  );

--- === USAGE === ---

- ==下载处理数据== -

使用外部PL / SQL C库您可以调用python程序将结果存储到外部表的预期位置。

execute exec;  

- ==查询数据== -

select 
  to_date("date",'yyyy-mm-dd') "date", 
  to_number("value") "value" 
from data 
  where "date" != 'Date';

- ==结果== -

date           value
--------- ----------
31-DEC-70     886414
31-DEC-71     885549
31-DEC-72     877533
31-DEC-73     862859

答案 3 :(得分:1)

我想这是不可能的,因为PL / SQL是专为在Oracle服务器内快速执行而设计的,由于内部限制,这不是其他供应商的任意代码可能的地方。

OTOH您可以通过TCP通道从存储过程与另一台服务器进行交互,this page是指UTL_TCP包。在外部网络服务器中,您可以使用任何语言和任何逻辑。

答案 4 :(得分:1)

根据要使用Python的上下文,可以考虑 OML4Py

Oracle Machine Learning

主要优点:

数据库内处理:“移动算法,而不是数据!”-处理数据所在的位置以消除数据移动,并进一步利用Oracle环境作为具有并行功能的高性能计算引擎,分布式算法。

快速部署机器学习应用程序-由于数据库内机器学习模型是本机SQL函数,因此可以通过SQL和R脚本立即进行模型部署。


进一步阅读:

Oracle Machine Learning: Scaling R and Python for the Enterprise

幻灯片(回答此特定问题):

  • 第7页:数据访问,分析和探索
  • 第24页:创建 SQL中的用户定义函数(或R / Python中使用的函数)
  • 第25页: 从SQL调用用户定义的函数

Oracle Machine Learning Platform Move the algorithms, not the data!

答案 5 :(得分:0)

您可以对外部表使用预处理器功能,该功能允许您调用Python脚本以用数据填充外部表。可以在此OTN文章的“使用外部表”部分找到一个示例:https://community.oracle.com/docs/DOC-994731

答案 6 :(得分:0)

有点复杂但可能。我见过一次。您需要

  1. 在oracle数据库中创建一个javaclass。此类在包含它的目录中调用.py文件。
  2. 创建一个调用项目1的java类的过程。
  3. 在您的sql查询中,在需要时调用第2项的过程。