返回

SQL 数据多级分类排序:拥有者、类别及子类别高效排序

mysql

SQL 数据结果按拥有者、类别和子类别排序

在使用 SQL 查询数据库时,经常需要根据特定条件对数据进行排序。当数据涉及拥有者、类别和子类别等多级分类时,如何实现高效且准确的排序成为一个关键问题。针对根据拥有者、类别和子类别进行排序,下面将探讨几种有效的解决思路及对应操作。

一、 问题解析

核心需求是按层级对数据排序。 顺序为拥有者(owner_id)、类别(category_id)、子类别(subcategory_id)。原始 SQL 查询使用 CASE 语句,意图实现对不同类别和子类别的排序。 问题在于这种方法需要预先定义每个类别和子类别的对应关系, 增加了维护难度且降低灵活性。

二、 优化方案

解决该问题有两种推荐的方案。

1. 创建排序辅助列

通过增加一个排序辅助列。 为不同分类逻辑赋予数字,以建立清晰的优先级关系,然后通过多列排序解决。

  • 原理 :将分类信息映射为连续的整数值。通过辅助列,可以简化ORDER BY子句, 使其更加清晰易懂, 从而摆脱繁琐的 CASE 语句。

  • 操作步骤

    1. 修改表结构, 添加新的 sort_order 字段。 可将这个操作通过更新语句临时放在虚拟表格, 不一定要更改数据表格本身。
    2. 通过类似以下规则的方式更新此字段。 通过一个辅助字段来规范化原本通过多个case 表达的内容。
      • category_id = 1 AND subcategory_id = 1 设置为 1
      • category_id = 1 AND subcategory_id = 2 设置为 2
      • category_id = 1 AND subcategory_id = 3 设置为 3
      • category_id = 1 AND subcategory_id = 4 设置为 4
      • category_id = 2 AND subcategory_id = 7 设置为 5
      • category_id = 2 AND subcategory_id = 8 设置为 6
      • category_id = 2 AND subcategory_id = 9 设置为 7
      • category_id = 1 AND subcategory_id = 17 设置为 8
      • category_id = 1 AND subcategory_id = 18 设置为 9
      • category_id = 1 AND subcategory_id = 19 设置为 10
      • 其他情况 设置为 11
    3. 使用ORDER BY进行多列排序。
  • 代码示例

SELECT t.*
FROM (
  SELECT 
    *,
    CASE 
      WHEN (category_id = 1 AND subcategory_id = 1) THEN 1  
      WHEN (category_id = 1 AND subcategory_id = 2) THEN 2  
      WHEN (category_id = 1 AND subcategory_id = 3) THEN 3  
      WHEN (category_id = 1 AND subcategory_id = 4) THEN 4  
      WHEN (category_id = 2 AND subcategory_id = 7) THEN 5  
      WHEN (category_id = 2 AND subcategory_id = 8) THEN 6  
      WHEN (category_id = 2 AND subcategory_id = 9) THEN 7  
      WHEN (category_id = 1 AND subcategory_id = 17) THEN 8 
      WHEN (category_id = 1 AND subcategory_id = 18) THEN 9 
      WHEN (category_id = 1 AND subcategory_id = 19) THEN 10 
      ELSE 11
    END as sort_order
  FROM transactions
  WHERE is_submitted = 0
) as t
ORDER BY 
  owner_id,
  sort_order,
  receipt_date;
  • 安全建议 : 创建 sort_order 列需要谨慎设置,需要保持数据一致。 当涉及到新增内容时要更新相应的序号逻辑,以免出现排序问题。 如果没有更改表的权限可以尝试利用临时表或子查询创建临时 sort_order 列。

2. 建立映射表

可以考虑引入一个专门的映射表来存储类别、子类别和排序值之间的对应关系。 方案一的 case 内容可以被这个表的逻辑所替代。

  • 原理 :使用映射表可以将排序逻辑与业务数据解耦。 查询时通过表连接 JOIN 获取对应的排序值,从而避免了在每个查询中都编写复杂的CASE 语句, 也免除了为每个表添加 sort_order 辅助列的需求。

  • 操作步骤

    1. 创建一个新的映射表 category_sort_order, 包含以下字段:category_id, subcategory_id, sort_order

    2. 在该表中添加如下数据。

      category_id subcategory_id sort_order
      1 1 1
      1 2 2
      1 3 3
      1 4 4
      2 7 5
      2 8 6
      2 9 7
      1 17 8
      1 18 9
      1 19 10
    3. transactions 表与此表关联进行查询。

  • 代码示例

SELECT t.*
FROM transactions t
LEFT JOIN category_sort_order cso ON t.category_id = cso.category_id AND t.subcategory_id = cso.subcategory_id
WHERE t.is_submitted = 0
ORDER BY 
  t.owner_id, 
  cso.sort_order, 
  t.receipt_date;
  • 安全建议 : 对category_sort_order表做好读写权限控制, 只允许少数必要的人对这个表的内容进行更改。 可以通过外键约束确保 transactions 表与 category_sort_order 表中的数据一致性。 在数据量比较大的情况下, 提前设计好表的索引能优化查询速度。