MySQL:类似MAX的子查询中选择一行,只是自定义顺序优先级

标签 mysql sql

假设我有两个表abid 列在表 a 中是唯一的,但在表 b 中有多个条目,列 x 具有不同的值以及该表中的其他列。 b表的主键是(id,x)

我需要能够将 b 中的一行连接到 SELECT 查询,就像我可以像这样使用 MAX 那样:

SELECT * FROM a
INNER JOIN b USING(id)
WHERE b.x = (SELECT MAX(x) FROM b WHERE b.id = a.id)

有了 MAX,这没问题。但我不需要MAX。我实际上需要这样的东西:

SELECT * FROM a
INNER JOIN b USING(id)
WHERE b.x = (SELECT x FROM b 
             WHERE x IN (2,3,7) OR x IS NULL
             ORDER BY FIELD(x,3,2,7) DESC
             LIMIT 1)

此查询失败,因为我的 MySQL 版本不支持此子查询中的 LIMIT。有没有另一种方法可以确保按照我提供的顺序至少加入一行?

我正在尝试做的事情的示意图概述是:

Select * from table a for each id
Join table b where x = 7
If no entry in b exists where a.id=b.id and x = 7, join b where x = 2
If no entry in b exists where a.id=b.id and x IN (2,7), join b where x = 3
If no entry in b exists where a.id=b.id and x IN (2,3,7), join b where x IS NULL

我有这个需求是因为:

  • 我不能在我需要的这段特定代码中使用多个查询;它必须是一个神奇的一体化查询
  • 我不能使用 INNER JOIN (SELECT *) 语句
  • 我无法在任何 id 出现不止一次的情况下进行查询(因此 DISTINCT 不是选项,因为表 b 中的值可能不同)
  • 我肯定知道表 b 有一个 a.id 的条目,其中 x 是 2、3、7 或 NULL,但我不知道是哪个,也不知道 a 有多少条目.id在表b中。
  • 升级 MySQL 不是最佳选择,因为此代码必须在我的任何客户的通用服务器上运行

所以,简而言之,我的问题是:是否有类似于 MAX 的函数可以考虑特定顺序而不是 MAX 值?

提前致谢!

最佳答案

您可以通过手动指定每个 X 的订购重量来做到这一点。

mysql> select * from aa;
+----+------+
| id | name |
+----+------+
|  1 | John |
|  2 | Ted  |
|  3 | Jill |
|  4 | Jack |
+----+------+
4 rows in set (0.00 sec)

mysql> select * from bb;
+------+------+------------+
| id   | x    | class      |
+------+------+------------+
|    1 |    7 | HighPriori |
|    1 |    2 | MediumPrio |
|    1 |    3 | LowPriorit |
|    2 |    2 | Medium     |
|    2 |    3 | Low        |
|    3 |    3 | Low only   |
+------+------+------------+
5 rows in set (0.00 sec)


select version();
+-------------+
| version()   |
+-------------+
| 5.5.25a-log |
+-------------+

SELECT aa.name, bb.x, bb.class
    FROM aa LEFT JOIN bb ON (aa.id = bb.id AND bb.x IN (2,3,7)
        AND bb.x = ( SELECT x FROM bb WHERE bb.id = aa.id AND x IN (2,3,7)
           ORDER BY CASE
               WHEN x = 7 THEN 100
               WHEN x = 2 THEN 200
               WHEN x = 3 THEN 300
               ELSE 400
           END LIMIT 1 )
    );

最里面的 SELECT 将根据优先级选择最合适的 X 值:如果可用则为 7,否则为 2,否则为 3。如果在本例中“优先级”是不按顺序,即 7 高于 3,但 2 也高于 3。

然后 LEFT JOIN 将匹配该记录(如果它存在)或 NULL(如果不存在)。

John 有 2,3 和 7 并获得 7 记录,而 Ted 有 2 和 3 并获得 2:

+------+------+------------+
| name | x    | class      |
+------+------+------------+
| John |    7 | HighPriori |
| Ted  |    2 | Medium     |
| Jill |    3 | Low only   |
| Jack | NULL | NULL       |
+------+------+------------+

(严格来说就是内层SELECT中的IN (2,3,7)CASE中的ELSE 是多余的;两者都可以。

CASE的需要...

如果 X 字段已经建立了顺序,例如,您希望值 3,2,7 以 2 - 3 - 7 或 7 - 3 - 2 数字顺序排列,则可以不使用 CASE:代替

       ORDER BY CASE
           WHEN x = 7 THEN 100
           WHEN x = 2 THEN 200
           WHEN x = 3 THEN 300
           ELSE 400
       END 

你可以指定

       ORDER BY x

       ORDER BY x DESC

...性能改进很小,即使 x 被索引,但如果你有很多 X 值,那么在 CASE 中指定它们可能会很尴尬,学习异常(exception)情况可能会很长。

但是假设您希望对象按此顺序排列

       First                          Last
       20,21,22,23,40,41,42,9,1,2,3,4,5

通常在 X 被声明为 1-5 的无符号值时出现(“我们将永远需要更多类,而 1 总是将是第一个!”),然后几周后有人补充说“这是一个令人兴奋的新产品,必须先行!”异常并为其分配 9,最后它再次发生“让我们为即将推出的产品系列添加一个以 2X 和 4X 开头的全新 XY 类......”,你可以这样做

       WHEN x <= 5 THEN 900+x
       WHEN x = 9   THEN 809
       ELSE 700 + x

按顺序翻译上述困惑的集合

720,721,722,723,740,741,742,809,901,902,903,904,905

并使用三个 WHEN 完成所有工作。

关于MySQL:类似MAX的子查询中选择一行,只是自定义顺序优先级,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13025814/

相关文章:

使用 Ubuntu Server 12.04 的 VPS 上的 MySQL 每天都会崩溃 - 如何找到错误或冲突?

mysql - 从 PostgreSQL 中的 group by 子句中获取特定行

SQL方法用单个空格替换重复空格

php - MySQL:选择 `project_id` 等于任何 `project` 与特定 `user_id` 的行

mysql - mssql转mysql出错

php - 无法流式传输 pdf : headers already sent codeigniter

mysql - 如何在模式的特定位置找到这个 ^

php - 使用 HTML 和 PHP 时无法将电子邮件插入 MySQL 表

mysql - 无法执行 GROUP_CONCAT

sql - VBA/Visual Basic。简单的字符串 SQL