2

假设我有两个表ab;列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 值?

提前致谢!

4

2 回答 2

1

您可以通过手动指定每个 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       |
+------+------+------------+

(严格来说,IN (2,3,7)in the innerSELECT和 the ELSEin theCASE都是多余的;两者都可以)。

急需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可能会很尴尬,并且研究异常可能很长。

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

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

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

       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

使用 3 个WHENs 即可完成所有工作。

于 2012-10-23T08:22:12.573 回答
0
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)

将子查询切换为连接

SELECT * FROM a
join ( SELECT x FROM b 
             WHERE x,id IN (2,3,7) OR x IS NULL
             ORDER BY FIELD(x,3,2,7) DESC
             LIMIT 1) as X on X.id = a.id 
于 2012-10-23T08:05:42.980 回答