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.
@@ -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
- filename=name, count=f'{i}/{count}',
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/Downloads/DMP报表'
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 = '达摩盘3'
1286
- table_name = 'dmp人群报表'
1288
+ db_name = '生意参谋3'
1289
+ table_name = '店铺流量来源构成'
1287
1290
  upload_dir(
1288
- path=r'/Users/xigua/Downloads/DMP报表',
1291
+ path=r'/Users/xigua/数据中心/原始文件3/生意参谋/店铺流量来源',
1289
1292
  db_name=db_name,
1290
1293
  collection_name=table_name,
1291
1294
  )
@@ -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
- service_database=service_database, # 用来追踪处理进度
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
- service_database=service_database,
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
- service_database=service_database,
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
- service_database=service_database,
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
- service_database=service_database,
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
- service_database=service_database,
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
- service_database=service_database,
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
- service_database=service_database,
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
- system = platform.system() # 本机系统
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=db_list,
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(10,4)'})
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(12,6)'})
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, db_name='远程数据源', icm_update=[], service_database={'xigua_lx': 'mysql'}, move_insert=False, df_sql=False, drop_duplicates=False, filename=None, count=None, json_path=None, reset_id=False):
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
- df 写入数据库
369
- db_name: 数据库名称
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
- cv = converter.DataFrameConverter()
395
- df = cv.convert_df_cols(df=df) # 清理 dataframe 非法值
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(f'{now}正在更新: mysql ({self.host}:{self.port}) {db_name}/{table_name}, {count}, {self.filename}')
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
- # print(f'重置自增')
470
- # # 6. 重置自增列
471
- # try:
472
- # cursor.execute(f"SHOW COLUMNS FROM {table_name} LIKE 'id'")
473
- # result = cursor.fetchone()
474
- # if result:
475
- # cursor.execute(f"ALTER TABLE {table_name} DROP COLUMN id;") # 删除 id 列
476
- # cursor.execute(
477
- # f"ALTER TABLE {table_name} ADD column id INT AUTO_INCREMENT PRIMARY KEY FIRST;")
478
- # cursor.execute(f"ALTER TABLE {table_name} AUTO_INCREMENT = 1") # 设置自增从 1 开始
479
- # except Exception as e:
480
- # print(f'{e}')
481
- # connection.rollback()
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 item not in icm_update and item != 'id'] # 除了主键外的其他列
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(not_change_values) # 示例: `短信剩余` = '888' AND `test1` = '93'
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
- ss = '2024-11-08'
1179
- ss= re.sub(r'\\N', '0', ss)
1180
- print(ss, '111')
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)
@@ -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}: 已入库')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mdbq
3
- Version: 2.9.4
3
+ Version: 2.9.5
4
4
  Home-page: https://pypi.org/project/mdbq
5
5
  Author: xigua,
6
6
  Author-email: 2587125111@qq.com