多对多的属性对应表如何做按照类别的多属性匹配搜索

电商设计中常用到的属性对应表需要做按照类别的多属性匹配功能,举例建表如下

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,最常见的地方是商品的属性搜索,参考

https://s.taobao.com/list?spm=a217l.8087239.620327.1.729cb1d2ofc3E9&q=%E7%94%B5%E9%A5%AD%E7%85%B2&style=grid&seller_type=taobao

可以看到能够根据该类别商品的属性进行搜索

比如容量 控制方式 等进行多属性匹配搜索。

那么,就需要一个很复杂的复合查询才行,采用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

posted @ 2017-07-18 11:34  李照耀  阅读(1435)  评论(0编辑  收藏  举报