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.
@@ -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