假设我有两个表a 和b; id 列在表 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/