mdbq 2.9.4__py3-none-any.whl → 2.9.5__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.
- mdbq/aggregation/aggregation.py +15 -12
- mdbq/aggregation/query_data.py +40 -17
- mdbq/config/products.py +0 -10
- mdbq/mysql/mysql.py +105 -163
- mdbq/mysql/recheck_mysql.py +1 -1
- mdbq/req_post/req_tb.py +1 -1
- {mdbq-2.9.4.dist-info → mdbq-2.9.5.dist-info}/METADATA +1 -1
- {mdbq-2.9.4.dist-info → mdbq-2.9.5.dist-info}/RECORD +10 -12
- mdbq/company/copysh_bak.py +0 -417
- mdbq/company/home_sh.py +0 -386
- {mdbq-2.9.4.dist-info → mdbq-2.9.5.dist-info}/WHEEL +0 -0
- {mdbq-2.9.4.dist-info → mdbq-2.9.5.dist-info}/top_level.txt +0 -0
mdbq/aggregation/aggregation.py
CHANGED
@@ -919,8 +919,8 @@ class DatabaseUpdateBak:
|
|
919
919
|
move_insert=True, # 先删除,再插入
|
920
920
|
df_sql=False, # 值为 True 时使用 df.to_sql 函数上传整个表, 不会排重
|
921
921
|
drop_duplicates=False, # 值为 True 时检查重复数据再插入,反之直接上传,会比较慢
|
922
|
+
count=None,
|
922
923
|
filename=rt_filename, # 用来追踪处理进度
|
923
|
-
service_database=service_database, # 字典
|
924
924
|
)
|
925
925
|
df_to_json.as_json_file() # 写入 json 文件, 包含数据的 dtypes 信息
|
926
926
|
|
@@ -1083,8 +1083,8 @@ class DatabaseUpdateBak:
|
|
1083
1083
|
move_insert=True, # 先删除,再插入
|
1084
1084
|
df_sql=False, # 值为 True 时使用 df.to_sql 函数上传整个表, 不会排重
|
1085
1085
|
drop_duplicates=False, # 值为 True 时检查重复数据再插入,反之直接上传,会比较慢
|
1086
|
+
count=None,
|
1086
1087
|
filename=None, # 用来追踪处理进度
|
1087
|
-
service_database=service_database, # 用来追踪处理进度
|
1088
1088
|
)
|
1089
1089
|
# return df
|
1090
1090
|
|
@@ -1116,8 +1116,8 @@ class DatabaseUpdateBak:
|
|
1116
1116
|
move_insert=False, # 先删除,再插入
|
1117
1117
|
df_sql=False, # 值为 True 时使用 df.to_sql 函数上传整个表, 不会排重
|
1118
1118
|
drop_duplicates=True, # 值为 True 时检查重复数据再插入,反之直接上传,会比较慢
|
1119
|
+
count=None,
|
1119
1120
|
filename=None, # 用来追踪处理进度
|
1120
|
-
service_database=service_database, # 用来追踪处理进度
|
1121
1121
|
)
|
1122
1122
|
|
1123
1123
|
|
@@ -1189,7 +1189,8 @@ def upload_dir(path, db_name, collection_name, json_path=None):
|
|
1189
1189
|
move_insert=False, # 先删除,再插入
|
1190
1190
|
df_sql = True,
|
1191
1191
|
drop_duplicates=False,
|
1192
|
-
|
1192
|
+
count=f'{i}/{count}',
|
1193
|
+
filename=name,
|
1193
1194
|
)
|
1194
1195
|
# nas.df_to_mysql(df=df, db_name=db_name, table_name=collection_name, drop_duplicates=True,)
|
1195
1196
|
except Exception as e:
|
@@ -1210,15 +1211,16 @@ def one_file_to_mysql(file, db_name, table_name):
|
|
1210
1211
|
df=df,
|
1211
1212
|
db_name=db_name,
|
1212
1213
|
table_name=table_name,
|
1213
|
-
filename=filename,
|
1214
1214
|
move_insert=False,
|
1215
1215
|
df_sql=True,
|
1216
1216
|
drop_duplicates=False,
|
1217
|
+
count=None,
|
1218
|
+
filename=filename,
|
1217
1219
|
)
|
1218
1220
|
|
1219
1221
|
|
1220
1222
|
def test():
|
1221
|
-
path = r'/Users/xigua
|
1223
|
+
path = r'/Users/xigua/数据中心/原始文件3/达摩盘/dmp人群报表'
|
1222
1224
|
|
1223
1225
|
results = []
|
1224
1226
|
for root, dirs, files in os.walk(path, topdown=False):
|
@@ -1231,8 +1233,10 @@ def test():
|
|
1231
1233
|
if len(df) == 0:
|
1232
1234
|
continue
|
1233
1235
|
if '达摩盘消耗占比' in df.columns.tolist():
|
1234
|
-
print(name)
|
1235
1236
|
df.pop('达摩盘消耗占比')
|
1237
|
+
if '更新时间' not in df.columns.tolist():
|
1238
|
+
print(name)
|
1239
|
+
df['更新时间'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
1236
1240
|
# df.insert(loc=1, column='店铺名称', value='万里马官方旗舰店')
|
1237
1241
|
# df['更新时间'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
1238
1242
|
df.to_csv(os.path.join(root, name), encoding='utf-8_sig', index=False, header=True)
|
@@ -1278,14 +1282,13 @@ if __name__ == '__main__':
|
|
1278
1282
|
# )
|
1279
1283
|
|
1280
1284
|
# test()
|
1281
|
-
|
1282
|
-
col = 1
|
1285
|
+
col = 0
|
1283
1286
|
if col:
|
1284
1287
|
# 上传一个目录到指定数据库
|
1285
|
-
db_name = '
|
1286
|
-
table_name = '
|
1288
|
+
db_name = '生意参谋3'
|
1289
|
+
table_name = '店铺流量来源构成'
|
1287
1290
|
upload_dir(
|
1288
|
-
path=r'/Users/xigua
|
1291
|
+
path=r'/Users/xigua/数据中心/原始文件3/生意参谋/店铺流量来源',
|
1289
1292
|
db_name=db_name,
|
1290
1293
|
collection_name=table_name,
|
1291
1294
|
)
|
mdbq/aggregation/query_data.py
CHANGED
@@ -2119,8 +2119,9 @@ def date_table():
|
|
2119
2119
|
move_insert=True, # 先删除,再插入
|
2120
2120
|
df_sql=False, # 值为 True 时使用 df.to_sql 函数上传整个表, 不会排重
|
2121
2121
|
drop_duplicates=False, # 值为 True 时检查重复数据再插入,反之直接上传,会比较慢
|
2122
|
+
count=None,
|
2122
2123
|
filename=None, # 用来追踪处理进度
|
2123
|
-
|
2124
|
+
set_typ={},
|
2124
2125
|
)
|
2125
2126
|
|
2126
2127
|
|
@@ -2284,7 +2285,9 @@ def data_aggregation(months=1, is_juhe=True, less_dict=[]):
|
|
2284
2285
|
# df_sql=True,
|
2285
2286
|
drop_duplicates=False,
|
2286
2287
|
icm_update=['商品id'],
|
2287
|
-
|
2288
|
+
count=None,
|
2289
|
+
filename=None,
|
2290
|
+
set_typ={},
|
2288
2291
|
)
|
2289
2292
|
g.sp_index_datas = pd.DataFrame() # 重置,不然下个循环会继续刷入数据库
|
2290
2293
|
# g.as_csv(df=df, filename=table_name + '.csv') # 导出 csv
|
@@ -2297,7 +2300,9 @@ def data_aggregation(months=1, is_juhe=True, less_dict=[]):
|
|
2297
2300
|
# df_sql=True,
|
2298
2301
|
# drop_duplicates=False,
|
2299
2302
|
# icm_update=unique_key_list,
|
2300
|
-
|
2303
|
+
count=None,
|
2304
|
+
filename=None,
|
2305
|
+
set_typ={},
|
2301
2306
|
) # 3. 回传数据库
|
2302
2307
|
else: # 没有日期列的就用主键排重
|
2303
2308
|
m.df_to_mysql(
|
@@ -2308,7 +2313,9 @@ def data_aggregation(months=1, is_juhe=True, less_dict=[]):
|
|
2308
2313
|
# df_sql=True,
|
2309
2314
|
drop_duplicates=False,
|
2310
2315
|
icm_update=unique_key_list,
|
2311
|
-
|
2316
|
+
count=None,
|
2317
|
+
filename=None,
|
2318
|
+
set_typ={},
|
2312
2319
|
) # 3. 回传数据库
|
2313
2320
|
if is_juhe:
|
2314
2321
|
res = g.performance(bb_tg=True) # 盈亏表,依赖其他表,单独做
|
@@ -2320,7 +2327,9 @@ def data_aggregation(months=1, is_juhe=True, less_dict=[]):
|
|
2320
2327
|
# df_sql=True,
|
2321
2328
|
# drop_duplicates=False,
|
2322
2329
|
# icm_update=['日期', '商品id'], # 设置唯一主键
|
2323
|
-
|
2330
|
+
count=None,
|
2331
|
+
filename=None,
|
2332
|
+
set_typ={},
|
2324
2333
|
)
|
2325
2334
|
res = g.performance(bb_tg=False) # 盈亏表,依赖其他表,单独做
|
2326
2335
|
m.df_to_mysql(
|
@@ -2331,7 +2340,9 @@ def data_aggregation(months=1, is_juhe=True, less_dict=[]):
|
|
2331
2340
|
# df_sql=True,
|
2332
2341
|
# drop_duplicates=False,
|
2333
2342
|
# icm_update=['日期', '商品id'], # 设置唯一主键
|
2334
|
-
|
2343
|
+
count=None,
|
2344
|
+
filename=None,
|
2345
|
+
set_typ={},
|
2335
2346
|
)
|
2336
2347
|
res = g.performance_concat(bb_tg=False) # 推广主体合并直播表,依赖其他表,单独做
|
2337
2348
|
m.df_to_mysql(
|
@@ -2342,7 +2353,9 @@ def data_aggregation(months=1, is_juhe=True, less_dict=[]):
|
|
2342
2353
|
# df_sql=True,
|
2343
2354
|
# drop_duplicates=False,
|
2344
2355
|
# icm_update=['日期', '推广渠道', '营销场景', '商品id', '花费', '展现量', '点击量'], # 设置唯一主键
|
2345
|
-
|
2356
|
+
count=None,
|
2357
|
+
filename=None,
|
2358
|
+
set_typ={},
|
2346
2359
|
)
|
2347
2360
|
res = g.performance_jd(jd_tg=False) # 盈亏表,依赖其他表,单独做
|
2348
2361
|
m.df_to_mysql(
|
@@ -2353,18 +2366,32 @@ def data_aggregation(months=1, is_juhe=True, less_dict=[]):
|
|
2353
2366
|
# df_sql=True,
|
2354
2367
|
# drop_duplicates=False,
|
2355
2368
|
# icm_update=['日期', '跟单sku_id', '货号', '花费'], # 设置唯一主键
|
2356
|
-
|
2369
|
+
count=None,
|
2370
|
+
filename=None,
|
2371
|
+
set_typ={},
|
2357
2372
|
)
|
2358
2373
|
|
2359
2374
|
|
2360
2375
|
def main(days=100, months=3):
|
2361
|
-
# 更新日期表
|
2376
|
+
# 1. 更新日期表 更新货品年份基准表, 属性设置 3 - 货品年份基准
|
2362
2377
|
date_table()
|
2363
|
-
# 更新货品年份基准表, 属性设置 3 - 货品年份基准
|
2364
2378
|
p = products.Products()
|
2365
2379
|
p.to_mysql()
|
2366
2380
|
|
2367
|
-
#
|
2381
|
+
# 2. 清理非聚合数据库
|
2382
|
+
system = platform.system() # 本机系统
|
2383
|
+
host_name = socket.gethostname() # 本机名
|
2384
|
+
conf = myconfig.main()
|
2385
|
+
db_list = conf[system][host_name]['mysql']['数据库集']
|
2386
|
+
not_juhe_db_list = [item for item in db_list if item != '聚合数据']
|
2387
|
+
optimize_data.op_data(
|
2388
|
+
db_name_lists=not_juhe_db_list,
|
2389
|
+
days=31, # 原始数据不需要设置清理太长
|
2390
|
+
is_mongo=False,
|
2391
|
+
is_mysql=True,
|
2392
|
+
)
|
2393
|
+
|
2394
|
+
# 3. 数据聚合
|
2368
2395
|
data_aggregation(
|
2369
2396
|
months=months,
|
2370
2397
|
is_juhe=True, # 生成聚合表
|
@@ -2372,13 +2399,9 @@ def main(days=100, months=3):
|
|
2372
2399
|
)
|
2373
2400
|
time.sleep(60)
|
2374
2401
|
|
2375
|
-
|
2376
|
-
host_name = socket.gethostname() # 本机名
|
2377
|
-
conf = myconfig.main()
|
2378
|
-
db_list = conf[system][host_name]['mysql']['数据库集']
|
2379
|
-
# 清理所有库
|
2402
|
+
# 4. 清理聚合数据
|
2380
2403
|
optimize_data.op_data(
|
2381
|
-
db_name_lists=
|
2404
|
+
db_name_lists=['聚合数据'],
|
2382
2405
|
days=days,
|
2383
2406
|
is_mongo=False,
|
2384
2407
|
is_mysql=True,
|
mdbq/config/products.py
CHANGED
@@ -147,16 +147,6 @@ class Products:
|
|
147
147
|
'上市年份': 'mediumtext',
|
148
148
|
},
|
149
149
|
)
|
150
|
-
# m.df_to_mysql(
|
151
|
-
# df=df,
|
152
|
-
# db_name='属性设置3',
|
153
|
-
# table_name='货品年份基准',
|
154
|
-
# move_insert = False,
|
155
|
-
# df_sql=False, # 值为 True 时使用 df.to_sql 函数上传整个表, 不会排重
|
156
|
-
# drop_duplicates=True, # 值为 True 时检查重复数据再插入,反之直接上传,会比较慢
|
157
|
-
# icm_update=[],
|
158
|
-
# service_database=service_database, # 用来追踪处理进度
|
159
|
-
# )
|
160
150
|
|
161
151
|
def market_date(self, product_id: int):
|
162
152
|
try:
|
mdbq/mysql/mysql.py
CHANGED
@@ -306,6 +306,7 @@ class MysqlUpload:
|
|
306
306
|
connection.close()
|
307
307
|
|
308
308
|
def cover_dict_dtypes(self, dict_data):
|
309
|
+
""" 清理字典键值 并转换数据类型 """
|
309
310
|
if not dict_data:
|
310
311
|
print(f'mysql.py -> MysqlUpload -> cover_dict_dtypes -> 传入的字典不能为空')
|
311
312
|
return
|
@@ -339,7 +340,7 @@ class MysqlUpload:
|
|
339
340
|
elif str(v) == '':
|
340
341
|
__res_dict.update({k: 'mediumtext'})
|
341
342
|
elif result2: # 小数
|
342
|
-
__res_dict.update({k: 'decimal(
|
343
|
+
__res_dict.update({k: 'decimal(12,4)'})
|
343
344
|
elif date_type == 1: # 纯日期
|
344
345
|
__res_dict.update({k: 'DATE'})
|
345
346
|
elif date_type == 2: # 日期+时间
|
@@ -352,7 +353,7 @@ class MysqlUpload:
|
|
352
353
|
v = round(float(v), 4)
|
353
354
|
__res_dict.update({k: 'decimal(12,4)'})
|
354
355
|
elif count_float >= 6:
|
355
|
-
__res_dict.update({k: 'decimal(
|
356
|
+
__res_dict.update({k: 'decimal(14,6)'})
|
356
357
|
elif count_float >= 4:
|
357
358
|
__res_dict.update({k: 'decimal(10,4)'})
|
358
359
|
else:
|
@@ -362,12 +363,78 @@ class MysqlUpload:
|
|
362
363
|
new_dict_data.update({k: v})
|
363
364
|
return __res_dict, new_dict_data
|
364
365
|
|
366
|
+
def cover_df(self, df):
|
367
|
+
""" 清理 df 的值和列名 """
|
368
|
+
df.replace([np.inf, -np.inf], 0, inplace=True) # 清理一些非法值
|
369
|
+
df.replace(to_replace=['\\N', '-', '--', '', 'nan', 'NAN'], value=0, regex=False, inplace=True) # 替换掉特殊字符
|
370
|
+
df.replace(to_replace=[','], value='', regex=True, inplace=True)
|
371
|
+
df.replace(to_replace=['="'], value='', regex=True, inplace=True) # ="和"不可以放在一起清洗, 因为有: id=86785565
|
372
|
+
df.replace(to_replace=['"'], value='', regex=True, inplace=True)
|
373
|
+
cols = df.columns.tolist()
|
374
|
+
for col in cols:
|
375
|
+
df[col] = df[col].apply(lambda x: float(re.sub(r'%$', '', str(x))) / 100 if (
|
376
|
+
str(x) != '' and str(x).endswith('%')) else '0.0' if str(x) == '0%' else x)
|
377
|
+
try:
|
378
|
+
df[col] = df[col].apply(
|
379
|
+
lambda x: int(x) if '_' not in str(x) and '.' not in str(x) else x) # 不含小数点尝试转整数
|
380
|
+
except:
|
381
|
+
pass
|
382
|
+
if df[col].dtype == 'object':
|
383
|
+
try:
|
384
|
+
df[col] = df[col].apply(lambda x: float(x) if '.' in str(x) and '_' not in str(x) else x)
|
385
|
+
except:
|
386
|
+
pass
|
387
|
+
new_col = col.lower()
|
388
|
+
new_col = re.sub(r'[()\-,,&~^、 ()\"\'“”=·/。》《><!!`]', '_', new_col, re.IGNORECASE)
|
389
|
+
new_col = new_col.replace(')', '')
|
390
|
+
new_col = re.sub(r'_{2,}', '_', new_col)
|
391
|
+
new_col = re.sub(r'_+$', '', new_col)
|
392
|
+
df.rename(columns={col: new_col}, inplace=True)
|
393
|
+
df.fillna(0, inplace=True)
|
394
|
+
return df
|
395
|
+
|
396
|
+
def convert_df_dtypes(self, df: pd.DataFrame):
|
397
|
+
""" 清理 df 的值和列名,并转换数据类型 """
|
398
|
+
df = self.cover_df(df=df) # 清理 df 的值和列名
|
399
|
+
[pd.to_numeric(df[col], errors='ignore') for col in df.columns.tolist()]
|
400
|
+
dtypes = df.dtypes.to_dict()
|
401
|
+
__res_dict = {}
|
402
|
+
for k, v in dtypes.items():
|
403
|
+
result1 = re.findall(r'编码|_?id|货号|款号|文件大小', k, re.IGNORECASE)
|
404
|
+
result2 = re.findall(r'占比$|投产$|产出$|roi$|率$', k, re.IGNORECASE)
|
405
|
+
result3 = re.findall(r'同比$|环比$', k, re.IGNORECASE)
|
406
|
+
result4 = re.findall(r'花费$|消耗$|金额$', k, re.IGNORECASE)
|
407
|
+
|
408
|
+
if result1: # id/sku/spu商品信息
|
409
|
+
__res_dict.update({k: 'varchar(50)'})
|
410
|
+
elif result2: # 小数
|
411
|
+
__res_dict.update({k: 'decimal(10,4)'})
|
412
|
+
elif result3: # 小数
|
413
|
+
__res_dict.update({k: 'decimal(12,4)'})
|
414
|
+
elif result4: # 小数
|
415
|
+
__res_dict.update({k: 'decimal(12,2)'})
|
416
|
+
elif k == '日期':
|
417
|
+
__res_dict.update({k: 'DATE'})
|
418
|
+
elif k == '更新时间':
|
419
|
+
__res_dict.update({k: 'TIMESTAMP'})
|
420
|
+
elif v == 'int64':
|
421
|
+
__res_dict.update({k: 'int'})
|
422
|
+
elif v == 'float64':
|
423
|
+
__res_dict.update({k: 'decimal(10,4)'})
|
424
|
+
elif v == 'bool':
|
425
|
+
__res_dict.update({k: 'BOOLEAN'})
|
426
|
+
elif v == 'datetime64[ns]':
|
427
|
+
__res_dict.update({k: 'datetime'})
|
428
|
+
else:
|
429
|
+
__res_dict.update({k: 'varchar(255)'})
|
430
|
+
return __res_dict, df
|
431
|
+
|
365
432
|
@try_except
|
366
|
-
def df_to_mysql(self, df, table_name,
|
433
|
+
def df_to_mysql(self, df, db_name, table_name, set_typ=None, icm_update=[], move_insert=False, df_sql=False, drop_duplicates=False,
|
434
|
+
filename=None, count=None, reset_id=False):
|
367
435
|
"""
|
368
|
-
|
369
|
-
|
370
|
-
table_name: 集合/表名称
|
436
|
+
db_name: 数据库名
|
437
|
+
table_name: 表名
|
371
438
|
move_insert: 根据df 的日期,先移除数据库数据,再插入, df_sql, drop_duplicates, icm_update 都要设置为 False
|
372
439
|
原则上只限于聚合数据使用,原始数据插入时不要设置
|
373
440
|
|
@@ -376,8 +443,6 @@ class MysqlUpload:
|
|
376
443
|
icm_update: 增量更新, 在聚合数据中使用,原始文件不要使用,设置此参数时需将 drop_duplicates 改为 False
|
377
444
|
使用增量更新: 必须确保 icm_update 传进来的列必须是数据表中唯一主键,值不会发生变化,不会重复,否则可能产生错乱覆盖情况
|
378
445
|
filename: 用来追踪处理进度,传这个参数是方便定位产生错误的文件
|
379
|
-
service_database: 这个参数是用来设置更新哪台服务器的 types 信息到本地 json 文件
|
380
|
-
json_path: 这个参数同样也是是用来设置更新 json 文件
|
381
446
|
"""
|
382
447
|
self.filename = filename
|
383
448
|
if isinstance(df, pd.DataFrame):
|
@@ -391,8 +456,13 @@ class MysqlUpload:
|
|
391
456
|
print(f'{db_name} 不能为 None')
|
392
457
|
return
|
393
458
|
|
394
|
-
|
395
|
-
df =
|
459
|
+
# 清理 dataframe 非法值,并转换获取数据类型
|
460
|
+
dtypes, df = self.convert_df_dtypes(df)
|
461
|
+
if set_typ:
|
462
|
+
# 更新自定义的列数据类型
|
463
|
+
for k, v in dtypes.items():
|
464
|
+
# 确保传进来的 set_typ 键存在于实际的 df 列才 update
|
465
|
+
[dtypes.update({k: inside_v}) for inside_k, inside_v in set_typ.items() if k == inside_k]
|
396
466
|
|
397
467
|
connection = pymysql.connect(**self.config) # 连接数据库
|
398
468
|
with connection.cursor() as cursor:
|
@@ -422,15 +492,6 @@ class MysqlUpload:
|
|
422
492
|
cursor.execute(sql)
|
423
493
|
print(f'创建 mysql 表: {table_name}')
|
424
494
|
|
425
|
-
for service_name, database in service_database.items():
|
426
|
-
# 2. 列数据类型转换,将 df 数据类型转换为 mysql 的数据类型
|
427
|
-
dtypes, cl, db_n, tb_n = self.convert_dtypes(df=df, db_name=db_name, table_name=table_name, path=json_path, service_name=service_name)
|
428
|
-
for dy in dtypes.keys():
|
429
|
-
if '日期' == dy:
|
430
|
-
dtypes.update({'日期': 'DATE'})
|
431
|
-
if '更新时间' == dy:
|
432
|
-
dtypes.update({'更新时间': 'TIMESTAMP'})
|
433
|
-
|
434
495
|
# 有特殊字符不需转义
|
435
496
|
sql = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = %s AND TABLE_NAME = %s;"
|
436
497
|
cursor.execute(sql, (db_name, table_name))
|
@@ -456,7 +517,8 @@ class MysqlUpload:
|
|
456
517
|
|
457
518
|
if df_sql:
|
458
519
|
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S ")
|
459
|
-
print(
|
520
|
+
print(
|
521
|
+
f'{now}正在更新: mysql ({self.host}:{self.port}) {db_name}/{table_name}, {count}, {self.filename}')
|
460
522
|
engine = create_engine(
|
461
523
|
f"mysql+pymysql://{self.username}:{self.password}@{self.host}:{self.port}/{db_name}") # 创建数据库引擎
|
462
524
|
df.to_sql(
|
@@ -466,45 +528,23 @@ class MysqlUpload:
|
|
466
528
|
index=False,
|
467
529
|
chunksize=1000
|
468
530
|
)
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
if cl and db_n and tb_n:
|
484
|
-
mysql_types.mysql_all_dtypes(db_name=db_name, table_name=table_name) # 更新一个表的 dtypes
|
485
|
-
elif cl and db_n:
|
486
|
-
mysql_types.mysql_all_dtypes(db_name=db_name) # 更新一个数据库的 dtypes
|
487
|
-
elif cl:
|
488
|
-
mysql_types.mysql_all_dtypes() # 更新所有数据库所有数据表的 dtypes 信息到本地 json
|
489
|
-
|
531
|
+
if reset_id:
|
532
|
+
# 6. 重置自增列
|
533
|
+
try:
|
534
|
+
cursor.execute(f"SHOW COLUMNS FROM {table_name} LIKE 'id'")
|
535
|
+
result = cursor.fetchone()
|
536
|
+
if result:
|
537
|
+
cursor.execute(f"ALTER TABLE {table_name} DROP COLUMN id;") # 删除 id 列
|
538
|
+
cursor.execute(
|
539
|
+
f"ALTER TABLE {table_name} ADD column id INT AUTO_INCREMENT PRIMARY KEY FIRST;")
|
540
|
+
cursor.execute(f"ALTER TABLE {table_name} AUTO_INCREMENT = 1") # 设置自增从 1 开始
|
541
|
+
except Exception as e:
|
542
|
+
print(f'{e}')
|
543
|
+
connection.rollback()
|
544
|
+
connection.commit() # 提交事务
|
490
545
|
connection.close()
|
491
546
|
return
|
492
547
|
|
493
|
-
# print(cl, db_n, tb_n)
|
494
|
-
# 返回这些结果的目的是等添加完列再写 json 文件才能读到 types 信息
|
495
|
-
# ⚠️ mysql_all_dtypes 函数默认只读取 xigua_lx 的数据库信息,不会读取其他系统
|
496
|
-
if cl and db_n and tb_n:
|
497
|
-
mysql_types.mysql_all_dtypes(db_name=db_name, table_name=table_name) # 更新一个表的 dtypes
|
498
|
-
elif cl and db_n:
|
499
|
-
mysql_types.mysql_all_dtypes(db_name=db_name) # 更新一个数据库的 dtypes
|
500
|
-
elif cl:
|
501
|
-
mysql_types.mysql_all_dtypes() # 更新所有数据库所有数据表的 dtypes 信息到本地 json
|
502
|
-
|
503
|
-
# 4. 更新插入数据
|
504
|
-
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S ")
|
505
|
-
for service_name, database in service_database.items():
|
506
|
-
print(f'{now}正在更新 mysql ({self.host}:{self.port}) {db_name}/{table_name}, {count}, {service_name}, {self.filename}')
|
507
|
-
|
508
548
|
# 5. 移除指定日期范围内的数据,原则上只限于聚合数据使用,原始数据插入时不要设置
|
509
549
|
if move_insert and '日期' in df.columns.tolist():
|
510
550
|
# 移除数据
|
@@ -546,7 +586,6 @@ class MysqlUpload:
|
|
546
586
|
# data 是传进来待处理的数据, 不是数据库数据
|
547
587
|
# data 示例: {'日期': Timestamp('2024-08-27 00:00:00'), '推广费余额': 33299, '品销宝余额': 2930.73, '短信剩余': 67471}
|
548
588
|
try:
|
549
|
-
|
550
589
|
condition = []
|
551
590
|
for k, v in data.items():
|
552
591
|
condition += [f'`{k}` = "{v}"']
|
@@ -571,7 +610,8 @@ class MysqlUpload:
|
|
571
610
|
cursor.execute(sql, (db_name, {table_name}))
|
572
611
|
columns = cursor.fetchall()
|
573
612
|
cols_exist = [col['COLUMN_NAME'] for col in columns] # 数据表的所有列, 返回 list
|
574
|
-
update_col = [item for item in cols_exist if
|
613
|
+
update_col = [item for item in cols_exist if
|
614
|
+
item not in icm_update and item != 'id'] # 除了主键外的其他列
|
575
615
|
|
576
616
|
# unique_keys 示例: `日期`, `余额`
|
577
617
|
unique_keys = ', '.join(f"`{item}`" for item in update_col) # 列名需要转义
|
@@ -607,7 +647,8 @@ class MysqlUpload:
|
|
607
647
|
if change_values: # change_values 有数据返回,表示值需要更新
|
608
648
|
if not_change_col:
|
609
649
|
not_change_values = [f'`{col}` = "{str(data[col])}"' for col in not_change_col]
|
610
|
-
not_change_values = ' AND '.join(
|
650
|
+
not_change_values = ' AND '.join(
|
651
|
+
not_change_values) # 示例: `短信剩余` = '888' AND `test1` = '93'
|
611
652
|
# print(change_values, not_change_values)
|
612
653
|
condition += f' AND {not_change_values}' # 重新构建完整的查询条件,将未发生变化的列加进查询条件
|
613
654
|
change_values = ', '.join(f"{item}" for item in change_values) # 注意这里 item 外面没有反引号
|
@@ -641,107 +682,6 @@ class MysqlUpload:
|
|
641
682
|
connection.commit() # 提交事务
|
642
683
|
connection.close()
|
643
684
|
|
644
|
-
def convert_dtypes(self, df, db_name, table_name, path=None, service_name=None):
|
645
|
-
"""
|
646
|
-
根据本地 json 转换 df 的类型为 mysql 专有的数据类型
|
647
|
-
可能不存在本地 json 文件 (函数按指定规则转换并更新 json)
|
648
|
-
允许通过 json 文件手动添加或修改列的数据类型(仅影响初创数据表)
|
649
|
-
"""
|
650
|
-
cols = df.columns.tolist()
|
651
|
-
# path = set_support.SetSupport(dirname='support').dirname
|
652
|
-
d = mysql_types.DataTypes(path=path, service_name=service_name)
|
653
|
-
# 从本地文件中读取 dtype 信息
|
654
|
-
dtypes, cl, db_n, tb_n = d.load_dtypes(cl='mysql', db_name=db_name, table_name=table_name)
|
655
|
-
# 可能会因为没有 json 文件, 返回 None
|
656
|
-
if dtypes:
|
657
|
-
# 按照文件记录更新 dtypes
|
658
|
-
dtypes.update({col: dtypes[col] for col in cols if col in dtypes.keys()})
|
659
|
-
# 可能存在部分列不在文件记录中
|
660
|
-
col_not_exist = [col for col in cols if col not in dtypes.keys()]
|
661
|
-
# 这些列不存在于 df 中, 必须移除
|
662
|
-
[dtypes.pop(col) for col in list(dtypes.keys()) if col not in cols]
|
663
|
-
else: # 没有 json 文件时
|
664
|
-
dtypes = df.dtypes.apply(str).to_dict() # 将 dataframe 数据类型转为字典形式
|
665
|
-
col_not_exist = cols
|
666
|
-
# 对文件不存在的列信息进行数据类型转换(按指定规则)
|
667
|
-
dtypes.update({col: self.convert_dtype_to_sql(df=df, col=col, dtype=df[col].dtype) for col in col_not_exist})
|
668
|
-
# print(dtypes)
|
669
|
-
# 至此 df 中全部列类型已经转换完成
|
670
|
-
# 返回结果, 示例: {'上市年份': 'mediumtext', '商品id': 'mediumtext', '平台': 'mediumtext'}
|
671
|
-
return dtypes, cl, db_n, tb_n # 返回这些结果的目的是等添加完列再写 json 文件才能读到 types 信息
|
672
|
-
|
673
|
-
def convert_dtype_to_sql(self, df, col, dtype):
|
674
|
-
""" 按照以下规则转换DataFrame列的数据类型为 MYSQL 专有的数据类型 """
|
675
|
-
|
676
|
-
def find_longest_decimal_value(number_list):
|
677
|
-
# 针对小数设置的函数, 用来获取列表中小数位数最长的值
|
678
|
-
longest_value = None
|
679
|
-
max_decimals = 0
|
680
|
-
for num in number_list:
|
681
|
-
try:
|
682
|
-
decimal_places = len(str(num).split('.')[1])
|
683
|
-
if decimal_places > max_decimals:
|
684
|
-
max_decimals = decimal_places
|
685
|
-
longest_value = num
|
686
|
-
except:
|
687
|
-
longest_value = num
|
688
|
-
continue
|
689
|
-
return longest_value
|
690
|
-
|
691
|
-
if '商品编码' in col: # 京东sku/spu商品信息
|
692
|
-
return 'mediumtext'
|
693
|
-
if '文件大小' in col: # bw 程序
|
694
|
-
return 'mediumtext'
|
695
|
-
if col.endswith('占比') and (df[col].dtype == 'float' or df[col].dtype == 'int'):
|
696
|
-
return 'decimal(10,4)'
|
697
|
-
elif dtype == 'datetime64[ns]':
|
698
|
-
return 'DATETIME' # 使用 DATE 后续排重可能会引发不能排重
|
699
|
-
elif dtype == 'int32':
|
700
|
-
max_num = str(max(df[col].tolist()))
|
701
|
-
if len(max_num) >= 10: # 数值长度超限转为 mediumtext
|
702
|
-
return 'mediumtext'
|
703
|
-
return 'INT'
|
704
|
-
elif dtype == 'int64':
|
705
|
-
max_num = str(max(df[col].tolist()))
|
706
|
-
if len(max_num) >= 10:
|
707
|
-
return 'mediumtext'
|
708
|
-
return 'INT'
|
709
|
-
elif dtype == 'float64':
|
710
|
-
res = find_longest_decimal_value(df[col].tolist()) # 取小数位数最长的值
|
711
|
-
if 'e' in str(res):
|
712
|
-
res = round(float(res), 4)
|
713
|
-
int_step = len(str(res).split('.')[0]) # 整数位数长度
|
714
|
-
f_step = len(str(res).split('.')[1]) # 小数位数长度
|
715
|
-
|
716
|
-
if int_step >= 12:
|
717
|
-
return 'mediumtext' # mysql 中不要使用 float 和 double 类型,会影响计算结果
|
718
|
-
elif int_step >= 8 and f_step >= 0:
|
719
|
-
return 'decimal(16, 2)'
|
720
|
-
elif int_step >= 6 and f_step >= 0:
|
721
|
-
return 'decimal(10, 2)'
|
722
|
-
elif int_step >= 4 and f_step >= 0:
|
723
|
-
return 'decimal(10, 2)'
|
724
|
-
elif int_step >= 2 and f_step >= 6:
|
725
|
-
return 'decimal(12, 4)'
|
726
|
-
elif int_step >= 2 and f_step > 4:
|
727
|
-
return 'decimal(12, 4)'
|
728
|
-
elif int_step >= 2 and f_step > 2:
|
729
|
-
return 'decimal(10, 4)'
|
730
|
-
elif int_step >= 2 and f_step >= 0:
|
731
|
-
return 'decimal(10, 2)'
|
732
|
-
elif int_step >= 1 and f_step >= 6:
|
733
|
-
return 'decimal(12, 4)'
|
734
|
-
elif int_step >= 1 and f_step > 4:
|
735
|
-
return 'decimal(12, 4)'
|
736
|
-
elif int_step >= 1 and f_step > 2:
|
737
|
-
return 'decimal(10, 4)'
|
738
|
-
else:
|
739
|
-
return 'decimal(10, 2)'
|
740
|
-
elif dtype == 'object':
|
741
|
-
return 'mediumtext'
|
742
|
-
else:
|
743
|
-
return 'mediumtext'
|
744
|
-
|
745
685
|
# @try_except
|
746
686
|
def read_mysql(self, table_name, start_date, end_date, db_name='远程数据源', date_name='日期'):
|
747
687
|
""" 读取指定数据表,可指定日期范围,返回结果: df """
|
@@ -1175,6 +1115,8 @@ if __name__ == '__main__':
|
|
1175
1115
|
username, password, host, port = data['username'], data['password'], data['host'], data['port']
|
1176
1116
|
print(username, password, host, port)
|
1177
1117
|
|
1178
|
-
|
1179
|
-
ss=
|
1180
|
-
|
1118
|
+
df = pd.read_excel('/Users/xigua/Downloads/66563857.xlsx')
|
1119
|
+
ss = MysqlUpload(username, password, host, port)
|
1120
|
+
res, data = ss.convert_df_dtypes(df=df)
|
1121
|
+
print(data)
|
1122
|
+
print(res)
|
mdbq/mysql/recheck_mysql.py
CHANGED
@@ -150,8 +150,8 @@ class ReCheckMysql:
|
|
150
150
|
move_insert=True, # 先删除,再插入
|
151
151
|
df_sql=False, # 值为 True 时使用 df.to_sql 函数上传整个表, 不会排重
|
152
152
|
drop_duplicates=False, # 值为 True 时检查重复数据再插入,反之直接上传,会比较慢
|
153
|
+
count=None,
|
153
154
|
filename='', # 用来追踪处理进度
|
154
|
-
service_database={'company': 'mysql'}, # 字典
|
155
155
|
)
|
156
156
|
|
157
157
|
|
mdbq/req_post/req_tb.py
CHANGED
@@ -611,8 +611,8 @@ def company_run(service_databases=[]):
|
|
611
611
|
move_insert=False, # 先删除,再插入
|
612
612
|
df_sql=False, # 值为 True 时使用 df.to_sql 函数上传整个表, 不会排重
|
613
613
|
drop_duplicates=False, # 值为 True 时检查重复数据再插入,反之直接上传,会比较慢
|
614
|
+
count=None,
|
614
615
|
filename=None, # 用来追踪处理进度
|
615
|
-
service_database=dt, # 字典
|
616
616
|
)
|
617
617
|
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S ")
|
618
618
|
print(f'{now} {db_name} -> {table_name}: 已入库')
|