diff --git a/.codex-police-test-pom.xml b/.codex-police-test-pom.xml
deleted file mode 100644
index decf7a0..0000000
--- a/.codex-police-test-pom.xml
+++ /dev/null
@@ -1,374 +0,0 @@
-
-
- 4.0.0
-
- org.springframework.boot
- spring-boot-starter-parent
- 3.3.2
-
-
- com.biutag
- supervision
- 0.0.1-SNAPSHOT
- supervision
- 长沙公安数据督察
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 17
-
-
-
-
- org.jfree
- jfreechart
- 1.5.4
-
-
-
- com.deepoove
- poi-tl
- 1.12.2
-
-
- org.apache.poi
- poi
-
-
- org.apache.poi
- poi-ooxml
-
-
- org.apache.poi
- poi-ooxml-lite
-
-
-
-
-
- org.projectlombok
- lombok
- true
-
-
-
-
- org.mapstruct
- mapstruct
- 1.5.5.Final
-
-
-
-
- org.mapstruct
- mapstruct-processor
- 1.5.5.Final
- provided
-
-
-
-
- com.google.guava
- guava
- 32.1.2-jre
-
-
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
-
- org.springframework.boot
- spring-boot-starter-validation
-
-
-
- org.springframework.boot
- spring-boot-starter-data-redis-reactive
-
-
-
-
-
- com.dameng
- DmJdbcDriver18
- 8.1.3.140
-
-
-
-
-
-
-
-
-
-
-
- com.mysql
- mysql-connector-j
- runtime
-
-
-
-
- com.microsoft.sqlserver
- mssql-jdbc
- 12.8.1.jre11
-
-
- javax.xml.bind
- jaxb-api
- 2.3.0
-
-
- com.sun.xml.bind
- jaxb-impl
- 2.3.0
-
-
- com.sun.xml.bind
- jaxb-core
- 2.3.0
-
-
- javax.activation
- activation
- 1.1.1
-
-
-
-
- com.gbase
- gbase-jdbc
- system
- 8.3.81.53-build55.2.1-bin
- ${project.basedir}/src/main/resources/lib/gbase-connector-java-8.3.81.53-build52.8-bin.jar
-
-
-
-
- com.dameng
- DmJdbcDriver18
- 8.1.3.140
-
-
-
-
- org.postgresql
- postgresql
- 42.7.4
-
-
-
- com.alibaba
- druid-spring-boot-starter
- 1.2.22
-
-
-
- org.projectlombok
- lombok
- true
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
-
- com.baomidou
- mybatis-plus-spring-boot3-starter
- 3.5.7
-
-
-
- com.alibaba
- easyexcel
- 4.0.1
-
-
-
- commons-io
- commons-io
- 2.17.0
-
-
-
- cn.hutool
- hutool-all
- 5.8.29
-
-
-
-
- cn.hutool
- hutool-core
- 5.8.29
-
-
- cn.hutool
- hutool-http
- 5.8.29
-
-
-
- com.baomidou
- dynamic-datasource-spring-boot3-starter
- 4.3.1
-
-
-
- org.freemarker
- freemarker
- 2.3.32
-
-
-
- org.springframework.security
- spring-security-crypto
-
-
-
- com.github.tobato
- fastdfs-client
- 1.27.2
-
-
-
-
- com.github.xiaoymin
- knife4j-openapi3-jakarta-spring-boot-starter
- 4.1.0
-
-
-
- com.alibaba
- fastjson
- 2.0.53
-
-
-
- net.logstash.logback
- logstash-logback-encoder
- 4.9
-
-
-
-
- com.aspose
- aspose-words
- 15.8.0
-
-
- org.springframework.boot
- spring-boot-starter-actuator
-
-
-
-
- cn.com.jit
- authSDK
- 3.0.3
-
-
- commons-httpclient
- commons-httpclient
- 3.0.1
-
-
-
- org.jsoup
- jsoup
- 1.17.2
-
-
-
-
-
-
-
- src/main/resources
- false
-
-
-
- src/main/resources
-
- application.yml
-
- true
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
- 3.2.5
-
- false
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
- true
-
-
- org.projectlombok
- lombok
-
-
-
-
-
-
- supervision
-
-
-
-
- local
-
-
- local
-
-
-
-
- true
-
-
-
- dev
-
-
- dev
-
-
-
- prod
-
- prod
-
-
-
-
-
diff --git a/pom.codex-query-verify.xml b/pom.codex-query-verify.xml
deleted file mode 100644
index decf7a0..0000000
--- a/pom.codex-query-verify.xml
+++ /dev/null
@@ -1,374 +0,0 @@
-
-
- 4.0.0
-
- org.springframework.boot
- spring-boot-starter-parent
- 3.3.2
-
-
- com.biutag
- supervision
- 0.0.1-SNAPSHOT
- supervision
- 长沙公安数据督察
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 17
-
-
-
-
- org.jfree
- jfreechart
- 1.5.4
-
-
-
- com.deepoove
- poi-tl
- 1.12.2
-
-
- org.apache.poi
- poi
-
-
- org.apache.poi
- poi-ooxml
-
-
- org.apache.poi
- poi-ooxml-lite
-
-
-
-
-
- org.projectlombok
- lombok
- true
-
-
-
-
- org.mapstruct
- mapstruct
- 1.5.5.Final
-
-
-
-
- org.mapstruct
- mapstruct-processor
- 1.5.5.Final
- provided
-
-
-
-
- com.google.guava
- guava
- 32.1.2-jre
-
-
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
-
- org.springframework.boot
- spring-boot-starter-validation
-
-
-
- org.springframework.boot
- spring-boot-starter-data-redis-reactive
-
-
-
-
-
- com.dameng
- DmJdbcDriver18
- 8.1.3.140
-
-
-
-
-
-
-
-
-
-
-
- com.mysql
- mysql-connector-j
- runtime
-
-
-
-
- com.microsoft.sqlserver
- mssql-jdbc
- 12.8.1.jre11
-
-
- javax.xml.bind
- jaxb-api
- 2.3.0
-
-
- com.sun.xml.bind
- jaxb-impl
- 2.3.0
-
-
- com.sun.xml.bind
- jaxb-core
- 2.3.0
-
-
- javax.activation
- activation
- 1.1.1
-
-
-
-
- com.gbase
- gbase-jdbc
- system
- 8.3.81.53-build55.2.1-bin
- ${project.basedir}/src/main/resources/lib/gbase-connector-java-8.3.81.53-build52.8-bin.jar
-
-
-
-
- com.dameng
- DmJdbcDriver18
- 8.1.3.140
-
-
-
-
- org.postgresql
- postgresql
- 42.7.4
-
-
-
- com.alibaba
- druid-spring-boot-starter
- 1.2.22
-
-
-
- org.projectlombok
- lombok
- true
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
-
-
- com.baomidou
- mybatis-plus-spring-boot3-starter
- 3.5.7
-
-
-
- com.alibaba
- easyexcel
- 4.0.1
-
-
-
- commons-io
- commons-io
- 2.17.0
-
-
-
- cn.hutool
- hutool-all
- 5.8.29
-
-
-
-
- cn.hutool
- hutool-core
- 5.8.29
-
-
- cn.hutool
- hutool-http
- 5.8.29
-
-
-
- com.baomidou
- dynamic-datasource-spring-boot3-starter
- 4.3.1
-
-
-
- org.freemarker
- freemarker
- 2.3.32
-
-
-
- org.springframework.security
- spring-security-crypto
-
-
-
- com.github.tobato
- fastdfs-client
- 1.27.2
-
-
-
-
- com.github.xiaoymin
- knife4j-openapi3-jakarta-spring-boot-starter
- 4.1.0
-
-
-
- com.alibaba
- fastjson
- 2.0.53
-
-
-
- net.logstash.logback
- logstash-logback-encoder
- 4.9
-
-
-
-
- com.aspose
- aspose-words
- 15.8.0
-
-
- org.springframework.boot
- spring-boot-starter-actuator
-
-
-
-
- cn.com.jit
- authSDK
- 3.0.3
-
-
- commons-httpclient
- commons-httpclient
- 3.0.1
-
-
-
- org.jsoup
- jsoup
- 1.17.2
-
-
-
-
-
-
-
- src/main/resources
- false
-
-
-
- src/main/resources
-
- application.yml
-
- true
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
- 3.2.5
-
- false
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
- true
-
-
- org.projectlombok
- lombok
-
-
-
-
-
-
- supervision
-
-
-
-
- local
-
-
- local
-
-
-
-
- true
-
-
-
- dev
-
-
- dev
-
-
-
- prod
-
- prod
-
-
-
-
-
diff --git a/sql/20261027_profile_query_column_check.sql b/sql/20261027_profile_query_column_check.sql
new file mode 100644
index 0000000..6f242be
--- /dev/null
+++ b/sql/20261027_profile_query_column_check.sql
@@ -0,0 +1,43 @@
+-- MySQL 5.7.21:只读核查关联字段,不查询业务明细。
+-- 在 result.txt 对应的业务库执行,返回全部结果集。
+SELECT VERSION(), DATABASE(), @@character_set_connection, @@collation_connection;
+
+SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE,
+ CHARACTER_SET_NAME, COLLATION_NAME, COLUMN_KEY
+FROM information_schema.COLUMNS
+WHERE TABLE_SCHEMA = DATABASE()
+ AND (
+ (TABLE_NAME = 'negative' AND COLUMN_NAME IN ('id', 'involveDepartId', 'crtTime', 'discoveryTime', 'processing_status', 'checkStatus'))
+ OR (TABLE_NAME = 'negative_blame' AND COLUMN_NAME IN ('negativeId', 'blameIdCode', 'type'))
+ OR (TABLE_NAME = 'sup_depart' AND COLUMN_NAME IN ('id', 'pid'))
+ OR (TABLE_NAME = 'sup_depart_police_size' AND COLUMN_NAME = 'depart_id')
+ OR (TABLE_NAME = 'sup_police' AND COLUMN_NAME IN ('id_code', 'org_id', 'del'))
+ )
+ORDER BY TABLE_NAME, ORDINAL_POSITION;
+
+-- JSON 计划中的 attached_condition 可显示隐式转换,SHOW WARNINGS 紧随 EXPLAIN 执行。
+EXPLAIN FORMAT=JSON
+SELECT n.id, nb.blameIdCode
+FROM negative n
+LEFT JOIN negative_blame nb ON nb.negativeId = n.id
+WHERE n.crtTime BETWEEN '1949-01-01 00:00:00' AND NOW()
+LIMIT 10;
+SHOW WARNINGS;
+
+EXPLAIN FORMAT=JSON
+SELECT d.id, p.police_size
+FROM sup_depart d
+LEFT JOIN sup_depart_police_size p ON p.depart_id = d.id
+WHERE d.level = 3
+LIMIT 10;
+SHOW WARNINGS;
+
+EXPLAIN FORMAT=JSON
+SELECT d.id
+FROM sup_depart d
+WHERE d.level = 3 AND EXISTS (
+ SELECT 1 FROM negative n
+ WHERE n.involveDepartId = d.id
+ AND n.crtTime BETWEEN '1949-01-01 00:00:00' AND NOW()
+);
+SHOW WARNINGS;
diff --git a/sql/20261027_profile_query_explain.sql b/sql/20261027_profile_query_explain.sql
index 344b40c..ac4f48a 100644
--- a/sql/20261027_profile_query_explain.sql
+++ b/sql/20261027_profile_query_explain.sql
@@ -31,12 +31,12 @@ SELECT
LEFT JOIN negative_blame nb ON n.id = nb.negativeId
WHERE d.level = 3
AND n.crtTime BETWEEN @beginTime AND @endTime
-
-
-
+
+
+
GROUP BY d.id, d1.short_name, d.short_name, p.police_size, p.aux_size
ORDER BY
- verify_size
+ verify_size
DESC , d.id ASC
LIMIT 0, 10;
@@ -58,9 +58,9 @@ SELECT
LEFT JOIN negative_blame nb ON n.id = nb.negativeId
WHERE d.level = 3
AND n.crtTime BETWEEN @beginTime AND @endTime
-
-
-
+
+
+
GROUP BY d.id, d1.short_name, d.short_name, p.police_size, p.aux_size
) counted;
@@ -79,22 +79,22 @@ SELECT DISTINCT
count(DISTINCT nb.blameIdCode) verify_police_size,
count(DISTINCT n.id) verify_size
FROM sup_depart d
- INNER JOIN negative n ON d.id = n.involveDepartId
- LEFT JOIN negative_blame nb ON n.id = nb.negativeId
- WHERE
+ INNER JOIN negative n ON n.involveDepartId = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
+ LEFT JOIN negative_blame nb ON nb.negativeId = CONVERT(n.id USING utf8) COLLATE utf8_general_ci
+ WHERE
d.level = 3
-
-
-
-
+
+
+
+
AND n.crtTime BETWEEN @beginTime AND @endTime
GROUP BY d.id
) stats
INNER JOIN sup_depart d ON d.id = stats.depart_id
LEFT JOIN sup_depart d1 ON d.pid = d1.id
- LEFT JOIN sup_depart_police_size p ON p.depart_id = d.id
+ LEFT JOIN sup_depart_police_size p ON p.depart_id = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
ORDER BY
- verify_size
+ verify_size
DESC , d.id ASC
LIMIT 0, 10;
@@ -103,16 +103,16 @@ EXPLAIN
SELECT count(*) FROM (
SELECT DISTINCT d.id, p.police_size, p.aux_size
FROM sup_depart d
- LEFT JOIN sup_depart_police_size p ON p.depart_id = d.id
- WHERE
+ LEFT JOIN sup_depart_police_size p ON p.depart_id = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
+ WHERE
d.level = 3
-
-
-
-
+
+
+
+
AND EXISTS (
SELECT 1 FROM negative n
- WHERE n.involveDepartId = d.id
+ WHERE n.involveDepartId = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
AND n.crtTime BETWEEN @beginTime AND @endTime
)
) counted;
@@ -141,11 +141,11 @@ SELECT
AND n.checkStatus IN ('1', '2')
AND n.processing_status = 'completed'
AND n.discoveryTime BETWEEN @beginTime AND @endTime
-
-
-
-
+
+
+
+
GROUP BY
p.id_code,
p.NAME,
@@ -186,11 +186,11 @@ SELECT
AND n.checkStatus IN ('1', '2')
AND n.processing_status = 'completed'
AND n.discoveryTime BETWEEN @beginTime AND @endTime
-
-
-
-
+
+
+
+
GROUP BY
p.id_code,
p.NAME,
@@ -205,7 +205,7 @@ SELECT
-- 个人问题库:优化后列表查询(第一页 10 条)
EXPLAIN
-SELECT DISTINCT
+SELECT DISTINCT
p.id_code, p.name, p.emp_no, p.position, p.employment_date, p.person_type,
d.short_name depart_name, d.id depart_id, d1.short_name parent_depart_name
, stats.verify_size
@@ -213,24 +213,24 @@ SELECT DISTINCT
SELECT nb.blameIdCode, count(DISTINCT n.id) verify_size
FROM negative n
INNER JOIN negative_blame nb ON nb.negativeId = n.id
- WHERE
+ WHERE
nb.type = 'personal'
AND n.checkStatus IN ('1', '2')
AND n.processing_status = 'completed'
AND n.discoveryTime BETWEEN @beginTime AND @endTime
-
+
GROUP BY nb.blameIdCode
) stats
INNER JOIN sup_police p ON p.id_code = stats.blameIdCode
LEFT JOIN sup_depart d ON p.org_id = d.id
LEFT JOIN sup_depart d1 ON d.pid = d1.id AND d1.level >= 2
- WHERE
+ WHERE
p.del = 0
-
-
-
-
-
+
+
+
+
+
ORDER BY verify_size
DESC , p.id_code ASC
LIMIT 0, 10;
@@ -238,30 +238,31 @@ LIMIT 0, 10;
-- 个人问题库:优化后分页总数
EXPLAIN
SELECT count(*) FROM (
- SELECT DISTINCT
+ SELECT DISTINCT
p.id_code, p.name, p.emp_no, p.position, p.employment_date, p.person_type,
d.short_name depart_name, d.id depart_id, d1.short_name parent_depart_name
-
+
FROM sup_police p
LEFT JOIN sup_depart d ON p.org_id = d.id
LEFT JOIN sup_depart d1 ON d.pid = d1.id AND d1.level >= 2
- WHERE
+ WHERE
p.del = 0
-
-
-
-
-
+
+
+
+
+
AND EXISTS (
SELECT 1 FROM negative_blame nb
- INNER JOIN negative n ON nb.negativeId = n.id
- WHERE nb.blameIdCode = p.id_code
- AND
- nb.type = 'personal'
- AND n.checkStatus IN ('1', '2')
+ WHERE nb.blameIdCode = p.id_code AND nb.type = 'personal'
+ AND EXISTS (
+ SELECT 1 FROM negative n
+ WHERE n.id = nb.negativeId
+ AND
+ n.checkStatus IN ('1', '2')
AND n.processing_status = 'completed'
AND n.discoveryTime BETWEEN @beginTime AND @endTime
-
+
+ )
)
) counted;
-
diff --git a/sql/20261027_profile_query_indexes.sql b/sql/20261027_profile_query_indexes.sql
index 106b831..c19689e 100644
--- a/sql/20261027_profile_query_indexes.sql
+++ b/sql/20261027_profile_query_indexes.sql
@@ -1,3 +1,5 @@
+-- 暂缓执行:生产 result.txt 显示已有连接索引未被用作查找,需先核对字段类型与字符集。
+-- 先执行 20261027_profile_query_column_check.sql,再根据结果修订本候选脚本。
-- 单位/个人问题库查询索引,MySQL InnoDB。
-- 在目标业务库执行;先核对字段类型、索引长度和剩余磁盘空间。
-- 按完整左前缀识别已有等价索引;LOCK=NONE 不支持时会报错,不自动降级为锁表。
@@ -12,7 +14,7 @@ SET @profile_index_sql = IF(
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'negative' AND INDEX_TYPE = 'BTREE'
GROUP BY INDEX_NAME
) existing_indexes
- WHERE prefix_parts = 0 AND (columns_in_index = 'involveDepartId,crtTime' OR columns_in_index LIKE 'involveDepartId,crtTime,%')
+ WHERE prefix_parts = 0 AND (columns_in_index = 'involveDepartId,crtTime' OR LEFT(columns_in_index, LENGTH('involveDepartId,crtTime') + 1) = CONCAT('involveDepartId,crtTime', ','))
),
'SELECT ''idx_profile_depart_time: existing equivalent index, skipped'' AS result',
'ALTER TABLE `negative` ADD INDEX `idx_profile_depart_time` (`involveDepartId`, `crtTime`), ALGORITHM=INPLACE, LOCK=NONE'
@@ -30,7 +32,7 @@ SET @profile_index_sql = IF(
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'negative' AND INDEX_TYPE = 'BTREE'
GROUP BY INDEX_NAME
) existing_indexes
- WHERE prefix_parts = 0 AND (columns_in_index = 'processing_status,checkStatus,discoveryTime' OR columns_in_index LIKE 'processing_status,checkStatus,discoveryTime,%')
+ WHERE prefix_parts = 0 AND (columns_in_index = 'processing_status,checkStatus,discoveryTime' OR LEFT(columns_in_index, LENGTH('processing_status,checkStatus,discoveryTime') + 1) = CONCAT('processing_status,checkStatus,discoveryTime', ','))
),
'SELECT ''idx_profile_verified_time: existing equivalent index, skipped'' AS result',
'ALTER TABLE `negative` ADD INDEX `idx_profile_verified_time` (`processing_status`, `checkStatus`, `discoveryTime`), ALGORITHM=INPLACE, LOCK=NONE'
@@ -48,7 +50,7 @@ SET @profile_index_sql = IF(
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'negative_blame' AND INDEX_TYPE = 'BTREE'
GROUP BY INDEX_NAME
) existing_indexes
- WHERE prefix_parts = 0 AND (columns_in_index = 'negativeId,type,blameIdCode' OR columns_in_index LIKE 'negativeId,type,blameIdCode,%')
+ WHERE prefix_parts = 0 AND (columns_in_index = 'negativeId,type,blameIdCode' OR LEFT(columns_in_index, LENGTH('negativeId,type,blameIdCode') + 1) = CONCAT('negativeId,type,blameIdCode', ','))
),
'SELECT ''idx_profile_problem_person: existing equivalent index, skipped'' AS result',
'ALTER TABLE `negative_blame` ADD INDEX `idx_profile_problem_person` (`negativeId`, `type`, `blameIdCode`), ALGORITHM=INPLACE, LOCK=NONE'
@@ -66,7 +68,7 @@ SET @profile_index_sql = IF(
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'negative_blame' AND INDEX_TYPE = 'BTREE'
GROUP BY INDEX_NAME
) existing_indexes
- WHERE prefix_parts = 0 AND (columns_in_index = 'blameIdCode,type,negativeId' OR columns_in_index LIKE 'blameIdCode,type,negativeId,%')
+ WHERE prefix_parts = 0 AND (columns_in_index = 'blameIdCode,type,negativeId' OR LEFT(columns_in_index, LENGTH('blameIdCode,type,negativeId') + 1) = CONCAT('blameIdCode,type,negativeId', ','))
),
'SELECT ''idx_profile_person_problem: existing equivalent index, skipped'' AS result',
'ALTER TABLE `negative_blame` ADD INDEX `idx_profile_person_problem` (`blameIdCode`, `type`, `negativeId`), ALGORITHM=INPLACE, LOCK=NONE'
@@ -84,7 +86,7 @@ SET @profile_index_sql = IF(
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'sup_police' AND INDEX_TYPE = 'BTREE'
GROUP BY INDEX_NAME
) existing_indexes
- WHERE prefix_parts = 0 AND (columns_in_index = 'id_code,del' OR columns_in_index LIKE 'id_code,del,%')
+ WHERE prefix_parts = 0 AND (columns_in_index = 'id_code,del' OR LEFT(columns_in_index, LENGTH('id_code,del') + 1) = CONCAT('id_code,del', ','))
),
'SELECT ''idx_profile_police_identity: existing equivalent index, skipped'' AS result',
'ALTER TABLE `sup_police` ADD INDEX `idx_profile_police_identity` (`id_code`, `del`), ALGORITHM=INPLACE, LOCK=NONE'
diff --git a/sql/20261027_profile_query_performance.md b/sql/20261027_profile_query_performance.md
index 7adc03d..e1d9354 100644
--- a/sql/20261027_profile_query_performance.md
+++ b/sql/20261027_profile_query_performance.md
@@ -21,3 +21,38 @@
- 本地模拟使用 SQLite 内存数据库及 12 万条问题,属于结果一致性和优化方向验证,不是生产 MySQL 5.7 性能验收。
- 开发数据库连接失败;生产索引、执行计划和端到端耗时尚未取得,不能承诺已达到 3 秒。
- 索引脚本尚未对任何数据库执行;本次未改前端或运行前端构建。
+
+## 本地模拟记录
+
+数据:12 万条问题、约 43.6 万条责任记录、1.2 万个人、200 个三级单位,包含重复人员资料、重复责任记录、空身份证号和不符合状态/时间条件的记录。使用 SQLite 内存数据库及拟新增索引;新旧查询在相同索引下比较。
+
+两类列表各 10 组筛选/排序场景的全部返回行、总数和第二页条数一致。每次计时包含分页总数查询及第一页 10 条列表查询,取三次中位数:
+
+| 查询 | 原查询 | 优化后 |
+| --- | ---: | ---: |
+| 单位问题库 | 9435.6 ms | 2198.2 ms |
+| 个人问题库 | 3227.7 ms | 1009.9 ms |
+
+这些数字仅用于本地优化方向验证;不含生产网络、真实字段宽度/索引分布、磁盘和并发开销,不能据此认定生产接口已达到 3 秒。
+
+## 生产执行计划反馈(2026-10-27)
+
+result.txt 的表规模为 information_schema 估计:negative 112186 行、negative_blame 117768 行、sup_police 21144 行。
+
+- 单位原查询和改写后的聚合查询中,negative_blame 都未出现可用的连接索引(possible_keys 为空),扫描 idx_negativeId_blameIdCode 全索引并使用 Block Nested Loop。该索引明明以 negativeId 开头,需优先检查其与 negative.id 的数据类型、字符集和排序规则。
+- 单位人数表 sup_depart_police_size 有 depart_id 主键,却以 ALL + Block Nested Loop 关联;sup_depart.id 的主键 key_len 为 4,需检查两侧类型是否一致。
+- 单位分页总数的 DEPENDENT SUBQUERY 估计扫描 negative 112186 条索引记录;需检查 negative.involveDepartId 与 sup_depart.id 的类型转换。
+- 个人优化后列表中 sup_police 已使用 index_id_code 的 ref 查找,negative 使用主键 eq_ref;这是计划改善,但尚无实测耗时。其 ref=func 表明关联表达式经过计算,需通过 JSON 计划确认具体转换。
+- 上述推导出的万亿级派生表 rows 是优化器估计,不是实际生成或扫描的行数。
+
+下一步执行 20261027_profile_query_column_check.sql。现有候选索引脚本暂缓执行;在明确字段定义前不添加 CAST、CONVERT、FORCE INDEX,也不直接修改生产字段,避免统计语义变化或无效建索引。
+
+## 已确认的索引失效原因及修正(result1.txt)
+
+- negative.id 为 utf8mb4,negative_blame.negativeId 为 utf8。原执行计划在 nb.negativeId 上执行 CONVERT(... USING utf8mb4),导致责任表全索引扫描。单位列表改为在驱动侧 n.id 转成 utf8,并显式指定 utf8_general_ci,让 nb.negativeId 保持原始索引列。
+- sup_depart.id 为 int,negative.involveDepartId 和 sup_depart_police_size.depart_id 为 utf8 varchar。MySQL 返回 Warning 1739,无法对这两个字符串索引做 ref/range 查找。列表和总数查询改为将 d.id 转成 utf8 字符串,不对被查找的字符串列做函数转换。
+- 个人列表中 n 主键原本已为 eq_ref,不盲目反转该关联的转换方向。继续保留先聚合后关联的优化,通过生产复测决定是否需要覆盖索引。
+
+执行 20261027_profile_query_retest.sql:先单独执行数据检查,三项 affected_rows 全为 0 才部署单位查询;非零说明前导零/非标准数字或不能转换为 utf8 的问题 ID 可能影响新旧统计一致性,必须先核查。后续 SQL 提供新计划和 SQL_NO_CACHE 实测,SHOW PROFILES 中列表与总数的 Duration 之和才是该页的数据库耗时;接口还包含网络与应用开销。
+
+本次未执行生产 DDL;候选索引仍暂缓,优先复用已有索引验证转换方向修正的收益。生产 3 秒目标尚未验收。
diff --git a/sql/20261027_profile_query_retest.sql b/sql/20261027_profile_query_retest.sql
new file mode 100644
index 0000000..e4cdac8
--- /dev/null
+++ b/sql/20261027_profile_query_retest.sql
@@ -0,0 +1,256 @@
+-- MySQL 5.7.21:result1.txt 确认类型/字符集不一致后的复测,无 DDL、无业务数据修改。
+-- 步骤一:先单独执行以下检查。affected_rows 必须全部为 0,才部署新版单位查询。
+-- 非零时请返回计数,先处理编号语义差异,不要直接部署。
+SELECT 'negative.involveDepartId 非标准编号' AS check_item, COUNT(*) AS affected_rows
+FROM negative n JOIN sup_depart d ON n.involveDepartId = d.id
+WHERE d.level = 3
+ AND NOT (n.involveDepartId = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci)
+UNION ALL
+SELECT 'sup_depart_police_size.depart_id 非标准编号', COUNT(*)
+FROM sup_depart_police_size p JOIN sup_depart d ON p.depart_id = d.id
+WHERE d.level = 3
+ AND NOT (p.depart_id = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci)
+UNION ALL
+SELECT 'negative.id 无法无损转成 utf8', COUNT(*)
+FROM negative
+WHERE BINARY id <> BINARY CONVERT(CONVERT(id USING utf8) USING utf8mb4);
+
+-- 步骤二:以上计数全为 0 后,执行下列 EXPLAIN 和实际查询。
+-- 预期:nb 不再全索引扫描+Block Nested Loop,p 使用主键,单位总数中的 n 使用单位索引。
+-- 查询时间仍需实测;请返回四份 EXPLAIN 和 SHOW PROFILES 的耗时,无需返回人员明细。
+SET @beginTime = '1949-01-01 00:00:00';
+SET @endTime = NOW();
+SET profiling = 1;
+
+-- 单位问题库:优化后列表查询(第一页 10 条)
+EXPLAIN
+SELECT DISTINCT
+ d.id depart_id,
+ d1.short_name parent_depart_name,
+ d.short_name depart_name,
+ p.police_size,
+ p.aux_size,
+ stats.verify_police_size,
+ stats.verify_size
+ FROM (
+ SELECT d.id depart_id,
+ count(DISTINCT nb.blameIdCode) verify_police_size,
+ count(DISTINCT n.id) verify_size
+ FROM sup_depart d
+ INNER JOIN negative n ON n.involveDepartId = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
+ LEFT JOIN negative_blame nb ON nb.negativeId = CONVERT(n.id USING utf8) COLLATE utf8_general_ci
+ WHERE
+ d.level = 3
+
+
+
+
+ AND n.crtTime BETWEEN @beginTime AND @endTime
+ GROUP BY d.id
+ ) stats
+ INNER JOIN sup_depart d ON d.id = stats.depart_id
+ LEFT JOIN sup_depart d1 ON d.pid = d1.id
+ LEFT JOIN sup_depart_police_size p ON p.depart_id = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
+ ORDER BY
+ verify_size
+ DESC , d.id ASC
+LIMIT 0, 10;
+SHOW WARNINGS;
+
+SELECT SQL_NO_CACHE DISTINCT
+ d.id depart_id,
+ d1.short_name parent_depart_name,
+ d.short_name depart_name,
+ p.police_size,
+ p.aux_size,
+ stats.verify_police_size,
+ stats.verify_size
+ FROM (
+ SELECT d.id depart_id,
+ count(DISTINCT nb.blameIdCode) verify_police_size,
+ count(DISTINCT n.id) verify_size
+ FROM sup_depart d
+ INNER JOIN negative n ON n.involveDepartId = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
+ LEFT JOIN negative_blame nb ON nb.negativeId = CONVERT(n.id USING utf8) COLLATE utf8_general_ci
+ WHERE
+ d.level = 3
+
+
+
+
+ AND n.crtTime BETWEEN @beginTime AND @endTime
+ GROUP BY d.id
+ ) stats
+ INNER JOIN sup_depart d ON d.id = stats.depart_id
+ LEFT JOIN sup_depart d1 ON d.pid = d1.id
+ LEFT JOIN sup_depart_police_size p ON p.depart_id = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
+ ORDER BY
+ verify_size
+ DESC , d.id ASC
+LIMIT 0, 10;
+
+-- 单位问题库:优化后分页总数
+EXPLAIN
+SELECT count(*) FROM (
+ SELECT DISTINCT d.id, p.police_size, p.aux_size
+ FROM sup_depart d
+ LEFT JOIN sup_depart_police_size p ON p.depart_id = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
+ WHERE
+ d.level = 3
+
+
+
+
+ AND EXISTS (
+ SELECT 1 FROM negative n
+ WHERE n.involveDepartId = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
+ AND n.crtTime BETWEEN @beginTime AND @endTime
+ )
+ ) counted;
+SHOW WARNINGS;
+
+SELECT SQL_NO_CACHE count(*) FROM (
+ SELECT DISTINCT d.id, p.police_size, p.aux_size
+ FROM sup_depart d
+ LEFT JOIN sup_depart_police_size p ON p.depart_id = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
+ WHERE
+ d.level = 3
+
+
+
+
+ AND EXISTS (
+ SELECT 1 FROM negative n
+ WHERE n.involveDepartId = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
+ AND n.crtTime BETWEEN @beginTime AND @endTime
+ )
+ ) counted;
+
+-- 个人问题库:优化后列表查询(第一页 10 条)
+EXPLAIN
+SELECT DISTINCT
+ p.id_code, p.name, p.emp_no, p.position, p.employment_date, p.person_type,
+ d.short_name depart_name, d.id depart_id, d1.short_name parent_depart_name
+ , stats.verify_size
+ FROM (
+ SELECT nb.blameIdCode, count(DISTINCT n.id) verify_size
+ FROM negative n
+ INNER JOIN negative_blame nb ON nb.negativeId = n.id
+ WHERE
+ nb.type = 'personal'
+ AND n.checkStatus IN ('1', '2')
+ AND n.processing_status = 'completed'
+ AND n.discoveryTime BETWEEN @beginTime AND @endTime
+
+ GROUP BY nb.blameIdCode
+ ) stats
+ INNER JOIN sup_police p ON p.id_code = stats.blameIdCode
+ LEFT JOIN sup_depart d ON p.org_id = d.id
+ LEFT JOIN sup_depart d1 ON d.pid = d1.id AND d1.level >= 2
+ WHERE
+ p.del = 0
+
+
+
+
+
+ ORDER BY verify_size
+ DESC , p.id_code ASC
+LIMIT 0, 10;
+SHOW WARNINGS;
+
+SELECT SQL_NO_CACHE DISTINCT
+ p.id_code, p.name, p.emp_no, p.position, p.employment_date, p.person_type,
+ d.short_name depart_name, d.id depart_id, d1.short_name parent_depart_name
+ , stats.verify_size
+ FROM (
+ SELECT nb.blameIdCode, count(DISTINCT n.id) verify_size
+ FROM negative n
+ INNER JOIN negative_blame nb ON nb.negativeId = n.id
+ WHERE
+ nb.type = 'personal'
+ AND n.checkStatus IN ('1', '2')
+ AND n.processing_status = 'completed'
+ AND n.discoveryTime BETWEEN @beginTime AND @endTime
+
+ GROUP BY nb.blameIdCode
+ ) stats
+ INNER JOIN sup_police p ON p.id_code = stats.blameIdCode
+ LEFT JOIN sup_depart d ON p.org_id = d.id
+ LEFT JOIN sup_depart d1 ON d.pid = d1.id AND d1.level >= 2
+ WHERE
+ p.del = 0
+
+
+
+
+
+ ORDER BY verify_size
+ DESC , p.id_code ASC
+LIMIT 0, 10;
+
+-- 个人问题库:优化后分页总数
+EXPLAIN
+SELECT count(*) FROM (
+ SELECT DISTINCT
+ p.id_code, p.name, p.emp_no, p.position, p.employment_date, p.person_type,
+ d.short_name depart_name, d.id depart_id, d1.short_name parent_depart_name
+
+ FROM sup_police p
+ LEFT JOIN sup_depart d ON p.org_id = d.id
+ LEFT JOIN sup_depart d1 ON d.pid = d1.id AND d1.level >= 2
+ WHERE
+ p.del = 0
+
+
+
+
+
+ AND EXISTS (
+ SELECT 1 FROM negative_blame nb
+ WHERE nb.blameIdCode = p.id_code AND nb.type = 'personal'
+ AND EXISTS (
+ SELECT 1 FROM negative n
+ WHERE n.id = nb.negativeId
+ AND
+ n.checkStatus IN ('1', '2')
+ AND n.processing_status = 'completed'
+ AND n.discoveryTime BETWEEN @beginTime AND @endTime
+
+ )
+ )
+ ) counted;
+SHOW WARNINGS;
+
+SELECT SQL_NO_CACHE count(*) FROM (
+ SELECT DISTINCT
+ p.id_code, p.name, p.emp_no, p.position, p.employment_date, p.person_type,
+ d.short_name depart_name, d.id depart_id, d1.short_name parent_depart_name
+
+ FROM sup_police p
+ LEFT JOIN sup_depart d ON p.org_id = d.id
+ LEFT JOIN sup_depart d1 ON d.pid = d1.id AND d1.level >= 2
+ WHERE
+ p.del = 0
+
+
+
+
+
+ AND EXISTS (
+ SELECT 1 FROM negative_blame nb
+ WHERE nb.blameIdCode = p.id_code AND nb.type = 'personal'
+ AND EXISTS (
+ SELECT 1 FROM negative n
+ WHERE n.id = nb.negativeId
+ AND
+ n.checkStatus IN ('1', '2')
+ AND n.processing_status = 'completed'
+ AND n.discoveryTime BETWEEN @beginTime AND @endTime
+
+ )
+ )
+ ) counted;
+
+SHOW PROFILES;
+SET profiling = 0;
diff --git a/src/main/java/com/biutag/supervision/controller/sensitiveperception/ProfileDepartController.java b/src/main/java/com/biutag/supervision/controller/sensitiveperception/ProfileDepartController.java
index ca1d0c7..beac13b 100644
--- a/src/main/java/com/biutag/supervision/controller/sensitiveperception/ProfileDepartController.java
+++ b/src/main/java/com/biutag/supervision/controller/sensitiveperception/ProfileDepartController.java
@@ -87,12 +87,23 @@ public class ProfileDepartController {
.setName(depart.getShortName())
.setShortName(depart.getShortName())
.setDepartImg(depart.getImg());
+ if (Integer.valueOf(3).equals(depart.getLevel()) && StrUtil.isNotBlank(depart.getPid())) {
+ SupDepart parent = departService.getById(depart.getPid());
+ if (parent != null) {
+ profileDepart.getDepartInfo().setParentName(StrUtil.blankToDefault(parent.getShortName(), parent.getName()));
+ }
+ }
}
List polices = policeService.listAllByDepartId(departId);
profileDepart.getDepartInfo().setMainRole(polices.stream().filter(item -> "正职".equals(item.getPosition())).findFirst().map(SupPolice::getName).orElse(null));
profileDepart.getDepartInfo().setDeputyRole(polices.stream().filter(item -> "副职".equals(item.getPosition())).map(SupPolice::getName).toList());
List negatives = profileDepartMapper.selectDepartProblems(departId, beginTime, endTime);
+ profileDepart.setComplaintCount(negatives.stream()
+ .map(Negative::getProblemSourcesCode)
+ .filter(Objects::nonNull)
+ .filter(ProblemSourcesEnum.GROUP_21_22_23_24_25::contains)
+ .count());
List negativeIds = negatives.stream().map(Negative::getId).toList();
SupDepartPoliceSize policeSize = departPoliceSizeService.getById(departId);
if(Objects.nonNull(policeSize)){
diff --git a/src/main/java/com/biutag/supervision/pojo/domain/ProfileDepart.java b/src/main/java/com/biutag/supervision/pojo/domain/ProfileDepart.java
index 9024284..2839291 100644
--- a/src/main/java/com/biutag/supervision/pojo/domain/ProfileDepart.java
+++ b/src/main/java/com/biutag/supervision/pojo/domain/ProfileDepart.java
@@ -21,6 +21,8 @@ import java.util.List;
public class ProfileDepart {
private Long days;
+ // 信访投诉下级来源的问题数
+ private long complaintCount;
private BigDecimal score = new BigDecimal(0);
private String expression;
@@ -60,6 +62,7 @@ public class ProfileDepart {
@Accessors(chain = true)
public static class DepartInfo {
private String name;
+ private String parentName;
private String shortName;
private String departImg;
// 所长
diff --git a/src/main/java/com/biutag/supervision/pojo/vo/NegativeProfile.java b/src/main/java/com/biutag/supervision/pojo/vo/NegativeProfile.java
index a3d42dd..f1bb77f 100644
--- a/src/main/java/com/biutag/supervision/pojo/vo/NegativeProfile.java
+++ b/src/main/java/com/biutag/supervision/pojo/vo/NegativeProfile.java
@@ -19,6 +19,9 @@ public class NegativeProfile {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
private LocalDateTime discoveryTime;
+ @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
+ private LocalDateTime crtTime;
+
private String problemSourcesCode;
// 问题来源
diff --git a/src/main/resources/mapper/ProfileDepartMapper.xml b/src/main/resources/mapper/ProfileDepartMapper.xml
index 06d1e47..778a639 100644
--- a/src/main/resources/mapper/ProfileDepartMapper.xml
+++ b/src/main/resources/mapper/ProfileDepartMapper.xml
@@ -22,16 +22,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
SELECT count(*) FROM (
SELECT DISTINCT d.id, p.police_size, p.aux_size
FROM sup_depart d
- LEFT JOIN sup_depart_police_size p ON p.depart_id = d.id
+ LEFT JOIN sup_depart_police_size p ON p.depart_id = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
WHERE
AND EXISTS (
SELECT 1 FROM negative n
- WHERE n.involveDepartId = d.id
+ WHERE n.involveDepartId = CAST(d.id AS CHAR CHARACTER SET utf8) COLLATE utf8_general_ci
AND n.crtTime BETWEEN #{beginTime} AND #{endTime}
)
) counted
+
@@ -83,7 +85,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
SELECT nb.blameIdCode, count(DISTINCT n.id) verify_size
FROM negative n
INNER JOIN negative_blame nb ON nb.negativeId = n.id
- WHERE
+ WHERE nb.type = 'personal' AND
GROUP BY nb.blameIdCode
) stats
INNER JOIN sup_police p ON p.id_code = stats.blameIdCode
diff --git a/src/main/resources/mapper/StatisticsGroupMapper.xml b/src/main/resources/mapper/StatisticsGroupMapper.xml
index 7cbf2c9..757706b 100644
--- a/src/main/resources/mapper/StatisticsGroupMapper.xml
+++ b/src/main/resources/mapper/StatisticsGroupMapper.xml
@@ -76,14 +76,22 @@
`value` DESC
+
+ CASE WHEN m.groupId IN ('20', '21', '22')
+ AND NULLIF(TRIM(parent_depart.short_name), '') IS NOT NULL
+ THEN CONCAT(parent_depart.short_name, '/', m.membersName)
+ ELSE m.membersName END label
+
+