# 06 索引策略与典型查询模式

本节从“页面/接口会怎么查”出发解释索引的设计意图，并给出可复用的查询模式。

---

## 1. 索引总体思路

从 `complete_mall_database.sql` / `mall_migration.sql` 中可以看到索引集中在：

- 列表页高频过滤字段：`status`、`created_at`、`merchant_id`、`user_id`、`category_id`
- 对外访问字段：`cid`、`slug`
- 排序/榜单字段：`sale_count`、`rating_avg`、`rating_count`、`base_price`
- 多值字段：`tags`（GIN）

其核心理念是：

- **读路径优先**：电商最常见的是“列表页 + 详情页”，索引优先覆盖这些路径。
- **SEO 友好**：对外 URL 常用 `cid/slug`，因此为其建索引。
- **避免重计算**：用触发器维护汇总字段（库存/销量），让查询尽量落在单表或轻量 join。

---

## 2. 典型查询模式与对应索引

> 注：以下 SQL 示例以可读性为主，实际项目可能通过视图（如 `ml_products_detail_view`）或 API 层封装。

### 2.1 商品列表页（按分类 + 上架状态 + 时间倒序）

典型查询：

```sql
select id, cid, name, base_price, main_image_url, sale_count, rating_avg
from public.ml_products
where category_id = '...category_uuid...'::uuid
  and status = 1
order by created_at desc
limit 20 offset 0;
```

依赖索引：

- `idx_ml_products_category(category_id, status)`
- `idx_ml_products_status(status, created_at desc)`

### 2.2 商品列表页（商家后台：按商家 + 状态）

```sql
select id, cid, name, status, total_stock, sale_count
from public.ml_products
where merchant_id = '...merchant_uuid...'::uuid
order by updated_at desc
limit 50;
```

依赖索引：

- `idx_ml_products_merchant(merchant_id, status)`（也会被 merchant_id 过滤利用）

### 2.3 商品详情页（按 cid 或 slug）

```sql
-- 方式 1：cid
select * from public.get_product_by_cid(12345);

-- 方式 2：slug
select *
from public.ml_products
where slug = 'iphone-15-pro' and status = 1;
```

依赖索引：

- `idx_ml_products_cid(cid)`
- `idx_ml_products_slug(slug)`

### 2.4 商品搜索/筛选（按 tags）

```sql
select id, cid, name
from public.ml_products
where status = 1
  and tags @> array['手机','苹果']::text[]
order by sale_count desc
limit 20;
```

依赖索引：

- `idx_ml_products_tags using gin(tags)`

说明：

- `tags @> array[...]` 是典型的 GIN 可加速模式。

### 2.5 订单列表（用户维度）

```sql
select id, order_no, total_amount, order_status, created_at
from public.ml_orders
where user_id = '...user_uuid...'::uuid
order by created_at desc
limit 20;
```

依赖索引：

- `idx_ml_orders_user(user_id, created_at desc)`

### 2.6 订单列表（商家维度）

```sql
select id, order_no, total_amount, order_status, created_at
from public.ml_orders
where merchant_id = '...merchant_uuid...'::uuid
order by created_at desc
limit 20;
```

依赖索引：

- `idx_ml_orders_merchant(merchant_id, created_at desc)`

### 2.7 订单按状态过滤（运营/商家后台常见）

```sql
select id, order_no
from public.ml_orders
where order_status in (1,2,3)
order by created_at desc
limit 50;
```

依赖索引：

- `idx_ml_orders_status(order_status, created_at desc)`

### 2.8 购物车加载

```sql
select c.*, s.price, p.name
from public.ml_shopping_cart c
left join public.ml_product_skus s on s.id = c.sku_id
left join public.ml_products p on p.id = c.product_id
where c.user_id = '...user_uuid...'::uuid
order by c.updated_at desc;
```

依赖索引：

- `idx_ml_shopping_cart_user(user_id)`

---

## 3. JSONB 字段的索引缺口（建议项）

当前脚本对 `tags` 做了 GIN，但对以下 JSONB 的查询与索引没有“强约束”体现：

- `ml_orders.shipping_address`
- `ml_shops.address/business_hours`
- `ml_coupon_templates.applicable_products/categories`

如果业务上出现以下高频查询：

- “按城市/区域筛选订单/店铺”
- “某个商品可用哪些券”

建议考虑：

- 关系化建模（反向关联表）
- 或表达式索引（例如对 JSONB 内部字段建索引）

---

## 4. 索引维护建议

- 新增字段/查询前先用 `EXPLAIN (ANALYZE, BUFFERS)` 验证是否命中索引。
- 避免为低选择性字段（如 `status` 单列）盲目建索引，优先组合索引匹配真实查询。
- 注意 RLS 会影响执行计划与开销，常用过滤字段建议都具备索引（`user_id/merchant_id/status/created_at`）。
