mdbq 3.12.2__py3-none-any.whl → 3.12.3__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/__version__.py CHANGED
@@ -1 +1 @@
1
- VERSION = '3.12.2'
1
+ VERSION = '3.12.3'
@@ -21,7 +21,7 @@ warnings.filterwarnings('ignore')
21
21
  logger = mylogger.MyLogger(
22
22
  name='deduplicator',
23
23
  logging_mode='file',
24
- log_level='info',
24
+ log_level='debug',
25
25
  log_file='deduplicator.log',
26
26
  log_format='json',
27
27
  max_log_size=50,
@@ -34,7 +34,22 @@ logger = mylogger.MyLogger(
34
34
 
35
35
  class MySQLDeduplicator:
36
36
  """
37
- MySQL数据去重
37
+ MySQL 数据去重。
38
+
39
+ 支持按天分区(如有 date_column)或全表去重,支持自定义去重分组字段(columns),可排除指定列(exclude_columns),并支持多线程并发。
40
+
41
+ 主要参数说明:
42
+ - columns: 指定去重分组字段,控制唯一性分组行为。若 columns 有值且不包含 date_column,则全表去重,否则按天分区。
43
+ - exclude_columns: 去重时排除的列名列表,自动合并 ['id', '更新时间'],在分组时排除。
44
+ - date_column: 指定日期分区字段,默认为 '日期'。如表存在该字段且 columns 未排除,则按天分区去重。
45
+ - duplicate_keep_mode: 'keep_one'(默认,重复组保留一条),'remove_all'(全部删除重复组)。
46
+ - dry_run: 是否为模拟运行,不实际删除数据。
47
+ - use_python_dedup: 是否用 Python 方式去重(否则用 SQL)。
48
+
49
+ 分天/全表去重行为:
50
+ - 若 columns 有值且不包含 date_column,则直接全表去重,分组字段为 columns。
51
+ - 否则,若表存在 date_column,则按天分区去重,分组字段为 columns(如有)或全表字段。
52
+ - exclude_columns 始终生效,分组时自动排除。
38
53
  """
39
54
 
40
55
  def __init__(
@@ -370,15 +385,17 @@ class MySQLDeduplicator:
370
385
  lock_table: bool = True
371
386
  ) -> Tuple[int, int]:
372
387
  """
373
- 执行单表单天去重。只处理 date_val 这一天的数据(如果有 date_column),否则全表。
374
-
388
+ 执行单表单天或全表去重。根据 columns date_column 决定分天或全表:
389
+ - 若 columns 不包含 date_column,则全表去重(不分天)。
390
+ - 若 columns 包含 date_column 或未指定 columns,则按天分区去重(date_val 为当前处理日期)。
391
+
375
392
  Args:
376
393
  database (str): 数据库名。
377
394
  table (str): 表名。
378
- columns (Optional[List[str]]): 指定去重列。
395
+ columns (Optional[List[str]]): 指定去重列。若不含 date_column,则全表去重。
379
396
  dry_run (bool): 是否为模拟运行。
380
397
  use_python_dedup (bool): 是否用 Python 方式去重。
381
- date_val (Optional[str]): 指定处理的日期(如有 date_column)。
398
+ date_val (Optional[str]): 指定处理的日期(如有 date_column 且分天时)。
382
399
  lock_table (bool): 是否加表级锁。
383
400
  Returns:
384
401
  Tuple[int, int]: (重复组数, 实际删除行数)
@@ -392,13 +409,12 @@ class MySQLDeduplicator:
392
409
  exclude_columns_lower = [col.lower() for col in getattr(self, 'exclude_columns', [])]
393
410
  time_col = self.date_column
394
411
  time_col_lower = time_col.lower() if time_col else None
395
- if time_col_lower and time_col_lower in exclude_columns_lower:
396
- logger.warning('date_column在exclude_columns中,跳过该表', {"库": database, "表": table, "date_column": time_col, "exclude_columns": self.exclude_columns})
397
- return (0, 0)
398
- has_time_col = time_col_lower in all_columns_lower if time_col_lower else False
399
-
400
- # 只要有date_column,始终分天处理(本函数只处理一天)
401
- if has_time_col and date_val is not None:
412
+ # 如果传了columns且columns不包含date_column,则不分天,直接全表去重
413
+ if columns and (not time_col_lower or time_col_lower not in [c.lower() for c in columns]):
414
+ has_time_col = False # 全表去重
415
+ else:
416
+ has_time_col = time_col_lower in all_columns_lower if time_col_lower else False # 分天去重
417
+ if has_time_col:
402
418
  self._ensure_index(database, table, time_col)
403
419
  # 获取去重列
404
420
  use_columns = columns or all_columns
@@ -442,14 +458,11 @@ class MySQLDeduplicator:
442
458
  if not dry_run and del_ids:
443
459
  with self._conn_ctx() as conn:
444
460
  with conn.cursor() as cursor:
445
- for i in range(0, len(del_ids), self.batch_size):
446
- batch_ids = del_ids[i:i+self.batch_size]
447
- del_ids_str = ','.join([str(i) for i in batch_ids])
448
- delete_sql = f"DELETE FROM `{database}`.`{table}` WHERE `{pk_real}` IN ({del_ids_str})"
449
- cursor.execute(delete_sql)
450
- batch_deleted = cursor.rowcount
451
- affected_rows += batch_deleted
452
- conn.commit()
461
+ format_ids = ','.join([str(i) for i in del_ids])
462
+ del_sql = f"DELETE FROM `{database}`.`{table}` WHERE `{pk_real}` IN ({format_ids})"
463
+ cursor.execute(del_sql)
464
+ affected_rows = cursor.rowcount
465
+ conn.commit()
453
466
  logger.debug('去重完成', {"库": database, "表": table, "数据量": total_count, "重复组": dup_count, "实际删除": affected_rows, "去重方式": "Python", "数据处理": self.duplicate_keep_mode, "数据日期": date_val})
454
467
  return (dup_count, affected_rows)
455
468
  # SQL方式查找重复
@@ -652,12 +665,20 @@ class MySQLDeduplicator:
652
665
  use_python_dedup: bool = True
653
666
  ) -> Tuple[int, int]:
654
667
  """
655
- 对指定表进行去重。始终按天分区(如有 date_column),否则全表。
656
-
668
+ 对指定表进行去重。
669
+
670
+ 去重行为说明:
671
+ - 若 columns 参数传入且不包含 date_column,则全表直接按 columns 去重。
672
+ - 若 columns 包含 date_column 或未指定 columns,则按天分区去重(每一天独立去重)。
673
+ - exclude_columns 中的字段始终不会参与去重分组。
674
+ - date_column 默认为 '日期',可自定义。
675
+ - dry_run 模式下仅统计重复组和待删除行数,不实际删除。
676
+ - reorder_id=True 时,去重后自动重排主键 id。
677
+
657
678
  Args:
658
679
  database (str): 数据库名。
659
680
  table (str): 表名。
660
- columns (Optional[List[str]]): 指定去重列。
681
+ columns (Optional[List[str]]): 指定去重列。若不含 date_column,则全表去重。
661
682
  dry_run (bool): 是否为模拟运行。
662
683
  reorder_id (bool): 去重后是否自动重排 id 列。
663
684
  use_python_dedup (bool): 是否用 Python 方式去重。
@@ -686,7 +707,11 @@ class MySQLDeduplicator:
686
707
  all_columns_lower = [col.lower() for col in all_columns]
687
708
  time_col = self.date_column
688
709
  time_col_lower = time_col.lower() if time_col else None
689
- has_time_col = time_col_lower in all_columns_lower if time_col_lower else False
710
+ # 如果传了columns且columns不包含date_column,则不分天,直接全表去重
711
+ if columns and (not time_col_lower or time_col_lower not in [c.lower() for c in columns]):
712
+ has_time_col = False # 全表去重
713
+ else:
714
+ has_time_col = time_col_lower in all_columns_lower if time_col_lower else False # 分天去重
690
715
  if has_time_col:
691
716
  self._ensure_index(database, table, time_col)
692
717
  all_dates = self._get_all_dates(database, table, time_col)
@@ -1294,7 +1319,7 @@ def main():
1294
1319
  password='pwd',
1295
1320
  host='localhost',
1296
1321
  port=3306,
1297
- max_workers= 3,
1322
+ max_workers= 2,
1298
1323
  batch_size=1000,
1299
1324
  skip_system_dbs=True,
1300
1325
  max_retries=3,
@@ -1302,7 +1327,7 @@ def main():
1302
1327
  pool_size=30,
1303
1328
  recent_month=1,
1304
1329
  # date_range=['2025-06-09', '2025-06-10'],
1305
- date_column='日期',
1330
+ exclude_columns=['更新时间'],
1306
1331
  # exclude_databases=['测试库4'],
1307
1332
  # exclude_tables={
1308
1333
  # '推广数据2': [
@@ -1316,13 +1341,13 @@ def main():
1316
1341
  )
1317
1342
 
1318
1343
  # 全库去重(单线程)
1319
- deduplicator.deduplicate_all(dry_run=False, parallel=True, reorder_id=True)
1344
+ # deduplicator.deduplicate_all(dry_run=False, parallel=True, reorder_id=True)
1320
1345
 
1321
1346
  # # 指定数据库去重(多线程)
1322
1347
  # deduplicator.deduplicate_database('数据引擎2', dry_run=False, parallel=True, reorder_id=True)
1323
1348
 
1324
1349
  # # 指定表去重(使用特定列)
1325
- # deduplicator.deduplicate_table('my_db', 'my_table', columns=['name', 'data'], dry_run=False, reorder_id=True)
1350
+ deduplicator.deduplicate_table('安全组', '腾讯云cvm规则', columns=['平台', '本地主机', '端口范围', '授权ip'], dry_run=False, reorder_id=True)
1326
1351
 
1327
1352
  # # 重排id列
1328
1353
  # deduplicator.reorder_id_column('my_db', 'my_table', 'id', dry_run=False, auto_drop_backup=True)
@@ -1331,5 +1356,5 @@ def main():
1331
1356
  deduplicator.close()
1332
1357
 
1333
1358
  if __name__ == '__main__':
1334
- main()
1359
+ # main()
1335
1360
  pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mdbq
3
- Version: 3.12.2
3
+ Version: 3.12.3
4
4
  Home-page: https://pypi.org/project/mdbq
5
5
  Author: xigua,
6
6
  Author-email: 2587125111@qq.com
@@ -1,5 +1,5 @@
1
1
  mdbq/__init__.py,sha256=Il5Q9ATdX8yXqVxtP_nYqUhExzxPC_qk_WXQ_4h0exg,16
2
- mdbq/__version__.py,sha256=7f_XGixBIsGAP5w1W3y_kTPfLVlurDoRsv3FOOJLDIA,18
2
+ mdbq/__version__.py,sha256=9x3pJeqVdjIfn83Ln01YuTBiHKPCTeK3xkXQT4NcYno,18
3
3
  mdbq/aggregation/__init__.py,sha256=EeDqX2Aml6SPx8363J-v1lz0EcZtgwIBYyCJV6CcEDU,40
4
4
  mdbq/aggregation/query_data.py,sha256=nxL8hSy8yI1QLlqnkTNHHQSxRfo-6WKL5OA-N4xLB7c,179832
5
5
  mdbq/config/__init__.py,sha256=jso1oHcy6cJEfa7udS_9uO5X6kZLoPBF8l3wCYmr5dM,18
@@ -8,7 +8,7 @@ mdbq/log/__init__.py,sha256=Mpbrav0s0ifLL7lVDAuePEi1hJKiSHhxcv1byBKDl5E,15
8
8
  mdbq/log/mylogger.py,sha256=Crw6LwVo3I3IUbzIETu8f46Quza3CTCh-qYf4edbBPo,24139
9
9
  mdbq/log/spider_logging.py,sha256=-ozWWEGm3HVv604ozs_OOvVwumjokmUPwbaodesUrPY,1664
10
10
  mdbq/mysql/__init__.py,sha256=A_DPJyAoEvTSFojiI2e94zP0FKtCkkwKP1kYUCSyQzo,11
11
- mdbq/mysql/deduplicator.py,sha256=eILGFxFtMCSR6dvCgEgsOjwlK_hEWBe2dFSgJTPHfj8,68623
11
+ mdbq/mysql/deduplicator.py,sha256=uBRM2cBF-gzkFFrmBSKqBd_LLO-K67LYUJqpF9Fs928,70561
12
12
  mdbq/mysql/mysql.py,sha256=Kjpi-LL00WQUmTTOfhEBsNrmo4-4kFFJzrHbVKfqiBE,56770
13
13
  mdbq/mysql/s_query.py,sha256=dlnrVJ3-Vp1Suv9CNbPxyYSRqRJUHjOpF39tb2F-wBc,10190
14
14
  mdbq/mysql/uploader.py,sha256=szX6t4SObBF6fbHT2s5ixfh1-c288cigsJ66pFE02Qg,70266
@@ -24,7 +24,7 @@ mdbq/redis/__init__.py,sha256=YtgBlVSMDphtpwYX248wGge1x-Ex_mMufz4-8W0XRmA,12
24
24
  mdbq/redis/getredis.py,sha256=YHgCKO8mEsslwet33K5tGss-nrDDwPnOSlhA9iBu0jY,24078
25
25
  mdbq/spider/__init__.py,sha256=RBMFXGy_jd1HXZhngB2T2XTvJqki8P_Fr-pBcwijnew,18
26
26
  mdbq/spider/aikucun.py,sha256=cqK-JRd_DHbToC7hyo83m8o97NZkJFqmB2xBtr6aAVU,20961
27
- mdbq-3.12.2.dist-info/METADATA,sha256=1V071qDvuX5apscDGFLY_ICX6mMVBUhjzMGh_a_wd7w,364
28
- mdbq-3.12.2.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
29
- mdbq-3.12.2.dist-info/top_level.txt,sha256=2FQ-uLnCSB-OwFiWntzmwosW3X2Xqsg0ewh1axsaylA,5
30
- mdbq-3.12.2.dist-info/RECORD,,
27
+ mdbq-3.12.3.dist-info/METADATA,sha256=kkujbmKfbA4mOHCloHBALjK3jI2sNoft1yyHmpbnmoI,364
28
+ mdbq-3.12.3.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
29
+ mdbq-3.12.3.dist-info/top_level.txt,sha256=2FQ-uLnCSB-OwFiWntzmwosW3X2Xqsg0ewh1axsaylA,5
30
+ mdbq-3.12.3.dist-info/RECORD,,
File without changes