-- 已建档运输证回填购买证关联,并补上该购买证的合同和合同产品。 -- MySQL 5.7。执行前备份 pl_transport_cert_purchase_ref、pl_contract_cert_link、pl_contract_cert_product。 -- 临时表使用 utf8mb4_general_ci,与业务表一致;库默认 unicode_ci 时直接比较会报 1267。 -- 可重复执行。只补空缺,不改已经选过的购买证,不改、不删运输证上已有合同。 -- -- 规则与证件建档识别后的自动关联一致: -- 1. 引用证号与购买证证号去掉首尾空格后按二进制完全一致,且同一租户只命中一张未删除购买证,才写入购买证。 -- 命中 0 张或 2 张及以上保持为空。已经填写的购买证不覆盖。 -- 2. 仅针对本次新关联上的购买证,把它尚未出现在该运输证上的合同和产品复制过去。 -- 新记录使用新主键,不沿用购买证那边的关联 ID、产品范围 ID。 -- 合同已在运输证上存在(含已软删除)则整份跳过,不合并、不恢复。 -- 购买证上没有仍有效的合同产品的合同不复制。合同或产品行已删除的不复制。 -- 3. 作废、已核销的运输证只补购买证引用,不新增合同范围。 -- -- 只读核对想看结果、先不落库时,把文末 COMMIT 改成 ROLLBACK。 SET NAMES utf8mb4; SET SESSION group_concat_max_len = 1048576; DROP TEMPORARY TABLE IF EXISTS tmp_transport_purchase_match; DROP TEMPORARY TABLE IF EXISTS tmp_transport_contract_copy; CREATE TEMPORARY TABLE tmp_transport_purchase_match ( ref_id varchar(64) NOT NULL, tenant_no varchar(64) NOT NULL, transport_cert_id varchar(64) NOT NULL, transport_cert_no varchar(100) NOT NULL, transport_status varchar(20) NOT NULL, create_dept_id varchar(64) NOT NULL, purchase_cert_id varchar(64) NOT NULL, purchase_cert_no varchar(100) NOT NULL, reference_no varchar(100) NOT NULL, PRIMARY KEY (ref_id), KEY idx_match_transport (tenant_no, transport_cert_id), KEY idx_match_purchase (tenant_no, purchase_cert_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; CREATE TEMPORARY TABLE tmp_transport_contract_copy ( new_link_id varchar(64) NOT NULL, source_link_id varchar(64) NOT NULL, tenant_no varchar(64) NOT NULL, transport_cert_id varchar(64) NOT NULL, contract_id varchar(64) NOT NULL, contract_no varchar(100) NOT NULL, link_status varchar(16) NOT NULL, order_num int DEFAULT NULL, create_dept_id varchar(64) NOT NULL, PRIMARY KEY (new_link_id), UNIQUE KEY uk_transport_contract (tenant_no, transport_cert_id, contract_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; START TRANSACTION; INSERT INTO tmp_transport_purchase_match ( ref_id, tenant_no, transport_cert_id, transport_cert_no, transport_status, create_dept_id, purchase_cert_id, purchase_cert_no, reference_no ) SELECT ref.id, ref.tenant_no, ref.transport_cert_id, transport.cert_no, transport.status, transport.create_dept_id, MIN(purchase.id), MIN(purchase.cert_no), ref.reference_no FROM pl_transport_cert_purchase_ref ref JOIN pl_transport_cert transport ON transport.id = ref.transport_cert_id AND transport.tenant_no = ref.tenant_no AND transport.del_flag = 0 JOIN pl_purchase_cert purchase ON purchase.tenant_no = ref.tenant_no AND purchase.del_flag = 0 AND CHAR_LENGTH(TRIM(purchase.cert_no)) > 0 AND BINARY TRIM(purchase.cert_no) = BINARY TRIM(ref.reference_no) WHERE ref.del_flag = 0 AND CHAR_LENGTH(TRIM(IFNULL(ref.purchase_cert_id, ''))) = 0 AND CHAR_LENGTH(TRIM(ref.reference_no)) > 0 GROUP BY ref.id, ref.tenant_no, ref.transport_cert_id, transport.cert_no, transport.status, transport.create_dept_id, ref.reference_no HAVING COUNT(*) = 1; -- 引用证号命中多张购买证:不自动关联,留给人工核对。 SELECT ref.tenant_no, transport.cert_no AS transport_cert_no, ref.reference_no, COUNT(*) AS purchase_hit_count, GROUP_CONCAT(purchase.cert_no ORDER BY purchase.cert_no SEPARATOR '、') AS purchase_cert_nos FROM pl_transport_cert_purchase_ref ref JOIN pl_transport_cert transport ON transport.id = ref.transport_cert_id AND transport.tenant_no = ref.tenant_no AND transport.del_flag = 0 JOIN pl_purchase_cert purchase ON purchase.tenant_no = ref.tenant_no AND purchase.del_flag = 0 AND CHAR_LENGTH(TRIM(purchase.cert_no)) > 0 AND BINARY TRIM(purchase.cert_no) = BINARY TRIM(ref.reference_no) WHERE ref.del_flag = 0 AND CHAR_LENGTH(TRIM(IFNULL(ref.purchase_cert_id, ''))) = 0 AND CHAR_LENGTH(TRIM(ref.reference_no)) > 0 GROUP BY ref.tenant_no, transport.cert_no, ref.reference_no HAVING COUNT(*) > 1; INSERT INTO tmp_transport_contract_copy ( new_link_id, source_link_id, tenant_no, transport_cert_id, contract_id, contract_no, link_status, order_num, create_dept_id ) SELECT UPPER(REPLACE(UUID(), '-', '')), source_link.id, picked.tenant_no, picked.transport_cert_id, picked.contract_id, IFNULL(contract.contract_no, ''), IF(source_link.link_status = 'inactive', 'inactive', 'active'), source_link.order_num, picked.create_dept_id FROM ( SELECT matched.tenant_no, matched.transport_cert_id, source_link.contract_id, matched.create_dept_id, SUBSTRING_INDEX(GROUP_CONCAT(source_link.id ORDER BY matched.ref_id, source_link.id SEPARATOR ','), ',', 1) AS source_link_id FROM tmp_transport_purchase_match matched JOIN pl_contract_cert_link source_link ON source_link.tenant_no = matched.tenant_no AND source_link.cert_type = 'purchase' AND source_link.cert_id = matched.purchase_cert_id AND source_link.del_flag = 0 JOIN crm_contract contract ON contract.id = source_link.contract_id AND contract.tenant_no = source_link.tenant_no AND contract.del_flag = 0 WHERE matched.transport_status NOT IN ('void', 'closed') AND EXISTS ( -- MySQL 5.7 不能在子查询 JOIN 的 ON 里引用外层 source_link,合同条件放在 WHERE。 SELECT 1 FROM pl_contract_cert_product source_product JOIN crm_contract_product contract_product ON contract_product.id = source_product.contract_product_id AND contract_product.tenant_no = source_product.tenant_no AND contract_product.del_flag = 0 WHERE source_product.contract_cert_link_id = source_link.id AND source_product.tenant_no = source_link.tenant_no AND source_product.del_flag = 0 AND CHAR_LENGTH(TRIM(source_product.contract_product_id)) > 0 AND contract_product.contract_id = source_link.contract_id ) AND NOT EXISTS ( SELECT 1 FROM pl_contract_cert_link existing WHERE existing.tenant_no = matched.tenant_no AND existing.contract_id = source_link.contract_id AND existing.cert_type = 'transport' AND existing.cert_id = matched.transport_cert_id ) GROUP BY matched.tenant_no, matched.transport_cert_id, source_link.contract_id, matched.create_dept_id ) picked JOIN pl_contract_cert_link source_link ON source_link.id = picked.source_link_id JOIN crm_contract contract ON contract.id = picked.contract_id AND contract.tenant_no = picked.tenant_no AND contract.del_flag = 0; -- 本次将补上的购买证引用。 SELECT tenant_no, transport_cert_no, reference_no, purchase_cert_no, transport_status FROM tmp_transport_purchase_match ORDER BY tenant_no, transport_cert_no, reference_no; -- 本次将补上的合同。作废、已核销不在其中。 SELECT copy_row.tenant_no, transport.cert_no AS transport_cert_no, copy_row.contract_no, copy_row.link_status FROM tmp_transport_contract_copy copy_row JOIN pl_transport_cert transport ON transport.id = copy_row.transport_cert_id AND transport.tenant_no = copy_row.tenant_no ORDER BY copy_row.tenant_no, transport.cert_no, copy_row.contract_no; UPDATE pl_transport_cert_purchase_ref ref_row JOIN tmp_transport_purchase_match matched ON matched.ref_id = ref_row.id SET ref_row.purchase_cert_id = matched.purchase_cert_id, ref_row.update_by = 'system', ref_row.update_time = NOW() WHERE ref_row.del_flag = 0 AND CHAR_LENGTH(TRIM(IFNULL(ref_row.purchase_cert_id, ''))) = 0; INSERT INTO pl_contract_cert_link ( id, contract_id, contract_no_snapshot, cert_type, cert_id, link_status, scope_revision, tenant_no, order_num, remark, create_by, create_dept_id, create_time, update_by, update_time, del_flag ) SELECT new_link_id, contract_id, contract_no, 'transport', transport_cert_id, link_status, 1, tenant_no, order_num, NULL, 'system', create_dept_id, NOW(), 'system', NOW(), 0 FROM tmp_transport_contract_copy; INSERT INTO pl_contract_cert_product ( id, contract_cert_link_id, contract_product_id, scope_status, tenant_no, order_num, remark, create_by, create_dept_id, create_time, update_by, update_time, del_flag ) SELECT UPPER(REPLACE(UUID(), '-', '')), copy_row.new_link_id, source_product.contract_product_id, IF(source_product.scope_status = 'inactive', 'inactive', 'active'), copy_row.tenant_no, source_product.order_num, NULL, 'system', copy_row.create_dept_id, NOW(), 'system', NOW(), 0 FROM tmp_transport_contract_copy copy_row JOIN pl_contract_cert_product source_product ON source_product.contract_cert_link_id = copy_row.source_link_id AND source_product.tenant_no = copy_row.tenant_no AND source_product.del_flag = 0 AND CHAR_LENGTH(TRIM(source_product.contract_product_id)) > 0 JOIN crm_contract_product contract_product ON contract_product.id = source_product.contract_product_id AND contract_product.contract_id = copy_row.contract_id AND contract_product.tenant_no = source_product.tenant_no AND contract_product.del_flag = 0; -- MySQL 5.7 同一条语句不能重复打开临时表,汇总拆开查询。 SELECT COUNT(*) AS linked_purchase_refs FROM tmp_transport_purchase_match; SELECT COUNT(*) AS copied_contracts FROM tmp_transport_contract_copy; SELECT COUNT(*) AS copied_products FROM tmp_transport_contract_copy copy_row JOIN pl_contract_cert_product source_product ON source_product.contract_cert_link_id = copy_row.source_link_id AND source_product.tenant_no = copy_row.tenant_no AND source_product.del_flag = 0 JOIN crm_contract_product contract_product ON contract_product.id = source_product.contract_product_id AND contract_product.contract_id = copy_row.contract_id AND contract_product.tenant_no = source_product.tenant_no AND contract_product.del_flag = 0; COMMIT; DROP TEMPORARY TABLE IF EXISTS tmp_transport_purchase_match; DROP TEMPORARY TABLE IF EXISTS tmp_transport_contract_copy;