明细条件过滤单头优选实践

131 阅读4分钟

今天碰到个案例和大家分享下,需求为:用某些可选的明细表条件,过滤表头。比如,查询商品名称包含XXX的订单

假设订单单头表为t_order, 订单明细表为t_order_dtl,结构如下:

CREATE TABLE `t_order` (

`id` bigint NOT NULL COMMENT 'id',

`customer_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '客户id',

`store_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '分公司id',

`paper_no` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '订单号',

`order_type_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '订单类型名称',

`delivery_date` datetime NOT NULL DEFAULT '2020-01-01 00:00:00' COMMENT '送货时间 到时分秒',

`tag` int NOT NULL DEFAULT '0' COMMENT '订单状态:0.订单提交;1.订单取消;2.订单审核;',

`note` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '备注',

`c_t` bigint NOT NULL DEFAULT '0' COMMENT '创建时间',

`c_u` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '创建人',

`c_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '创建人名称',

`u_t` bigint NOT NULL DEFAULT '0' COMMENT '最后修改时间',

`u_u` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '更新人',

`u_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '修改人名称',

`version` int NOT NULL DEFAULT '0' COMMENT '版本号 用于乐观锁',

PRIMARY KEY (`id`,`store_id`),

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='订单表'

/*!50100 PARTITION BY KEY (store_id)

PARTITIONS 8 */;
CREATE TABLE `t_order_dtl` (

`id` bigint NOT NULL COMMENT 'id',

`store_id` bigint unsigned NOT NULL DEFAULT '0' COMMENT '分公司id',

`paper_no` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'app订单号',

`item` int NOT NULL DEFAULT '0' COMMENT '订单项次',

`ssu_id` bigint NOT NULL DEFAULT '0' COMMENT '商品ssu-id',

`ssu_name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '商品名称',

`spec` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '商品规格',

`unit` varchar(40) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '商品单位',

`order_qty` decimal(26,6) NOT NULL DEFAULT '0.000000' COMMENT '实订数量',

`order_price` decimal(26,6) NOT NULL DEFAULT '0.000000' COMMENT '订货单价',

`ssu_amount` decimal(26,6) NOT NULL DEFAULT '0.000000' COMMENT '商品金额',

`price_paper_no` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '价格单号',

`note` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '明细备注',

`status` tinyint NOT NULL DEFAULT '1' COMMENT '状态, 0 删除,1 生效',

`c_t` bigint NOT NULL DEFAULT '0' COMMENT '创建时间',

`c_u` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '创建人',

`c_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '创建人名称',

`u_t` bigint NOT NULL DEFAULT '0' COMMENT '最后修改时间',

`u_u` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '更新人',

`u_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '修改人名称',

`version` int unsigned NOT NULL DEFAULT '0' COMMENT '版本号 用于乐观锁',

PRIMARY KEY (`id`,`store_id`),

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC COMMENT='订单明细表'

/*!50100 PARTITION BY KEY (store_id)

PARTITIONS 8 */;

假设以前的查询单头语句为:

SELECT

  *

from t_order

where 1 = 1

/* 以下为查询单头的条件 */

同事如下做法:

SELECT

  o.*

from t_order o

left join t_order_dtl d on o.paper_no = d.paper_no

where 1 = 1

/* 以下为查询单头的条件 */

and o.paper_no = 'XXX'

/* 若选择明细条件则加上该条件*/

and d.ssu_name like '%XXX%'

很快发现多了重复单头,于是又加上group by 滤重

SELECT

o.*

from t_order o

left join t_order_dtl d on o.paper_no = d.paper_no

where 1 = 1

/* 以下为查询单头的条件 */

and o.paper_no = 'XXX'

/* 若选择明细条件则加上该条件*/

and d.ssu_name like '%XXX%'

GROUP BY [o.id](http://o.id)

后面结果经过反复折腾,貌似正确了。可是,这样做有巨多风险:

1、明细条件是可选的,上面语句为了某些场景可能用的明细条件,关联上明细表,导致所有场景都查明细表,性能有所损失。

2、因为关联上了明细表,单头为了滤重,强行加上了group by(或者distinct),导致语义变模糊,增加维护成本。

优选实践:

改成下面这样虽然好了一点,但仍不优雅

SELECT

o.*

from t_order o

/* 若存在明细查询条件则加上明细表关联 */

left join t_order_dtl d on o.paper_no = d.paper_no

where 1 = 1

/* 以下为查询单头的条件 */

and d.ssu_name like '%XXX%'

使用EXISTS更为优雅,具体如下:


SELECT

o.*

from t_order o

where 1 = 1

/* 以下为查询单头的条件 */

and o.paper_no = 'XXX'

/* 若选择明细条件则加上该条件*/

and EXISTS (

SELECT 1

from t_order_dtl d

where o.paper_no = d.paper_no

and d.ssu_name like '%XXX%'

)

总结:

案例非常简单,仅是Mysql一个关键字的使用,分享的核心不是这个,而是实际工作中过程中吗,面对问题解决问题的过程。