多对多的属性对应表如何做按照类别的多属性匹配搜索
电商设计中常用到的属性对应表需要做按照类别的多属性匹配功能,举例建表如下
CREATE TABLE goods_attr ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '自增id', `goods_id` int(11) DEFAULT '0' COMMENT '商品id', `type` int(11) DEFAULT '0' COMMENT '属性类型:1:商品类型 2:支持语言 3:支持平台', `value` varchar(50) DEFAULT '' COMMENT '属性值', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='商品属性信息表';
INSERT INTO goods_attr (`id`, `goods_id`, `type`, `value`) VALUES ('1', '118', '1', '1'); INSERT INTO goods_attr (`id`, `goods_id`, `type`, `value`) VALUES ('2', '118', '1', '5'); INSERT INTO goods_attr (`id`, `goods_id`, `type`, `value`) VALUES ('3', '118', '1', '8'); INSERT INTO goods_attr (`id`, `goods_id`, `type`, `value`) VALUES ('4', '118', '2', '1'); INSERT INTO goods_attr (`id`, `goods_id`, `type`, `value`) VALUES ('5', '146', '3', '1'); INSERT INTO goods_attr (`id`, `goods_id`, `type`, `value`) VALUES ('6', '146', '1', '1'); INSERT INTO goods_attr (`id`, `goods_id`, `type`, `value`) VALUES ('7', '157', '1', '8'); INSERT INTO goods_attr (`id`, `goods_id`, `type`, `value`) VALUES ('8', '157', '1', '5'); INSERT INTO goods_attr (`id`, `goods_id`, `type`, `value`) VALUES ('9', '157', '2', '1'); INSERT INTO goods_attr (`id`, `goods_id`, `type`, `value`) VALUES ('10', '157', '1', '1');
如上所述,goods_id type value组成唯一的一条记录
比如
首先,118商品拥有三个类型,是一个复合类型商品 有 1 5 8 三种类型
同时,118商品有一个语言 是1类型的语言
同时,118商品有1个平台 是1类型平台
146商品就只有一个类型是1,一种平台是1,而且没有语言
然后现在前台或者接口调用处要根据某个属性进行反查询得到商品id,最常见的地方是商品的属性搜索,参考
可以看到能够根据该类别商品的属性进行搜索
比如容量 控制方式 等进行多属性匹配搜索。
那么,就需要一个很复杂的复合查询才行,采用inner join多次连表也是能做到的,不过书写的SQL就比较复杂,而且很难保证效率。
针对我们的表 假如要查询 商品类型为5和8的复合类型,而且支持语言为1的商品,该如何查询呢?
最容易想到的是
select * from goods_attr where ( type=1 and value in ('5','8') ) or ( type=2 and value in ('1') )
但是这样的查询条件必然是不正确的,因为 只要符合其中一个条件,那些不相干的记录也被查出来了,稍稍改进进行自链接
select * from goods_attr as a left join goods_attr as b on a.goods_id=b.goods_id and (( a.type=1 and a.value in ('5','8') ) and ( b.type=2 and b.value in ('1') ))
这样的自链接看似是正确的,实际上偏离了我们要求的同时满足复合属性的记录存在,value必须有5 而且又有8 而不是in ('5','8') 所以也是不正确的。
如何得到同时有5又有8呢?只能这样写
select a.* from goods_attr as a inner join goods_attr as b on a.goods_id=b.goods_id and a.type=1 and a.value=5 and b.value=8
这种情况下能查询出 type=1的情况下既有5 又有8 的属性。那如何加上查询type=2的情况下 value=1的属性条件呢?当然是再连接一次!
select c.goods_id from ( select a.* from goods_attr as a inner join goods_attr as b on a.goods_id=b.goods_id and a.type=1 and a.value=5 and b.value=8 ) as tmp inner join goods_attr as c on c.goods_id=tmp.goods_id where c.type=2 and c.value=1
至此,自链接方法讲述完毕,如果不想用大量的自链接,又该如何做呢?
我想到,可以使用行转列的方式进行操作,行转列,借助 group_concat实现
select goods_id,type, group_concat(value) as v from ( select * from goods_attr order by goods_id asc,type asc,value asc ) as u where (type=1 and value in ('5','8')) or (type=2 and value in ('1')) group by goods_id,type
然而这样查询出来的结果并没有自然的排序放入 group_concat中 所以需要强制性指明排序
select goods_id,type, group_concat(value order by goods_id asc,type asc,value asc) as v from goods_attr where (type=1 and value in ('5','8')) or (type=2 and value in ('1')) group by goods_id,type
这样就能强制性拿到排序
现在得到了属性的分组,然后我们还要把属性分类和分组的值再次合并形成一列,和刚刚一样 再次使用group_concat
select group_concat(type,"-",v) as final,goods_id from (select goods_id,type,v from ( select goods_id,type, group_concat(value order by goods_id asc,type asc,value asc) as v from goods_attr as u where (type=1 and value in ('5','8')) or (type=2 and value in ('1')) group by goods_id,type ) as tmp ) as t group by goods_id
很显然,我们拿到了所有符合的商品属性的列和商品id,那么再加一条查询条件即可或得到我们需要的属性列表
select goods_id from ( select group_concat(type,"-",v) as final,goods_id from (select goods_id,type,v from ( select goods_id,type, group_concat(value order by goods_id asc,type asc,value asc) as v from goods_attr as u where (type=1 and value in ('5','8')) or (type=2 and value in ('1')) group by goods_id,type ) as tmp ) as t group by goods_id ) as f where final='1-5,8,2-1'
这样我们就得到了需要的商品id!!!
因这篇文章探讨SQL比较深入,所以这里加一下版权。
版权所有,转载需要声明原文地址 http://www.cnblogs.com/lizhaoyao/p/7199611.html