我有2张桌子-包装和物品。物料表包含所有属于包装的物料以及位置信息。像下面的示例表:
Packages table id, type(enum{general,special}) 1, general 2, special Items table id, package_id, location 1, 1, America 2, 1, Europe 3, 2, Europe
问题:我想查找属于某个位置的所有“特殊”软件包,如果未找到特殊软件包,则它应返回属于同一位置的“一般”软件包。
所以,
对于“欧洲”:包裹2应该是特殊包裹,因此应返回(包裹1也属于欧洲,但由于是普通包裹而不是必需的)
对于“美国”:由于没有特殊包装,应返回包装1
这是两种不同的解决方案:(注意:我将枚举字段称为“ package_type”)
第一个解决方案(通过IF()函数):
select i.location, if(ps.id is not null, ps.id, pg.id) as package_id from (select distinct location from Items) i inner join (select i.location, p.id from Items i inner join Packages p on (i.package_id = p.id and p.package_type = 'general') ) pg on (i.location = pg.location) left join (select i.location, p.id from Items i inner join Packages p on (i.package_id = p.id and p.package_type = 'special') ) ps on (i.location = ps.location)
此解决方案本质上采用这些位置,并将其与常规(假定为存在;因此inner join)和特殊包(为可选;因此left join)一起添加到包中。它创建如下记录:
inner join
left join
location | general-package | [special-package]
然后,它使用MySQLIF函数首先尝试选择特殊软件包的ID,然后回退到常规软件包的ID。
IF
第二个解决方案(通过将枚举转换为整数):
select i.location, p.id from (select i.location, max(cast(package_type as unsigned)) as package_type from Items i left join Packages p on (i.package_id = p.id) group by location ) i inner join (select i.location, p.id, p.package_type from Items i inner join Packages p on (i.package_id = p.id) ) p on (i.location = p.location and i.package_type = p.package_type)
该解决方案利用了枚举存储为整数的事实。它将枚举转换为整数。special在这种情况下将返回2并且general将返回1。因为在这种情况下保证这些特殊值高于一般值(即2> 1),所以我们可以使用MAX聚合函数。现在,我们基本上有了一张位置及其“推荐包装”的表格(即特殊的(如果存在的话,否则为普通的))。我们只需将其与期望的包类型一起加入到普通查询中,即可返回正确的结果。
special
2
general
1
MAX
免责声明:我不确定这两种方法的效率,因此您可能需要自己进行测试。
如果您想重新设计表格或对表格进行归一化以提高效率,我认为这种设计可能更合适:
GeneralPackages table id, name 1, General Package 1 SpecialPackages table id, name 1, Special Package 1 2, Special Package 2 Items table id, general_package_id, special_package_id, location 1, 1, NULL, America 2, 1, 2, Europe
这样做的好处是,可以更轻松地在数据库级别强制执行多个规则:
缺点是,每次使用一个旧查询时,您可能都需要使用UNION ALL。