我在MySQL中有三个相关的表,但在技术上并没有通过外键相互链接.它们是:用户,级别和类.
users表有一个karma列,一个数字类型.根据这个业力数字,我想知道用户的级别,我从级别表中检索.这不是一个很难的关系,因为一个级别与一系列业力值相关联,如下所示:
因此,如果用户的业力为400,则返回值应为2.为了使事情稍微复杂一些,级别编号表示用户的类,其定义存储在classes表中.这又是一个范围关系:
总之,我们讨论的是基于范围值的三个彼此隐含关系的表.我的问题涉及如何有效地查询这些表.我非常普遍的需求是根据条件获取一个或多个用户的信息,结果集应该包含用户详细信息,还包括用户的级别和类.
对于单个用户,我设法写这个查询工作正常:
SELECT u.*, lv.num as level, lvc.title as class, lvc.id as classid
FROM user as u, level as lv, levelclass as lvc
WHERE u.id = ? AND lv.min_karma <= u.karma AND lv.num <= lvc.minlevel_num
ORDER BY lv.num DESC LIMIT 1;
Run Code Online (Sandbox Code Playgroud)
但是,如果我通过留下WHERE u.id =?来扩大结果集.并删除LIMIT 1,从而查询用户列表,我得到所有三个表的组合.通常,您可以通过在键上执行内部联接来降低行,但由于这是范围检查,因此不起作用.我尝试在内连接条件下使用范围检查,但结果相同.即使使用分组我也无法得到我想要的结果.
在绝望的尝试中,我想出了这个查询,它有效:
SELECT usr.*,
(SELECT lv.num as level
FROM user as u, level as lv, levelclass as lvc
WHERE u.id = usr.id AND lv.min_karma <= u.karma AND lv.num <= lvc.minlevel_num
ORDER BY lv.num DESC LIMIT 1) as level,
(SELECT lvc.title as class
FROM user as u, level as lv, levelclass as lvc
WHERE u.id = usr.id AND lv.min_karma <= u.karma AND lv.num <= lvc.minlevel_num
ORDER BY lv.num DESC LIMIT 1) as class,
(SELECT lvc.image as class_image
FROM user as u, level as lv, levelclass as lvc
WHERE u.id = usr.id AND lv.min_karma <= u.karma AND lv.num <= lvc.minlevel_num
ORDER BY lv.num DESC LIMIT 1) as class_image,
(SELECT lvc.id as classid
FROM user as u, level as lv, levelclass as lvc
WHERE u.id = usr.id AND lv.min_karma <= u.karma AND lv.num <= lvc.minlevel_num
ORDER BY lv.num DESC LIMIT 1) as classid
FROM user as usr
ORDER BY usr.$sortby $direction LIMIT ?,?
Run Code Online (Sandbox Code Playgroud)
但是,对我来说这似乎非常低效.基本上我在这里做的是从级别和类表中为每个列(!)编写一个子查询.如果我在一个子查询中查询级别或类表的多个列,它将再次返回所有值的组合.我觉得我的数据库技能存在差距,我遗漏了一些明显的东西,这是我不了解的功能......
你能帮助我吗?如何为一组行写一个有效的查询,这些行组合了三个表中的列,但是没有按键链接(范围代替)?
PS:我知道如果我将这个模式非规范化以将级别和类组合到users表中,我可以大大简化场景,但是有一个特定的原因我需要这个,相信我.
如果你可以修改Level和Class表以获得行中的实际范围(即在Level表中有minkarma和maxkarma,在Class表中有minlevel和maxlevel),你应该能够在你的连接条件中使用它它只会为每个用户返回一行.
例:
SELECT * FROM user as u
INNER JOIN level as lv on
u.karma >= lv.min_karma AND u.karma <= lv.max_karma
INNER JOIN levelclass as lvc on
lv.num >= lvc.min_level AND lv.num <= lvc.max_level
Run Code Online (Sandbox Code Playgroud)
获取多行的原因是您只通过对最小值进行过滤来匹配多个级别和类.