|
|
@@ -0,0 +1,88 @@
|
|
|
+-- 乐收赢商户号槽位诊断(**只读**)2026-09-30
|
|
|
+-- 用途:线上报过
|
|
|
+-- duplicate key value violates unique constraint "uk_lsy_merchant_tenant_level"
|
|
|
+-- 详细:Key (tenant_id)=(2) already exists.
|
|
|
+-- 本脚本把「谁占着槽位」摊开给人看 —— 全部是 SELECT,不改任何数据。
|
|
|
+--
|
|
|
+-- 【怎么跑】五条 SELECT 相互独立,可以整段跑,也可以一条一条跑。
|
|
|
+-- 没有 psql 元命令(\echo 之类),图形客户端 / psycopg2 都能直接执行。
|
|
|
+--
|
|
|
+-- 背景:pay_lsy_merchant 上三条唯一约束都由数据库把关:
|
|
|
+-- uk_lsy_merchant_tenant_level UNIQUE(tenant_id) WHERE enterprise_id IS NULL ← 偏索引
|
|
|
+-- uk_lsy_merchant_tenant_ent UNIQUE(tenant_id, enterprise_id)
|
|
|
+-- uk_lsy_merchant_mch_no UNIQUE(mch_no) ← 全局,不带 tenant_id
|
|
|
+-- (代码侧的预检只负责把冲突说清楚,不负责挡住 —— 挡住的是这三条。见
|
|
|
+-- LsyMerchantService#rejectUniqueConflicts 的 javadoc。)
|
|
|
+--
|
|
|
+-- 想查别的租户:把下面所有的 2 换成目标 tenant_id 即可。
|
|
|
+
|
|
|
+
|
|
|
+-- ============================================================================
|
|
|
+-- ① 租户 2 的全部商户号登记行(租户级排在前面)
|
|
|
+-- 看这一条就够判断「谁占着槽位、我删掉的是哪一行」
|
|
|
+-- ============================================================================
|
|
|
+SELECT id,
|
|
|
+ tenant_id,
|
|
|
+ enterprise_id,
|
|
|
+ CASE WHEN enterprise_id IS NULL THEN '★ 租户级(占着 uk_lsy_merchant_tenant_level)'
|
|
|
+ ELSE '企业级'
|
|
|
+ END AS "槽位",
|
|
|
+ mch_no,
|
|
|
+ shop_no,
|
|
|
+ merchant_name,
|
|
|
+ review_status,
|
|
|
+ status,
|
|
|
+ created_time,
|
|
|
+ updated_time
|
|
|
+ FROM pay_lsy_merchant
|
|
|
+ WHERE tenant_id = 2
|
|
|
+ ORDER BY (enterprise_id IS NOT NULL), id;
|
|
|
+
|
|
|
+
|
|
|
+-- ============================================================================
|
|
|
+-- ② 谁占着租户 2 的租户级槽位(应当恰好 0 或 1 行)
|
|
|
+-- 新增「企业 ID 留空」的行时,撞的就是这一行
|
|
|
+-- ============================================================================
|
|
|
+SELECT id, tenant_id, mch_no, merchant_name, review_status, status, created_time
|
|
|
+ FROM pay_lsy_merchant
|
|
|
+ WHERE tenant_id = 2
|
|
|
+ AND enterprise_id IS NULL;
|
|
|
+
|
|
|
+
|
|
|
+-- ============================================================================
|
|
|
+-- ③ 全局重号的商户号(同一 mch_no 出现在多行)
|
|
|
+-- 只要这里出现一行,新增那个号必撞 uk_lsy_merchant_mch_no
|
|
|
+-- 期望:0 行
|
|
|
+-- ============================================================================
|
|
|
+SELECT mch_no,
|
|
|
+ count(*) AS "行数",
|
|
|
+ array_agg(tenant_id ORDER BY tenant_id) AS "租户们",
|
|
|
+ array_agg(id ORDER BY id) AS "ids"
|
|
|
+ FROM pay_lsy_merchant
|
|
|
+ GROUP BY mch_no
|
|
|
+HAVING count(*) > 1;
|
|
|
+
|
|
|
+
|
|
|
+-- ============================================================================
|
|
|
+-- ④ 每租户槽位占用总览
|
|
|
+-- 「租户级行数」只要 > 1 就是线上数据已经违反约束(不可能);= 1 表示该租户
|
|
|
+-- 已经不能再新增「企业 ID 留空」的行
|
|
|
+-- ============================================================================
|
|
|
+SELECT tenant_id,
|
|
|
+ count(*) FILTER (WHERE enterprise_id IS NULL) AS "租户级行数",
|
|
|
+ count(*) FILTER (WHERE enterprise_id IS NOT NULL) AS "企业级行数",
|
|
|
+ count(*) AS "合计"
|
|
|
+ FROM pay_lsy_merchant
|
|
|
+ GROUP BY tenant_id
|
|
|
+ ORDER BY tenant_id;
|
|
|
+
|
|
|
+
|
|
|
+-- ============================================================================
|
|
|
+-- ⑤ 线上三条唯一索引的真实定义(核对与代码的假设是否一致)
|
|
|
+-- 期望恰好 3 行,且第 1 条带 WHERE ... IS NULL
|
|
|
+-- ============================================================================
|
|
|
+SELECT indexname, indexdef
|
|
|
+ FROM pg_indexes
|
|
|
+ WHERE tablename = 'pay_lsy_merchant'
|
|
|
+ AND indexdef ILIKE '%unique%'
|
|
|
+ ORDER BY indexname;
|