从许多表中获取最大值

时间:2011-10-20 04:14:32

标签: mysql

我可以想到有两种方法可以从多个表中获得类似的结果。一个是UNION,另一个是JOIN。关于SO的类似问题都已用UNION回答。这是我刚刚找到的编码器:

SELECT max(up.id) AS up, max(sc.id) AS sc, max(cl.id) AS cl
    FROM updates up, chat_staff sc, change_log cl

解释

+----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra                        |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+
|  1 | SIMPLE      | NULL  | NULL | NULL          | NULL | NULL    | NULL | NULL | Select tables optimized away |
+----+-------------+-------+------+---------------+------+---------+------+------+------------------------------+

我的问题是 - 这比以下更好吗?

SELECT "up.id" AS K, max(id) AS V FROM updates 
 UNION
SELECT "sc.id" AS K, max(id) AS V FROM chat_staff 
 UNION
SELECT "cl.id" AS K, max(id) AS V FROM change_log

解释

+----+--------------+--------------+------+---------------+------+---------+------+-------------------------------------+
| id | select_type  | table        | type | possible_keys | key  | key_len | ref  | rows | Extra                        |
+----+--------------+--------------+------+---------------+------+---------+------+------+------------------------------+
|  1 | PRIMARY      | NULL         | NULL | NULL          | NULL | NULL    | NULL | NULL | Select tables optimized away |
|  2 | UNION        | NULL         | NULL | NULL          | NULL | NULL    | NULL | NULL | Select tables optimized away |
|  3 | UNION        | NULL         | NULL | NULL          | NULL | NULL    | NULL | NULL | Select tables optimized away |
| NULL | UNION RESULT | <union1,2,3> | ALL  | NULL          | NULL | NULL    | NULL | NULL |                              |
+----+--------------+--------------+------+---------------+------+---------+------+------+------------------------------+

2 个答案:

答案 0 :(得分:2)

这两种方法都很好。事实上,我有另一种方法:

SELECT
    IFNULL(maxidup,0) max_id_up,
    IFNULL(maxscup,0) max_sc_up,
    IFNULL(maxclup,0) max_cl_up
FROM
    (SELECT max(id) maxidup FROM updates)    up,
    (SELECT max(id) maxidsc FROM chat_staff) sc,
    (SELECT max(id) maxidcl FROM change_log) cl
;

此方法像第一个示例一样并排显示三个值。如果其中一个表为空,它也会显示0。

mysql> DROP DATABASE IF EXISTS junk;
Query OK, 3 rows affected (0.11 sec)

mysql> CREATE DATABASE junk;
Query OK, 1 row affected (0.00 sec)

mysql> use junk
Database changed
mysql> CREATE TABLE updates (id int not null auto_increment primary key,x int);
Query OK, 0 rows affected (0.07 sec)

mysql> CREATE TABLE chat_staff LIKE updates;
Query OK, 0 rows affected (0.07 sec)

mysql> CREATE TABLE change_log LIKE updates;
Query OK, 0 rows affected (0.06 sec)

mysql> INSERT INTO updates (x) VALUES (37),(84),(12);
Query OK, 3 rows affected (0.06 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> INSERT INTO change_log (x) VALUES (37),(84),(12),(14),(35);
Query OK, 5 rows affected (0.09 sec)
Records: 5  Duplicates: 0  Warnings: 0

mysql> SELECT
    -> IFNULL(maxidup,0) max_id_up,
    -> IFNULL(maxidsc,0) max_sc_up,
    -> IFNULL(maxidcl,0) max_cl_up
    -> FROM
    -> (SELECT max(id) maxidup FROM updates)    up,
    -> (SELECT max(id) maxidsc FROM chat_staff) sc,
    -> (SELECT max(id) maxidcl FROM change_log) cl
    -> ;
+-----------+-----------+-----------+
| max_id_up | max_sc_up | max_cl_up |
+-----------+-----------+-----------+
|         3 |         0 |         5 |
+-----------+-----------+-----------+
1 row in set (0.00 sec)

mysql> explain SELECT  IFNULL(maxidup,0) max_id_up,  IFNULL(maxidsc,0) max_sc_up, IFNULL(maxidcl,0) max_cl_up  FROM  (SELECT max(id) maxidup FROM updates)    up, (SELECT max(id) maxidsc FROM chat_staff) sc,  (SELECT max(id) maxidcl FROM change_log) cl;
+----+-------------+------------+--------+---------------+------+---------+------+------+------------------------------+
| id | select_type | table      | type   | possible_keys | key  | key_len | ref  | rows | Extra                        |
+----+-------------+------------+--------+---------------+------+---------+------+------+------------------------------+
|  1 | PRIMARY     | <derived2> | system | NULL          | NULL | NULL    | NULL |    1 |                              |
|  1 | PRIMARY     | <derived3> | system | NULL          | NULL | NULL    | NULL |    1 |                              |
|  1 | PRIMARY     | <derived4> | system | NULL          | NULL | NULL    | NULL |    1 |                              |
|  4 | DERIVED     | NULL       | NULL   | NULL          | NULL | NULL    | NULL | NULL | Select tables optimized away |
|  3 | DERIVED     | NULL       | NULL   | NULL          | NULL | NULL    | NULL | NULL | No matching min/max row      |
|  2 | DERIVED     | NULL       | NULL   | NULL          | NULL | NULL    | NULL | NULL | Select tables optimized away |
+----+-------------+------------+--------+---------------+------+---------+------+------+------------------------------+
6 rows in set (0.02 sec)

在我的EXPLAIN计划中,Select tables optimized away就像你的一样。为什么?

由于id在所有表中都被索引,因此索引用于检索max(id)而不是表。因此,Select tables optimized away是正确的答案。

六分之一,另外六分之一。您如何从中提供数据完全取决于您的个人偏好。

更新2011-10-20 15:32美国东部时间

您评论过:您知道表锁定会如何影响这个吗?假设有一个表被锁定了。这个查询是否会锁定另外两个并保持锁定直到第一个被释放?

这取决于存储引擎。如果所有相关表都是MyISAM,那么MyISAM在INSERT,UPDATE,DELETE上执行完整表锁定的可能性是确定的。如果这三个表是InnoDB,那么MVCC的好处就是提供事务隔离。这将允许每个人在一个时间点查看数据。除了DDL和针对InnoDB的explcit LOCK TABLES之外,不应阻止您的查询。

答案 1 :(得分:1)

实际上,虽然它们很相似,但却有一个微妙的区别。第一个为您提供一行三列表(值为“跨越”),第二个为您提供一个三行,两列表(值为“向下”)。

如果您对以任何一种形式处理或查看数据感到满意,那么可能会降低性能。

根据我的经验(这与MySQL没有任何关系),后一个查询可能会更好。那是因为我与之合作的DBMS能够并行运行这样的查询以提高效率,并在完成时将它们组合在一起。它们位于不同表上的事实意味着它们之间的锁争用将为零。

可能是DBMS的查询分析引擎可以对第一个查询进行类似的优化,但它需要比我从大多数查询中看到的更多的智能。

一个简单的观点,如果您使用union all而不是union,则告诉数据库不要删除重复的行。由于K列对于所有三个子查询都不同,因此在这种情况下不会出现任何重复。

但是,正如所有的优化一样,衡量,不要猜测!当然不要把随机网络漫游者的咆哮当作福音(是的,甚至是我)。

将各种候选表与您可能在生产中使用的属性放在一起,并比较每种候选表的性能。