|
@@ -7,24 +7,30 @@
|
|
|
-- 【怎么跑】五条 SELECT 相互独立,可以整段跑,也可以一条一条跑。
|
|
-- 【怎么跑】五条 SELECT 相互独立,可以整段跑,也可以一条一条跑。
|
|
|
-- 没有 psql 元命令(\echo 之类),图形客户端 / psycopg2 都能直接执行。
|
|
-- 没有 psql 元命令(\echo 之类),图形客户端 / psycopg2 都能直接执行。
|
|
|
--
|
|
--
|
|
|
--- 背景:pay_lsy_merchant 上三条唯一约束都由数据库把关:
|
|
|
|
|
--- uk_lsy_merchant_tenant_level UNIQUE(tenant_id) WHERE enterprise_id IS NULL ← 偏索引
|
|
|
|
|
|
|
+-- 背景:pay_lsy_merchant 上两条唯一约束由数据库把关:
|
|
|
-- uk_lsy_merchant_tenant_ent UNIQUE(tenant_id, enterprise_id)
|
|
-- uk_lsy_merchant_tenant_ent UNIQUE(tenant_id, enterprise_id)
|
|
|
-- uk_lsy_merchant_mch_no UNIQUE(mch_no) ← 全局,不带 tenant_id
|
|
-- uk_lsy_merchant_mch_no UNIQUE(mch_no) ← 全局,不带 tenant_id
|
|
|
--- (代码侧的预检只负责把冲突说清楚,不负责挡住 —— 挡住的是这三条。见
|
|
|
|
|
|
|
+-- (代码侧的预检只负责把冲突说清楚,不负责挡住 —— 挡住的是这两条。见
|
|
|
-- LsyMerchantService#rejectUniqueConflicts 的 javadoc。)
|
|
-- LsyMerchantService#rejectUniqueConflicts 的 javadoc。)
|
|
|
--
|
|
--
|
|
|
|
|
+-- ★ 2026-09-30 补记:原先还有第三条 uk_lsy_merchant_tenant_level
|
|
|
|
|
+-- (UNIQUE(tenant_id) WHERE enterprise_id IS NULL),**已经被删掉**
|
|
|
|
|
+-- (见 db/lsy-merchant-multi-per-tenant.sql)—— 去企业化之后一个租户允许有多个商户号。
|
|
|
|
|
+-- ⇒ 本脚本 ①②④ 里那个「槽位」的说法现在是**历史名词**,不是约束:
|
|
|
|
|
+-- 同一个租户出现多条 enterprise_id IS NULL 的行**是合法的**,② 读到多行不是异常。
|
|
|
|
|
+-- ③⑤ 那两条与它无关,仍然有效。
|
|
|
|
|
+--
|
|
|
-- 想查别的租户:把下面所有的 2 换成目标 tenant_id 即可。
|
|
-- 想查别的租户:把下面所有的 2 换成目标 tenant_id 即可。
|
|
|
|
|
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- ============================================================================
|
|
|
--- ① 租户 2 的全部商户号登记行(租户级排在前面)
|
|
|
|
|
--- 看这一条就够判断「谁占着槽位、我删掉的是哪一行」
|
|
|
|
|
|
|
+-- ① 租户 2 的全部商户号登记行(enterprise_id 为空的行排在前面)
|
|
|
|
|
+-- 看这一条就够判断「我删掉的是哪一行」
|
|
|
-- ============================================================================
|
|
-- ============================================================================
|
|
|
SELECT id,
|
|
SELECT id,
|
|
|
tenant_id,
|
|
tenant_id,
|
|
|
enterprise_id,
|
|
enterprise_id,
|
|
|
- CASE WHEN enterprise_id IS NULL THEN '★ 租户级(占着 uk_lsy_merchant_tenant_level)'
|
|
|
|
|
|
|
+ CASE WHEN enterprise_id IS NULL THEN '无企业 ID'
|
|
|
ELSE '企业级'
|
|
ELSE '企业级'
|
|
|
END AS "槽位",
|
|
END AS "槽位",
|
|
|
mch_no,
|
|
mch_no,
|
|
@@ -40,8 +46,9 @@ SELECT id,
|
|
|
|
|
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- ============================================================================
|
|
|
--- ② 谁占着租户 2 的租户级槽位(应当恰好 0 或 1 行)
|
|
|
|
|
--- 新增「企业 ID 留空」的行时,撞的就是这一行
|
|
|
|
|
|
|
+-- ② 租户 2 里 enterprise_id 为空的行
|
|
|
|
|
+-- ★ 2026-09-30 之前这里「应当恰好 0 或 1 行」(偏索引拦着);索引已删,
|
|
|
|
|
+-- **多行是合法的** —— 一个租户本来就允许多个商户号。
|
|
|
-- ============================================================================
|
|
-- ============================================================================
|
|
|
SELECT id, tenant_id, mch_no, merchant_name, review_status, status, created_time
|
|
SELECT id, tenant_id, mch_no, merchant_name, review_status, status, created_time
|
|
|
FROM pay_lsy_merchant
|
|
FROM pay_lsy_merchant
|
|
@@ -64,9 +71,9 @@ HAVING count(*) > 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- ============================================================================
|
|
|
--- ④ 每租户槽位占用总览
|
|
|
|
|
--- 「租户级行数」只要 > 1 就是线上数据已经违反约束(不可能);= 1 表示该租户
|
|
|
|
|
--- 已经不能再新增「企业 ID 留空」的行
|
|
|
|
|
|
|
+-- ④ 每租户登记行总览
|
|
|
|
|
+-- ★ 索引删掉之前,「租户级行数」> 1 是「线上数据已经违反约束(不可能)」;
|
|
|
|
|
+-- 现在它就是**正常的多商户号**,不构成任何异常。
|
|
|
-- ============================================================================
|
|
-- ============================================================================
|
|
|
SELECT tenant_id,
|
|
SELECT tenant_id,
|
|
|
count(*) FILTER (WHERE enterprise_id IS NULL) AS "租户级行数",
|
|
count(*) FILTER (WHERE enterprise_id IS NULL) AS "租户级行数",
|
|
@@ -78,8 +85,11 @@ SELECT tenant_id,
|
|
|
|
|
|
|
|
|
|
|
|
|
-- ============================================================================
|
|
-- ============================================================================
|
|
|
--- ⑤ 线上三条唯一索引的真实定义(核对与代码的假设是否一致)
|
|
|
|
|
--- 期望恰好 3 行,且第 1 条带 WHERE ... IS NULL
|
|
|
|
|
|
|
+-- ⑤ 线上唯一索引的真实定义(核对与代码的假设是否一致)
|
|
|
|
|
+-- 期望恰好 3 行,逐条对:pay_lsy_merchant_pkey / uk_lsy_merchant_mch_no /
|
|
|
|
|
+-- uk_lsy_merchant_tenant_ent;**不应**再出现 uk_lsy_merchant_tenant_level。
|
|
|
|
|
+-- (2026-09-30 实测读数就是这 3 行 —— 这条 SELECT 的 ILIKE 过滤是有效的,
|
|
|
|
|
+-- 证据是它确实把两个 uk_* 捞出来了,不是「过滤条件把所有行都滤掉了」。)
|
|
|
-- ============================================================================
|
|
-- ============================================================================
|
|
|
SELECT indexname, indexdef
|
|
SELECT indexname, indexdef
|
|
|
FROM pg_indexes
|
|
FROM pg_indexes
|