SQL 执行

执行 SQL 语句是 Python 应用程序与 神通 数据库通信的主要方式。语句使用 Cursor.execute() 或 Cursor.executemany() 方法执行。语句包括查询、数据操作语言 (DML) 和数据定义语言 (DDL)。还可以执行其他一些专业语句。

STPython可用于执行单个语句,一次执行一个。它不会读取 SQL*Plus“.sql”文件。 SQL 语句尾部是否有分号 (“;”)不影响SQL语句执行。

cur.execute("select * from MyTable;")

cur.execute("select * from MyTable")

SQL 查询

查询(以 SELECT 或 WITH 开头的语句)只能使用方法 Cursor.execute() 执行。然后可以迭代行,也可以使用Cursor.fetchone(),Cursor.fetchmany()或Cursor.fetchall()之一来读取行,有一个默认类型映射到Python类型。

提示:将用户数据与 SQL 语句进行插值或串联是一种安全风险,会影响性能。请改用绑定变量。例如:

cursor.execute("SELECT * FROM mytab WHERE mycol = '" + myvar + "'")

改成

cursor.execute("SELECT * FROM mytab WHERE mycol = :mybv", mybv=myvar)

获取数据

在 Cursor.execute()之后,为方便起见,将返回游标。这允许代码循环访问如下行:

cursor = connection.cursor()
for row in cursor.execute("select * from MyTab"):
    print(row)

也可以使用Cursor.fetchone()方法一次读取一个行:

cur = connection.cursor()
cur.execute("select * from MyTab")
while True:
    row = cur.fetchone()
    if row is None:
        break
    print(row)

如果需要批量处理行,则可以使用方法 Cursor.fetchmany()。批处理的大小由参数控制,该参数默认为 Cursor.arraysize 的值,Cursor.arraysize 默认为100。

如下例子中,fetchmany将取出3行数据。

cur = connection.cursor()
cur.execute("select * from MyTab")
rows = cur.fetchmany(3)
print(rows)

例子: fetchmany一次取出10条,循环取完所有数据

cur = connection.cursor()
cur.execute("select * from MyTable")
num_rows = 10
while True:
    rows = cur.fetchmany(num_rows)
    if not rows:
        break
    for row in rows:
        print(row)

如果需要读取所有行,并且可以包含在内存中,则可以使用方法 Cursor.fetchall()。

cur = connection.cursor()
cur.execute("select * from MyTable")
rows = cur.fetchall()
for row in rows:
    print(row)

提取方法将数据作为元组返回。

关闭游标

游标可用于执行多个语句。一旦不再需要它,就应该通过调用 close() 来关闭它,以便回收数据库中的资源。当引用它的变量超出范围时,它将自动关闭(并且不保留进一步的引用)。控制游标生存期的另一种方法是使用“with”块,这可确保在块完成后关闭游标。

例如:

with connection.cursor() as cursor:
    for row in cursor.execute("select * from MyTable"):
        print(row)

此代码确保在块完成后,游标关闭,资源已由数据库回收。此外,任何在块外部使用变量的尝试都将失败。

查询列元数据

执行查询后,可以使用 Cursor.description 获取列元数据,如列名和数据类型:

cur = connection.cursor()
cur.execute("select * from MyTable")
for column in cur.description:
    print(column)

执行上面代码,可能得到如下结果:

('MYCOL', <class 'STPython.STRING'>, 400, 1600, None, None, 1)

数据类型

STPython数据类型与神通数据库数据类型对比如下表:

STPython类型 数据库数据类型 Python类型
STPython.BOOLEAN SMALLINT bool
STPython.NUMBER INT Int
STPython.NUMBER FLOAT Float
STPython.String VARCHAR(n) string
STPython.TIME TIME time
STPython.NUMBER NUMERIC float
STPython.DATETIME DATE datetime.date
STPython.DATETIME DATE datetime.datetime
STPython.BINARY BINARY Bytes
STPython.CLOB CLOB string
STPython.BLOB BLOB Bytes
STPython.TIMESTAMP TIMESTAMP datetime.datetime

STPython定义数据类型时,有些也支持python原生的类型,如int型:

var = cursor.var(int)
var.setvalue(0,5)
print(var)

等同于

var = cursor.var(STPython.NUMBER)
var.setvalue(0,5)
print(var)

支持python原生类型,如datetime类型:

import datetime
var = cursor.var(datetime.date)
var.setvalue(0,datetime.date(2007, 5, 18))
print(var)

使用输出类型处理程序更改提取的数据类型

有时,必须更改从 神通 数据库类型到 Python 类型的默认转换,以防止数据丢失或满足 Python 应用程序的用途。在这种情况下,可以为查询指定输出类型处理程序。输出类型处理程序不会影响从 Cursor.callfunc() 或 Cursor.callproc() 返回的值。 可以在连接或游标上指定输出类型处理程序。如果在游标上指定,则仅在该特定游标上更改读取类型处理。如果在连接上指定,则该连接创建的所有游标都将更改其读取类型处理。 输出类型处理程序应为具有以下签名的函数:

handler(cursor, name, defaultType, size, precision, scale)

这些参数与在 Cursor.description 中找到的查询列元数据的信息相同。该函数为要提取的每一列调用一次。该函数应返回一个变量对象(通常通过调用 Cursor.var())或值 。该值指示应使用默认类型。handler分为inputtypehandler和outputtypehandler,inputtypehandler和outputtypehandler不自定义情况下默认都为None。

如下例子,使用outputtypehandler 确保在获取某些数字时不会丢失数值精度:

import decimal

def number_to_decimal(cursor, name, default_type, size, precision, scale):
    if default_type == STPython.NUMBER:
        return cursor.var(decimal.Decimal, arraysize=cursor.arraysize)
cur = connection.cursor()
cur.outputtypehandler = number_to_decimal
cur.execute("select * from test_float")
val, = cur.fetchone()
print(val, "* 3 =", val * 3)

例子: 定义Building类、input_type_handler和output_type_handler:

class Building:
    def __init__(self, building_id, description, num_floors, date_built):
        self.building_id = building_id
        self.description = description
        self.num_floors = num_floors
        self.date_built = date_built
    def __repr__(self):
        return "<Building %s: %s>" % (self.building_id, self.description)

def input_type_handler(cursor, value, num_elements):
    if isinstance(value, Building):
        return cursor.var(obj_type, arraysize=num_elements,
                          inconverter=building_in_converter)
def output_type_handler(cursor, name, default_type, size, precision, scale):
        return cursor.var(obj_type, arraysize=cursor.arraysize,
                          outconverter=building_out_converter)

自定义inputtypehandler :

buildings = [
    Building(1, "The First Building", 5, datetime.date(2007, 5, 18)),
    Building(2, "The Second Building", 87, datetime.date(2010, 2, 7)),
    Building(3, "The Third Building", 12, datetime.date(2005, 6, 19)),
]
cur = con.cursor()
cur.inputtypehandler = input_type_handler
for building in buildings:
    try:
        cur.execute("insert into TestBuildings values (:1, :2)",
                (building.building_id, building))
    except STPython.DatabaseError as e:
        error, = e.args
        print("CONTEXT:", error.context)
        print("MESSAGE:", error.message)
        raise

自定义outputtypehandler :

cur = con.cursor()
cur.outputtypehandler = output_type_handler
print("WITH OUTPUT TYPE HANDLER:")
for row in cur.execute("select * from TestBuildings order by BuildingId"):
    print(row)

使用outconverter更改查询结果

def out_converter(value):
    if value is None:
        return ''
    return value
def output_type_handler(cursor, name, default_type, size, precision, scale):
    if default_type in (STPython.STRING, ):
        return cursor.var(str, size, arraysize=cur.arraysize,
                          outconverter=out_converter)
connection.outputtypehandler = output_type_handler

使用rowfactories更改查询结果

Cursor.rowfactory()是为从数据库中检索的每一行调用的方法。Cursor.rowfactory() 方法使用通常从数据库返回的元组调用。该方法可以将元组转换为其他值,并将其返回到应用程序以代替元组。 例如,要将查询的每一行作为字典来获取:

cursor.execute("select * from mytab")
columns = [col[0] for col in cursor.description]
cursor.rowfactory = lambda *args: dict(zip(columns, args))
data = cursor.fetchone()
print(data)

输出为字典格式:

{'MYCOL': 'aaa', 'COLUMN1': 112, 'BOOLEN': 1}

可滚动光标

可滚动游标使应用程序能够向后、向前移动、跳过行以及移动到查询结果集中的特定行。结果集将缓存在数据库服务器上,直到游标关闭。相反,常规游标仅限于向前移动。 可滚动游标是通过在创建游标时设置参数来创建的。方法 Cursor.scroll() 用于移动到结果集中的不同位置。scrollable=True

cursor = connection.cursor(scrollable=True)
cursor.execute("select * from mytab order by column1")

cursor.scroll(mode="last")
print("LAST ROW:", cursor.fetchone())
cursor.scroll(mode="first")
print("FIRST ROW:", cursor.fetchone())
cursor.scroll(8, mode="absolute")
print("ROW 8:", cursor.fetchone())
cursor.scroll(2)
print("SKIP 6 ROWS:", cursor.fetchone())
cursor.scroll(-4)
print("SKIP BACK 4 ROWS:", cursor.fetchone())

插入和更新语句

SQL 数据操作语言语句 (DML)(如 INSERT 和 UPDATE)可以通过STPython轻松执行。例如:

cursor = connection.cursor()
cursor.execute("insert into MyTab values (:a, :b)", ["Fredico",15])
connection.commit()

不要将用户数据连接或插值到 SQL 语句中。请参阅改用绑定变量。 有关提交和回滚数据更改的最佳做法,请参阅事务管理。 处理多个数据值时,请使用 executemany() 来提高性能。请参见批处理语句执行和大容量加载 例如:

createSql = """
CREATE TABLE test_rowid
(
    ID integer NOT NULL AUTO_INCREMENT,
    A character varying(50) ,
    B integer ,
    CONSTRAINT test_rowid_PKEY PRIMARY KEY (ID)
)
"""
cursor.execute(createSql)
rows = [
    ["First",  1],
    ["Second", 2],
    ["Third",  3],
    ["Fouth",  4],
    ["Fifth",  5]
]

cursor.executemany("insert into test_rowid(a,b) values(:1,:2)",rows)
connection.commit()

获取插入数据的lastInsertId

createSql = """
CREATE TABLE test_rowid
(
    ID integer NOT NULL AUTO_INCREMENT,
    A character varying(50) ,
    B integer ,
    CONSTRAINT test_rowid_PKEY PRIMARY KEY (ID)
)
"""
cursor.execute("insert into test_rowid(a,b) values(:1,:2)",["First", 1])
print(cursor.lastrowid)

输出: 自增id的值

PL/SQL 存储过程

Cursor.callproc() 方法用于调用 PL/SQL 过程。
cursor = connection.cursor()

proc = """
create or replace procedure myproc (
    a_Value1                            number,
    a_Value2                            out number
) as
begin
    a_Value2 := a_Value1 * 2;
end;
"""
cursor.execute(proc)

out_val = cursor.var(int)
cursor.callproc('myproc', [123, out_val])
print(out_val.getvalue())

调用 Cursor.callproc() 实际上会生成一个匿名的 PL/SQL 块,cursor.callproc('myproc', [123, out_val])相当于:

cursor.execute("begin myproc(:1,:2); end;", [123, out_val])

有关绑定的信息,请参阅使用绑定变量。

PL/SQL 存储函数

Cursor.callfunc() 方法用于调用 PL/SQL 函数。 callfunc() 的参数应为 Python 类型、STPython类型之一或对象类型

myfunc= """
create or replace function myfunc (
    a_StrVal varchar2,
    a_NumVal number
) return number as
begin
    return length(a_StrVal) + a_NumVal * 2;
end;
"""
cursor.execute(myfunc)

return_val = cursor.callfunc("myfunc", int, ["a string", 15])
print(return_val)