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

时间:2012-10-23 07:52:58

标签: mysql sql

假设我有两个表 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。
  • 升级MySQL不是最佳选择,因为此代码必须在我的任何客户的通用服务器上运行

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

提前致谢!

2 个答案:

答案 0 :(得分: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)中的SELECTELSE中的CASE是多余的;要么会这样做。

需要CASE ......

如果X字段已经建立了订单,例如,您希望以3 - 3 - 7或7 - 3 - 2数字顺序显示值3,2,7,则可以不使用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,最后再次发生“让我们为即将推出的产品系列添加一个以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来完成所有工作。

答案 1 :(得分: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