MySQL:如何为基于范围而不是外键的关系编写查询?
我在MySQL中有三个表是相关的,但在技术上并没有通过外键相互链接。它们是:用户、级别和类 用户表有一个数字类型的karma列。基于这个业力数字,我想知道用户的等级,我从等级表中检索。这不是一个艰难的关系,因为一个等级与一系列业力值相关联,比如: 一级最小业力:0 二级最小业力:100 三级最小业力:500 ... 因此,如果用户的业力为400,则返回值应为2。为了使事情稍微复杂一些,级别号表示用户的类,其定义存储在classes表中。这也是一种范围关系: 第1级 猫头鹰类最低5级 狮子班最低100级 ... 总之,我们讨论的是三个表,它们基于范围值彼此具有隐式关系。我的问题涉及如何有效地查询这些表。对于我来说,一个非常常见的需求是根据一个条件获取一个或多个用户的信息,结果集应该包含用户详细信息,还应该包含用户的级别和类别 对于单个用户,我成功地编写了这个查询,效果很好:MySQL:如何为基于范围而不是外键的关系编写查询?,mysql,Mysql,我在MySQL中有三个表是相关的,但在技术上并没有通过外键相互链接。它们是:用户、级别和类 用户表有一个数字类型的karma列。基于这个业力数字,我想知道用户的等级,我从等级表中检索。这不是一个艰难的关系,因为一个等级与一系列业力值相关联,比如: 一级最小业力:0 二级最小业力:100 三级最小业力:500 ... 因此,如果用户的业力为400,则返回值应为2。为了使事情稍微复杂一些,级别号表示用户的类,其定义存储在classes表中。这也是一种范围关系: 第1级 猫头鹰类最低5级 狮子班最低1
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;
但是,如果我通过离开WHERE u.id=?去掉限制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 ?,?
然而,在我看来,这是非常低效的。基本上,我在这里做的是为每一列编写一个子查询!我需要从等级表和等级表中找到。如果我在一个子查询中查询level或class表的多个列,它将再次返回所有值的组合。我觉得我的数据库技能有一个差距,一些我明显缺少的东西,一个我不知道的功能
你能帮我吗?如何为一组行编写一个高效的查询,这些行组合了三个表中的列,而不是通过键范围链接
PS:我知道,如果我将此模式反规范化以将级别和类合并到用户表中,我可以大大简化场景,但我需要这样做的具体原因是,相信我。如果您可以修改级别和类表,使其具有行中的实际范围,即在级别表中具有minkarma和maxkarma,在类表中的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
获取多行的原因是,您仅通过筛选最小值来匹配多个级别和类。我需要查看您的确切数据结构才能确定,但类似这样的内容如何
SELECT
*
FROM
[User] u
LEFT OUTER JOIN
[Level] lv
ON
lv.num =
(
SELECT
MAX(lv2.num)
FROM
[Level] lv2
WHERE
lv2.min_karma <= u.karma
)
LEFT OUTER JOIN
[LevelClass] lvc
ON
lvc.id =
(
SELECT
MAX(lvc2.id)
FROM
[LevelClass] lvc2
WHERE
lvc2.minlevel_num <= lv.num
)
您想在case statemnet上使用连接
SELECT
*
FROM
users u
JOIN
levels l
ON
l.id =
CASE
WHEN u.karma <= 10
THEN 1
WHEN u.karma > 11 & u.karma <= 100
THEN 2
WHEN u.karma > 100 & u.karma <= 1000
THEN 3
WHEN u.karma > 1000
THEN 4
END
JOIN
class c
ON
c.id is
CASE
WHEN l.id <= 5
THEN 'owl'
WHEN l.id > 5 & u.karma <= 10
THEN 'lion'
END
品尝季节。在等级和等级表中是否有max字段以及min字段?这会很容易添加吗?+1,很好的加速比,反规范化非常小。根据他的问题的结尾,他对表格没有修改能力。+1如果他有最小值和最大值,那么绝对是简单但完美的方法。我没有级别和类的最大列,但我添加了它们并测试了您的查询。很好!我希望有人注意到这个答案。它不需要表mod或subselect。您可以将数据硬编码到case语句中。如果级别发生变化,您必须进行编码更改,而不是更新文件中的某些记录。坏主意。