database-smartools 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- database_smartools/__init__.py +18 -0
- database_smartools/api/__init__.py +18 -0
- database_smartools/api/etl.py +160 -0
- database_smartools/database/__init__.py +18 -0
- database_smartools/database/dameng_extend.py +65 -0
- database_smartools/database/db_handler.py +676 -0
- database_smartools/database/mysql_extend.py +396 -0
- database_smartools/database/oceanbase_extend.py +55 -0
- database_smartools/database/oracle_extend.py +27 -0
- database_smartools/database/postgre_extend.py +396 -0
- database_smartools/main.py +86 -0
- database_smartools/module/__init__.py +18 -0
- database_smartools/module/function.py +115 -0
- database_smartools/utils/__init__.py +18 -0
- database_smartools/utils/config.py +56 -0
- database_smartools/utils/debug.py +59 -0
- database_smartools/utils/file.py +42 -0
- database_smartools/utils/http.py +74 -0
- database_smartools/utils/logger.py +237 -0
- database_smartools/utils/output.py +31 -0
- database_smartools/utils/texter.py +186 -0
- database_smartools/utils/timer.py +52 -0
- database_smartools-0.0.1.dist-info/METADATA +80 -0
- database_smartools-0.0.1.dist-info/RECORD +27 -0
- database_smartools-0.0.1.dist-info/WHEEL +5 -0
- database_smartools-0.0.1.dist-info/licenses/LICENSE +13 -0
- database_smartools-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,676 @@
|
|
1
|
+
import sys
|
2
|
+
import threading
|
3
|
+
|
4
|
+
from sqlalchemy import create_engine
|
5
|
+
import oracledb
|
6
|
+
from utils import config, logger, texter
|
7
|
+
import numpy as np
|
8
|
+
import pandas as pd
|
9
|
+
import dmPython
|
10
|
+
from dbutils.persistent_db import PersistentDB
|
11
|
+
import mysql.connector
|
12
|
+
import psycopg2
|
13
|
+
from utils.timer import get_time, get_timediff
|
14
|
+
from database import dameng_extend as de
|
15
|
+
from database import oracle_extend as oe
|
16
|
+
from database import oceanbase_extend as obe
|
17
|
+
import re
|
18
|
+
import jaydebeapi
|
19
|
+
|
20
|
+
# 获取数据池
|
21
|
+
POOL_MAP = {}
|
22
|
+
|
23
|
+
def _init_lib():
|
24
|
+
if "db_lib" in config.MAP.keys():
|
25
|
+
lib_dir = config.MAP["db_lib"]
|
26
|
+
oracledb.init_oracle_client(lib_dir=lib_dir)
|
27
|
+
|
28
|
+
def _init_local_db():
|
29
|
+
get_pool_by_key('local')
|
30
|
+
|
31
|
+
def get_pool():
|
32
|
+
try:
|
33
|
+
pool = oracledb.SessionPool(
|
34
|
+
user=config.MAP["db_user"],
|
35
|
+
password=config.MAP["db_password"],
|
36
|
+
dsn=oracledb.makedsn(
|
37
|
+
config.MAP["db_host"], config.MAP["db_port"], config.MAP["db_name"]
|
38
|
+
),
|
39
|
+
min=2,
|
40
|
+
max=15,
|
41
|
+
increment=1,
|
42
|
+
threaded=False,
|
43
|
+
)
|
44
|
+
except Exception as e:
|
45
|
+
message = f"实例化数据库连接池失败,错误信息:{e}"
|
46
|
+
logger.error(message)
|
47
|
+
return None, message
|
48
|
+
return pool, ""
|
49
|
+
|
50
|
+
def get_url(db_key):
|
51
|
+
global POOL_MAP
|
52
|
+
scan_db_conf = f"""
|
53
|
+
SELECT user_name,pass_word,url FROM task_template_data_source WHERE db_key = :db_key
|
54
|
+
"""
|
55
|
+
params = {"db_key": db_key}
|
56
|
+
pool_map = POOL_MAP['local']
|
57
|
+
conn_map, message = get_conn_map(pool_map)
|
58
|
+
logger.debug(f"获取数据源信息,sql:{scan_db_conf}, params: {params}")
|
59
|
+
select_result, select_message = select_by_map(conn_map, scan_db_conf, params)
|
60
|
+
logger.debug(f"数据源信息:{select_result}")
|
61
|
+
if not select_result:
|
62
|
+
return None, select_message
|
63
|
+
|
64
|
+
db_conf = select_result["data"][0]
|
65
|
+
user_name = db_conf["user_name"]
|
66
|
+
pass_word = db_conf["pass_word"]
|
67
|
+
url = db_conf["url"]
|
68
|
+
|
69
|
+
url_map = texter.parse_jdbc_url(url)
|
70
|
+
url_map["user_name"] = user_name
|
71
|
+
url_map["pass_word"] = pass_word
|
72
|
+
url_map["url"] = url
|
73
|
+
logger.debug(f"数据源配置:{url_map}")
|
74
|
+
return url_map
|
75
|
+
|
76
|
+
def get_pool_by_key(db_key, refresh=False):
|
77
|
+
global POOL_MAP
|
78
|
+
pool = None
|
79
|
+
logger.debug(f"---------------{__name__}-------------------")
|
80
|
+
# print(POOL_MAP)
|
81
|
+
if not POOL_MAP and 'local' not in POOL_MAP.keys() and db_key != 'local':
|
82
|
+
logger.debug("初始化本地数据库")
|
83
|
+
try:
|
84
|
+
_init_lib()
|
85
|
+
except Exception as e:
|
86
|
+
message = f"初始化数据库连接池失败,错误信息:{e}"
|
87
|
+
logger.error(message)
|
88
|
+
get_pool_by_key('local', refresh=True)
|
89
|
+
|
90
|
+
if db_key not in POOL_MAP.keys() or refresh:
|
91
|
+
if db_key == "local":
|
92
|
+
url_map = {
|
93
|
+
"type": config.MAP["biz_db_type"],
|
94
|
+
"host": config.MAP["db_host"],
|
95
|
+
"port": config.MAP["db_port"],
|
96
|
+
"database": config.MAP["db_name"],
|
97
|
+
"user_name": config.MAP["db_user"],
|
98
|
+
"pass_word": config.MAP["db_password"],
|
99
|
+
"url": config.MAP.get("url")
|
100
|
+
}
|
101
|
+
logger.debug(f"本地数据库信息:{url_map}")
|
102
|
+
else:
|
103
|
+
url_map = get_url(db_key)
|
104
|
+
|
105
|
+
if url_map["type"] == "oracle":
|
106
|
+
try:
|
107
|
+
oracledb.defaults.prefetchrows = int(config.MAP["batch_size"]) + 1
|
108
|
+
oracledb.defaults.arraysize = int(config.MAP["batch_size"])
|
109
|
+
pool = PersistentDB(
|
110
|
+
creator=oracledb, # 使用链接数据库的模块
|
111
|
+
maxusage=None, # 一个链接最多被重复使用的次数,None 表示无限制
|
112
|
+
setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
|
113
|
+
ping=0,
|
114
|
+
# ping DM 服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it isrequested, 2 = when a cursor is created, 7 = always
|
115
|
+
closeable=False,
|
116
|
+
# 如果为 False 时, conn.close() 实际上被忽略,供下次使用,再线程关闭时,才会自动关闭链接。
|
117
|
+
threadlocal=None, # 表示 thread-local 数据的类。
|
118
|
+
user=url_map["user_name"], # 用户名
|
119
|
+
password=url_map["pass_word"], # 密码
|
120
|
+
dsn=oracledb.makedsn(
|
121
|
+
url_map["host"], url_map["port"], url_map["database"]
|
122
|
+
),
|
123
|
+
)
|
124
|
+
# pool = cx_Oracle.SessionPool(user=user_name,
|
125
|
+
# password=pass_word,
|
126
|
+
# dsn=cx_Oracle.makedsn(url_map['host'], url_map['port'],
|
127
|
+
# url_map['database']),
|
128
|
+
# min=2, max=15, increment=1, threaded=False)
|
129
|
+
except Exception as e:
|
130
|
+
message = f"{url_map['type']}实例化数据库连接池失败,错误信息:{e}"
|
131
|
+
logger.error(message)
|
132
|
+
|
133
|
+
return None, message
|
134
|
+
|
135
|
+
if url_map["type"] == "dameng":
|
136
|
+
try:
|
137
|
+
pool = PersistentDB(
|
138
|
+
creator=dmPython, # 使用链接数据库的模块
|
139
|
+
maxusage=None, # 一个链接最多被重复使用的次数,None 表示无限制
|
140
|
+
setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
|
141
|
+
ping=0,
|
142
|
+
# ping DM 服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it isrequested, 2 = when a cursor is created, 7 = always
|
143
|
+
closeable=False,
|
144
|
+
# 如果为 False 时, conn.close() 实际上被忽略,供下次使用,再线程关闭时,才会自动关闭链接。
|
145
|
+
threadlocal=None, # 表示 thread-local 数据的类。
|
146
|
+
host=url_map["host"], # 主机号
|
147
|
+
port=url_map["port"], # 端口号
|
148
|
+
user=url_map["user_name"], # 用户名
|
149
|
+
password=url_map["pass_word"], # 密码
|
150
|
+
)
|
151
|
+
except Exception as e:
|
152
|
+
message = f"{url_map['type']}实例化数据库连接池失败,错误信息:{e}"
|
153
|
+
logger.error(message)
|
154
|
+
|
155
|
+
return None, message
|
156
|
+
|
157
|
+
if url_map["type"] == "postgresql":
|
158
|
+
try:
|
159
|
+
pool = PersistentDB(
|
160
|
+
creator=psycopg2, # 使用链接数据库的模块
|
161
|
+
maxusage=None, # 一个链接最多被重复使用的次数,None 表示无限制
|
162
|
+
setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
|
163
|
+
ping=0,
|
164
|
+
# ping DM 服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it isrequested, 2 = when a cursor is created, 7 = always
|
165
|
+
closeable=False,
|
166
|
+
# 如果为 False 时, conn.close() 实际上被忽略,供下次使用,再线程关闭时,才会自动关闭链接。
|
167
|
+
threadlocal=None, # 表示 thread-local 数据的类。
|
168
|
+
host=url_map["host"], # 主机号
|
169
|
+
port=url_map["port"], # 端口号
|
170
|
+
database=url_map["database"], # 数据库名称
|
171
|
+
user=url_map["user_name"], # 用户名
|
172
|
+
password=url_map["pass_word"], # 密码
|
173
|
+
)
|
174
|
+
except Exception as e:
|
175
|
+
message = f"{url_map['type']}实例化数据库连接池失败,错误信息:{e}"
|
176
|
+
logger.error(message)
|
177
|
+
|
178
|
+
return None, message
|
179
|
+
|
180
|
+
if url_map["type"] == "mysql":
|
181
|
+
try:
|
182
|
+
pool = PersistentDB(
|
183
|
+
creator=mysql.connector, # 使用的 MySQL 驱动
|
184
|
+
maxusage=None, # 一个链接最多被重复使用的次数,None 表示无限制
|
185
|
+
setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
|
186
|
+
ping=0,
|
187
|
+
# ping DM 服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it isrequested, 2 = when a cursor is created, 7 = always
|
188
|
+
closeable=False,
|
189
|
+
# 如果为 False 时, conn.close() 实际上被忽略,供下次使用,再线程关闭时,才会自动关闭链接。
|
190
|
+
threadlocal=None, # 表示 thread-local 数据的类。
|
191
|
+
host=url_map["host"], # 数据库主机
|
192
|
+
port=url_map["port"], # 端口号
|
193
|
+
user=url_map["user_name"], # 用户名
|
194
|
+
password=url_map["pass_word"], # 密码
|
195
|
+
database=url_map["database"], # 数据库名称
|
196
|
+
autocommit=True, # 是否自动提交事务
|
197
|
+
charset="utf8mb4", # 字符集
|
198
|
+
)
|
199
|
+
|
200
|
+
except Exception as e:
|
201
|
+
message = f"{url_map['type']}实例化数据库连接池失败,错误信息:{e}"
|
202
|
+
logger.error(message)
|
203
|
+
|
204
|
+
return None, message
|
205
|
+
|
206
|
+
if url_map["type"] == "oceanbase":
|
207
|
+
try:
|
208
|
+
pool = PersistentDB(
|
209
|
+
creator=jaydebeapi, # 使用链接数据库的模块
|
210
|
+
maxusage=None, # 一个链接最多被重复使用的次数,None 表示无限制
|
211
|
+
setsession=[], # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
|
212
|
+
ping=0,
|
213
|
+
# ping DM 服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it isrequested, 2 = when a cursor is created, 7 = always
|
214
|
+
closeable=False,
|
215
|
+
# 如果为 False 时, conn.close() 实际上被忽略,供下次使用,再线程关闭时,才会自动关闭链接。
|
216
|
+
threadlocal=None, # 表示 thread-local 数据的类。
|
217
|
+
jclassname=config.MAP["driver"], # 数据库驱动类名
|
218
|
+
url=url_map["url"], # 数据库连接字符串
|
219
|
+
driver_args=[url_map["user_name"], url_map["pass_word"]], # 驱动参数
|
220
|
+
jars=config.MAP["jar_file"], # jar包位置
|
221
|
+
)
|
222
|
+
|
223
|
+
except Exception as e:
|
224
|
+
message = f"{url_map['type']}实例化数据库连接池失败,错误信息:{e}"
|
225
|
+
logger.error(message)
|
226
|
+
return None, message
|
227
|
+
|
228
|
+
POOL_MAP[db_key] = {"pool": pool, "db_type": url_map["type"]}
|
229
|
+
logger.debug(f"创建新的连接池,db_key: {db_key}")
|
230
|
+
else:
|
231
|
+
pool = POOL_MAP[db_key]["pool"]
|
232
|
+
logger.debug(f"使用已有连接池,db_key: {db_key}")
|
233
|
+
|
234
|
+
return pool, ""
|
235
|
+
|
236
|
+
|
237
|
+
def close_pool(pool):
|
238
|
+
pool.close()
|
239
|
+
logger.debug(f"关闭连接池")
|
240
|
+
|
241
|
+
|
242
|
+
def get_conn(pool):
|
243
|
+
message = ""
|
244
|
+
try:
|
245
|
+
conn = pool.acquire()
|
246
|
+
logger.debug(f"从连接池中获取数据库连接")
|
247
|
+
return conn, message
|
248
|
+
except Exception as e:
|
249
|
+
message = f"获取数据连接异常,错误信息:{e}"
|
250
|
+
logger.error(f"获取数据连接异常,错误信息:{e}")
|
251
|
+
return None, message
|
252
|
+
|
253
|
+
|
254
|
+
def get_conn_map(pool_map):
|
255
|
+
message = ""
|
256
|
+
conn_map = None
|
257
|
+
|
258
|
+
def get_connection():
|
259
|
+
nonlocal conn_map
|
260
|
+
try:
|
261
|
+
conn = pool_map["pool"].connection()
|
262
|
+
db_type = pool_map["db_type"]
|
263
|
+
cursor = conn.cursor()
|
264
|
+
conn_map = {"conn": conn, "db_type": db_type, "cursor": cursor}
|
265
|
+
logger.debug(f"从连接池中获取数据库连接和数据库类型")
|
266
|
+
except Exception as e:
|
267
|
+
nonlocal message
|
268
|
+
message = f"获取数据连接异常,错误信息:{e}"
|
269
|
+
logger.error(f"获取数据连接异常,错误信息:{e}")
|
270
|
+
|
271
|
+
# 假设超时时间为 10 秒,可根据实际情况调整
|
272
|
+
timeout = max(int(config.MAP.get('db_overtime')), 30)
|
273
|
+
thread = threading.Thread(target=get_connection)
|
274
|
+
thread.start()
|
275
|
+
thread.join(timeout)
|
276
|
+
|
277
|
+
if thread.is_alive():
|
278
|
+
message = f"获取数据连接超时,超时时间:{timeout} 秒"
|
279
|
+
logger.error(message)
|
280
|
+
raise TimeoutError(message)
|
281
|
+
|
282
|
+
return conn_map, message
|
283
|
+
|
284
|
+
def get_engine():
|
285
|
+
url = f"oracle+cx_oracle://{config.MAP['db_user']}:{config.MAP['db_password']}@{config.MAP['db_host']}:{config.MAP['db_port']}/?service_name={config.MAP['db_name']}"
|
286
|
+
engine = create_engine(
|
287
|
+
url,
|
288
|
+
pool_size=30,
|
289
|
+
max_overflow=20,
|
290
|
+
pool_timeout=30,
|
291
|
+
pool_recycle=1800,
|
292
|
+
pool_pre_ping=True,
|
293
|
+
)
|
294
|
+
|
295
|
+
return engine
|
296
|
+
|
297
|
+
|
298
|
+
def close_engine(engine):
|
299
|
+
engine.dispose()
|
300
|
+
|
301
|
+
|
302
|
+
def close_conn(pool, conn):
|
303
|
+
if isinstance(pool, dict):
|
304
|
+
pool = pool["pool"]
|
305
|
+
|
306
|
+
if isinstance(conn, dict):
|
307
|
+
conn = conn["conn"]
|
308
|
+
conn.close()
|
309
|
+
logger.debug(f"从连接池中释放数据库连接")
|
310
|
+
return conn
|
311
|
+
|
312
|
+
pool.release(conn)
|
313
|
+
logger.debug(f"从连接池中释放数据库连接")
|
314
|
+
return conn
|
315
|
+
|
316
|
+
|
317
|
+
def commit(conn):
|
318
|
+
if isinstance(conn, dict):
|
319
|
+
conn = conn["conn"]
|
320
|
+
conn.commit()
|
321
|
+
logger.debug(f"数据库连接-提交事务")
|
322
|
+
return
|
323
|
+
|
324
|
+
|
325
|
+
def rollback(conn):
|
326
|
+
if isinstance(conn, dict):
|
327
|
+
conn = conn["conn"]
|
328
|
+
try:
|
329
|
+
conn.rollback()
|
330
|
+
except Exception as e:
|
331
|
+
logger.warning(f"[数据库连接]回滚异常:{e}")
|
332
|
+
logger.debug(f"数据库连接-回滚事务")
|
333
|
+
return
|
334
|
+
|
335
|
+
|
336
|
+
def select(conn, sql, params=None):
|
337
|
+
result = None
|
338
|
+
message = ""
|
339
|
+
with conn.cursor() as cursor:
|
340
|
+
try:
|
341
|
+
logger.debug(f"[数据查询]sql: {sql}, 参数列表: {params}")
|
342
|
+
if params:
|
343
|
+
rs = cursor.execute(sql, params)
|
344
|
+
data = rs.fetchall()
|
345
|
+
result = {"data": data, "desc": cursor.description}
|
346
|
+
else:
|
347
|
+
rs = cursor.execute(sql)
|
348
|
+
data = rs.fetchall()
|
349
|
+
result = {"data": data, "desc": cursor.description}
|
350
|
+
|
351
|
+
except Exception as e:
|
352
|
+
message = f"[数据查询]异常:{e}"
|
353
|
+
logger.error(message)
|
354
|
+
return None, message
|
355
|
+
|
356
|
+
data = []
|
357
|
+
for row in result["data"]:
|
358
|
+
item_map = {}
|
359
|
+
for index, item in enumerate(row):
|
360
|
+
if result["desc"][index][1] == oracledb.DATETIME and item is not None:
|
361
|
+
item = item.strftime("%Y-%m-%d %H:%M:%S")
|
362
|
+
|
363
|
+
item_map[result["desc"][index][0].lower()] = item
|
364
|
+
data.append(item_map)
|
365
|
+
result["data"] = data
|
366
|
+
return result, message
|
367
|
+
|
368
|
+
|
369
|
+
def select_by_map(conn_map, sql, params=None):
|
370
|
+
result = None
|
371
|
+
message = ""
|
372
|
+
conn = conn_map["conn"]
|
373
|
+
db_type = conn_map["db_type"]
|
374
|
+
if db_type == "oceanbase":
|
375
|
+
sql, params = map_named_to_positional_params(sql, params)
|
376
|
+
# result, message = obe.execute_select(conn, sql, params)
|
377
|
+
# return result, message
|
378
|
+
|
379
|
+
with conn.cursor() as cursor:
|
380
|
+
try:
|
381
|
+
logger.debug(f"[数据查询]sql: {sql}, 参数列表: {params}")
|
382
|
+
if params:
|
383
|
+
cursor.execute(sql, params)
|
384
|
+
rs = cursor.fetchall()
|
385
|
+
result = {"data": rs, "desc": cursor.description}
|
386
|
+
else:
|
387
|
+
cursor.execute(sql)
|
388
|
+
rs = cursor.fetchall()
|
389
|
+
result = {"data": rs, "desc": cursor.description}
|
390
|
+
|
391
|
+
column_types = []
|
392
|
+
for desc in result["desc"]:
|
393
|
+
column_types.append(desc[1])
|
394
|
+
result["data_types"] = column_types
|
395
|
+
|
396
|
+
except Exception as e:
|
397
|
+
message = f"[数据查询]异常:{e}"
|
398
|
+
# logger.error(message)
|
399
|
+
try:
|
400
|
+
conn.rollback()
|
401
|
+
except:
|
402
|
+
logger.warning("[数据库新增]回滚异常:{e}")
|
403
|
+
raise
|
404
|
+
# return None, message
|
405
|
+
|
406
|
+
# print(result)
|
407
|
+
# 将result["desc"] 的达梦数据类型转成Python的数据类型
|
408
|
+
start_time = get_time()
|
409
|
+
# if result['desc'][index][1] == cx_Oracle.DATETIME and item is not None:
|
410
|
+
# item = item.strftime("%Y-%m-%d")
|
411
|
+
if db_type == "dameng":
|
412
|
+
result["data"] = de.formatter(result)
|
413
|
+
if db_type == "oracle":
|
414
|
+
result["data"] = oe.formatter(result)
|
415
|
+
if db_type == "oceanbase":
|
416
|
+
result["data"] = obe.formatter(result)
|
417
|
+
|
418
|
+
end_time = get_time()
|
419
|
+
logger.debug(f"重构数据用时:{get_timediff(end_time, start_time)}")
|
420
|
+
return result, message
|
421
|
+
|
422
|
+
|
423
|
+
def delete(conn, sql, params=None):
|
424
|
+
message = ""
|
425
|
+
with conn.cursor() as cursor:
|
426
|
+
try:
|
427
|
+
logger.debug(f"[数据删除]sql: {sql}, 参数列表: {params}")
|
428
|
+
if params:
|
429
|
+
cursor.execute(sql, params)
|
430
|
+
else:
|
431
|
+
cursor.execute(sql)
|
432
|
+
|
433
|
+
except Exception as e:
|
434
|
+
message = f"[数据删除]异常:{e}"
|
435
|
+
logger.error(message)
|
436
|
+
try:
|
437
|
+
conn.rollback()
|
438
|
+
except:
|
439
|
+
logger.warning("[数据库新增]回滚异常:{e}")
|
440
|
+
raise
|
441
|
+
# return False, message
|
442
|
+
|
443
|
+
return True, message
|
444
|
+
|
445
|
+
|
446
|
+
def delete_by_map(conn_map, sql, params=None):
|
447
|
+
message = ""
|
448
|
+
conn = conn_map["conn"]
|
449
|
+
db_type = conn_map["db_type"]
|
450
|
+
if db_type == "oceanbase":
|
451
|
+
sql, params = map_named_to_positional_params(sql, params)
|
452
|
+
|
453
|
+
with conn.cursor() as cursor:
|
454
|
+
try:
|
455
|
+
logger.debug(f"[数据删除]sql: {sql}, 参数列表: {params}")
|
456
|
+
if params:
|
457
|
+
cursor.execute(sql, params)
|
458
|
+
else:
|
459
|
+
cursor.execute(sql)
|
460
|
+
|
461
|
+
except Exception as e:
|
462
|
+
message = f"[数据删除]异常:{e}"
|
463
|
+
logger.error(message)
|
464
|
+
try:
|
465
|
+
conn.rollback()
|
466
|
+
except:
|
467
|
+
logger.warning("[数据库新增]回滚异常:{e}")
|
468
|
+
raise
|
469
|
+
# return False, message
|
470
|
+
|
471
|
+
return True, message
|
472
|
+
|
473
|
+
|
474
|
+
def update(conn, sql, params=None):
|
475
|
+
message = ""
|
476
|
+
with conn.cursor() as cursor:
|
477
|
+
try:
|
478
|
+
if params:
|
479
|
+
cursor.execute(sql, params)
|
480
|
+
else:
|
481
|
+
cursor.execute(sql)
|
482
|
+
|
483
|
+
logger.debug(f"[数据更新]sql: {sql}, 参数列表: {params}")
|
484
|
+
except Exception as e:
|
485
|
+
message = f"[数据更新]异常:{e}"
|
486
|
+
logger.debug(f"[数据更新]sql: {sql}, 参数列表: {params}")
|
487
|
+
logger.error(message)
|
488
|
+
try:
|
489
|
+
conn.rollback()
|
490
|
+
except:
|
491
|
+
logger.warning("[数据库新增]回滚异常:{e}")
|
492
|
+
raise
|
493
|
+
# return False, message
|
494
|
+
return True, message
|
495
|
+
|
496
|
+
|
497
|
+
def update_by_map(conn_map, sql, params=None):
|
498
|
+
message = ""
|
499
|
+
conn = conn_map["conn"]
|
500
|
+
db_type = conn_map["db_type"]
|
501
|
+
if db_type == "oceanbase":
|
502
|
+
result, message = map_named_to_positional_params(sql, params)
|
503
|
+
|
504
|
+
with conn.cursor() as cursor:
|
505
|
+
try:
|
506
|
+
if params:
|
507
|
+
cursor.execute(sql, params)
|
508
|
+
else:
|
509
|
+
cursor.execute(sql)
|
510
|
+
|
511
|
+
logger.debug(f"[数据更新]sql: {sql}, 参数列表: {params}")
|
512
|
+
except Exception as e:
|
513
|
+
message = f"[数据更新]异常:{e}"
|
514
|
+
logger.debug(f"[数据更新]sql: {sql}, 参数列表: {params}")
|
515
|
+
logger.info(message)
|
516
|
+
try:
|
517
|
+
conn.rollback()
|
518
|
+
except:
|
519
|
+
logger.warning("[数据库新增]回滚异常:{e}")
|
520
|
+
raise
|
521
|
+
# return False, message
|
522
|
+
return True, message
|
523
|
+
|
524
|
+
|
525
|
+
def insert(conn, sql, params=None):
|
526
|
+
message = ""
|
527
|
+
with conn.cursor() as cursor:
|
528
|
+
try:
|
529
|
+
logger.debug(f"[数据库新增]sql: {sql}, 参数列表: {params}")
|
530
|
+
if params:
|
531
|
+
cursor.execute(sql, params)
|
532
|
+
else:
|
533
|
+
cursor.execute(sql)
|
534
|
+
|
535
|
+
except Exception as e:
|
536
|
+
message = f"[数据库新增]异常:{e}"
|
537
|
+
logger.error(message)
|
538
|
+
try:
|
539
|
+
conn.rollback()
|
540
|
+
except:
|
541
|
+
logger.warning("[数据库新增]回滚异常:{e}")
|
542
|
+
raise
|
543
|
+
# return False, message
|
544
|
+
|
545
|
+
return True, message
|
546
|
+
|
547
|
+
|
548
|
+
def insert_by_map(conn_map, sql, params=None):
|
549
|
+
message = ""
|
550
|
+
conn = conn_map["conn"]
|
551
|
+
db_type = conn_map["db_type"]
|
552
|
+
if db_type == "oceanbase":
|
553
|
+
sql, params = map_named_to_positional_params(sql, params)
|
554
|
+
|
555
|
+
with conn.cursor() as cursor:
|
556
|
+
try:
|
557
|
+
if db_type == "oceanbase":
|
558
|
+
set_sql = "set session nls_timestamp_format='YYYY-MM-DD HH24:MI:SS.XFF3'"
|
559
|
+
cursor.execute(set_sql)
|
560
|
+
set_sql = "set session nls_date_format='YYYY-MM-DD'"
|
561
|
+
cursor.execute(set_sql)
|
562
|
+
|
563
|
+
logger.debug(f"[数据库新增]sql: {sql}, 参数列表: {params}")
|
564
|
+
if params:
|
565
|
+
cursor.execute(sql, params)
|
566
|
+
else:
|
567
|
+
cursor.execute(sql)
|
568
|
+
|
569
|
+
except Exception as e:
|
570
|
+
message = f"[数据库新增]异常:{e}"
|
571
|
+
logger.error(message)
|
572
|
+
try:
|
573
|
+
conn.rollback()
|
574
|
+
except:
|
575
|
+
logger.warning("[数据库新增]回滚异常:{e}")
|
576
|
+
raise
|
577
|
+
# return False, message
|
578
|
+
|
579
|
+
return True, message
|
580
|
+
|
581
|
+
|
582
|
+
def insert_by_dataframe(conn_map, df, table_name):
|
583
|
+
message = ""
|
584
|
+
conn = conn_map
|
585
|
+
db_type = conn_map["db_type"]
|
586
|
+
if isinstance(conn_map, dict):
|
587
|
+
conn = conn_map["conn"]
|
588
|
+
|
589
|
+
columns = df.columns.tolist() # 获得dataframe的列名
|
590
|
+
# 新增空值处理逻辑
|
591
|
+
data_to_insert = [
|
592
|
+
tuple(None if pd.isna(value) else value for value in row)
|
593
|
+
for row in df.to_numpy(dtype=object)
|
594
|
+
]
|
595
|
+
|
596
|
+
with conn.cursor() as cursor:
|
597
|
+
try:
|
598
|
+
if db_type == "oceanbase":
|
599
|
+
set_sql = "set session nls_timestamp_format='YYYY-MM-DD HH24:MI:SS.XFF3'"
|
600
|
+
cursor.execute(set_sql)
|
601
|
+
set_sql = "set session nls_date_format='YYYY-MM-DD'"
|
602
|
+
cursor.execute(set_sql)
|
603
|
+
sql = df_insert2db(columns, save_table_name=table_name)
|
604
|
+
|
605
|
+
logger.debug(f"[数据库新增][dataframe]sql: {sql}")
|
606
|
+
batch_size = int(config.MAP["batch_size"])
|
607
|
+
for i in range(0, len(data_to_insert), batch_size):
|
608
|
+
batch_data = data_to_insert[i : i + batch_size]
|
609
|
+
logger.debug(
|
610
|
+
f"[分批写入][dataframe]当前载入至第{min(i + batch_size, len(data_to_insert))}行。"
|
611
|
+
)
|
612
|
+
print(batch_data[0])
|
613
|
+
if db_type == "oceanbase":
|
614
|
+
sql = re.sub(r':\w+', '?', sql)
|
615
|
+
|
616
|
+
cursor.executemany(sql, batch_data)
|
617
|
+
conn.commit() # 批量执行query_sql, 每批次提交一次
|
618
|
+
|
619
|
+
except Exception as e:
|
620
|
+
message = f"[数据库新增][dataframe]异常:{e}"
|
621
|
+
logger.error(message)
|
622
|
+
# if len(batch_data) > 0:
|
623
|
+
# logger.error(f"[数据库新增][dataframe]异常批次数据:{batch_data}")
|
624
|
+
try:
|
625
|
+
conn.rollback()
|
626
|
+
except:
|
627
|
+
logger.warning("[数据库新增]回滚异常:{e}")
|
628
|
+
raise
|
629
|
+
# return False, message
|
630
|
+
|
631
|
+
return True, message
|
632
|
+
|
633
|
+
|
634
|
+
def df_insert2db(columns, save_table_name):
|
635
|
+
"""
|
636
|
+
Args:
|
637
|
+
columns: 以列表的形式
|
638
|
+
save_table_name: 保存table的名字
|
639
|
+
Returns:直接的insert到Oracle的sql语句,字符串格式
|
640
|
+
"""
|
641
|
+
s = ""
|
642
|
+
for i in range(len(columns)):
|
643
|
+
s = s + columns[i] + "-"
|
644
|
+
sql_columns = s.replace("-", ",")[:-1] # 不要最后输出的逗号
|
645
|
+
sql_number_str = ""
|
646
|
+
for i in range(1, len(columns) + 1, 1):
|
647
|
+
sql_number_str = sql_number_str + ":" + str(i) + "," # ':1,:2,:3,:4,:5,:6'
|
648
|
+
sql_number_str = sql_number_str[:-1]
|
649
|
+
query_sql = (
|
650
|
+
"insert into "
|
651
|
+
+ save_table_name
|
652
|
+
+ "("
|
653
|
+
+ sql_columns
|
654
|
+
+ ") values("
|
655
|
+
+ sql_number_str
|
656
|
+
+ ") "
|
657
|
+
)
|
658
|
+
return query_sql
|
659
|
+
|
660
|
+
def map_named_to_positional_params(sql, param_dict):
|
661
|
+
"""
|
662
|
+
将带有命名参数的 SQL 转换为使用 ? 占位符的 SQL,
|
663
|
+
并生成相应的参数列表
|
664
|
+
"""
|
665
|
+
# 查找所有命名参数
|
666
|
+
if param_dict is None:
|
667
|
+
return sql, None
|
668
|
+
param_names = re.findall(r':(\w+)', sql)
|
669
|
+
|
670
|
+
# 替换命名参数为 ? 占位符
|
671
|
+
positional_sql = re.sub(r':\w+', '?', sql)
|
672
|
+
|
673
|
+
# 根据参数名生成参数列表
|
674
|
+
param_list = [param_dict[name] for name in param_names]
|
675
|
+
|
676
|
+
return positional_sql, param_list
|