MySQL:如何为基于范围而不是外键的关系编写查询?

MySQL:如何为基于范围而不是外键的关系编写查询?,mysql,Mysql,我在MySQL中有三个表是相关的,但在技术上并没有通过外键相互链接。它们是:用户、级别和类 用户表有一个数字类型的karma列。基于这个业力数字,我想知道用户的等级,我从等级表中检索。这不是一个艰难的关系,因为一个等级与一系列业力值相关联,比如: 一级最小业力:0 二级最小业力:100 三级最小业力:500 ... 因此,如果用户的业力为400,则返回值应为2。为了使事情稍微复杂一些,级别号表示用户的类,其定义存储在classes表中。这也是一种范围关系: 第1级 猫头鹰类最低5级 狮子班最低1

我在MySQL中有三个表是相关的,但在技术上并没有通过外键相互链接。它们是:用户、级别和类

用户表有一个数字类型的karma列。基于这个业力数字,我想知道用户的等级,我从等级表中检索。这不是一个艰难的关系,因为一个等级与一系列业力值相关联,比如:

一级最小业力:0 二级最小业力:100 三级最小业力:500 ... 因此,如果用户的业力为400,则返回值应为2。为了使事情稍微复杂一些,级别号表示用户的类,其定义存储在classes表中。这也是一种范围关系:

第1级 猫头鹰类最低5级 狮子班最低100级 ... 总之,我们讨论的是三个表,它们基于范围值彼此具有隐式关系。我的问题涉及如何有效地查询这些表。对于我来说,一个非常常见的需求是根据一个条件获取一个或多个用户的信息,结果集应该包含用户详细信息,还应该包含用户的级别和类别

对于单个用户,我成功地编写了这个查询,效果很好:

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语句中。如果级别发生变化,您必须进行编码更改,而不是更新文件中的某些记录。坏主意。