MySql 2行子查询

时间:2010-09-11 18:54:23

标签: sql mysql

我得到了这样的mysql查询:

SELECT name, 
   (SELECT timePing 
    FROM TerminalPings 
    WHERE terminalsId = Terminals.id
    ORDER BY timePing DESC LIMIT 1) 
 as timePing 
FROM Terminals`

它运行良好,但我需要从TerminalPings中选择前2个timePing,并将它们命名为timePing1和timePing2。最好的方法是什么?

修改 opps,抱歉伙计们,但是TerminalPings和Terminals之间确实存在关系...我的坏:(我觉得忘记写WHERE声明是如此愚蠢:(

3 个答案:

答案 0 :(得分:1)

第二次更新:

为更新的问题导出@OMG Ponies' solution

SELECT     t.name,
           MAX(CASE WHEN x.rank = 1 THEN x.timeping END) AS timeping1,
           MAX(CASE WHEN x.rank = 2 THEN x.timeping END) AS timeping2
FROM       terminals t
LEFT JOIN  (
              SELECT   tp.timeping, tp.terminalsId,
                       IF(@curId <> terminalsId, @rownum := 0, 0),
                       @rownum := @rownum + 1 AS rank,
                       @curId := terminalsId
              FROM     terminalPings tp
              JOIN     (SELECT @rownum := 0, @curId := 0) r
              ORDER BY tp.terminalsId, tp.timeping DESC
           ) x ON (x.terminalsId = t.id AND x.rank <= 2)
GROUP BY   t.name;

新测试用例:

CREATE TABLE terminals (id int, name varchar(20));
INSERT INTO terminals VALUES (1, 'Terminal 1');
INSERT INTO terminals VALUES (2, 'Terminal 2');
INSERT INTO terminals VALUES (3, 'Terminal 3');

CREATE TABLE terminalPings (terminalsId int, timePing int);
INSERT INTO terminalPings VALUES (1, 5);
INSERT INTO terminalPings VALUES (1, 8);
INSERT INTO terminalPings VALUES (1, 4);
INSERT INTO terminalPings VALUES (2, 6);
INSERT INTO terminalPings VALUES (2, 5);
INSERT INTO terminalPings VALUES (3, 4);
INSERT INTO terminalPings VALUES (3, 7);
INSERT INTO terminalPings VALUES (3, 2);

新结果:

+------------+-----------+-----------+
| name       | timeping1 | timeping2 |
+------------+-----------+-----------+
| Terminal 1 |         8 |         5 |
| Terminal 2 |         6 |         5 |
| Terminal 3 |         7 |         4 |
+------------+-----------+-----------+
3 rows in set (0.00 sec)

EXPLAIN输出(MySQL 5.1.45)

+----+-------------+------------+--------+---------------+------+---------+------+------+---------------------------------+
| id | select_type | table      | type   | possible_keys | key  | key_len | ref  | rows | Extra                           |
+----+-------------+------------+--------+---------------+------+---------+------+------+---------------------------------+
|  1 | PRIMARY     | t          | ALL    | NULL          | NULL | NULL    | NULL |    3 | Using temporary; Using filesort |
|  1 | PRIMARY     | <derived2> | ALL    | NULL          | NULL | NULL    | NULL |    8 |                                 |
|  2 | DERIVED     | <derived3> | system | NULL          | NULL | NULL    | NULL |    1 | Using filesort                  |
|  2 | DERIVED     | tp         | ALL    | NULL          | NULL | NULL    | NULL |    8 |                                 |
|  3 | DERIVED     | NULL       | NULL   | NULL          | NULL | NULL    | NULL | NULL | No tables used                  |
+----+-------------+------------+--------+---------------+------+---------+------+------+---------------------------------+
5 rows in set (0.00 sec)

更新: @OMG Ponies' solution比我之前建议的要快(请参阅两个答案的评论,原因)。我在这里留下答案供参考(这个答案仍然会返回一个正确的结果,当你在外部查询中有很多行时会慢得多:在终端表中)。

我仍然会使用两个单独的查询。虽然SQL是一种非常富有表现力的语言,但是不需要在一个查询中执行所有操作......就像在其他编程语言中不需要在单行代码中执行任务一样! MySQL使得下面的解决方案中的子查询无法访问的事实是您在使用复杂查询时可能遇到的一种潜在问题(可能很容易被分解为两个或更多非常简单的查询)。


上一个答案:

您可能希望使用两个单独的查询。但只是为了迎接挑战,您可能需要尝试以下方法:

SELECT name, 
       (
          SELECT timePing FROM
          ( 
             SELECT    timePing, @rowid := @rowid + 1 rownum
             FROM      TerminalPings, (SELECT @rowid := 0) rn
             ORDER BY  timePing DESC 
             LIMIT 1
          ) t
          WHERE t.rownum = 1
       ) AS timePing_1,
       (
          SELECT timePing FROM
          ( 
             SELECT    timePing, @rowid := @rowid + 1 rownum
             FROM      TerminalPings, (SELECT @rowid := 0) rn
             ORDER BY  timePing DESC 
             LIMIT 2
          ) t
          WHERE t.rownum = 2
       ) AS timePing_2 
FROM   Terminals;

测试用例:

CREATE TABLE terminals (name varchar(20));
INSERT INTO terminals VALUES ('Terminal 1');
INSERT INTO terminals VALUES ('Terminal 2');
INSERT INTO terminals VALUES ('Terminal 3');

CREATE TABLE terminalPings (timePing int);
INSERT INTO terminalPings VALUES (1);
INSERT INTO terminalPings VALUES (2);
INSERT INTO terminalPings VALUES (3);
INSERT INTO terminalPings VALUES (4);

结果:

+------------+------------+------------+
| name       | timePing_1 | timePing_2 |
+------------+------------+------------+
| Terminal 1 |          4 |          3 |
| Terminal 2 |          4 |          3 |
| Terminal 3 |          4 |          3 |
+------------+------------+------------+
3 rows in set (0.01 sec)

EXPLAIN输出(MySQL 5.1.45)(参见@OMG Ponies' answer的评论):

+----+----------------------+---------------+--------+---------------+------+---------+------+------+----------------+
| id | select_type          | table         | type   | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+----------------------+---------------+--------+---------------+------+---------+------+------+----------------+
|  1 | PRIMARY              | Terminals     | ALL    | NULL          | NULL | NULL    | NULL |    3 |                |
|  5 | UNCACHEABLE SUBQUERY | <derived6>    | ALL    | NULL          | NULL | NULL    | NULL |    2 | Using where    |
|  6 | DERIVED              | <derived7>    | system | NULL          | NULL | NULL    | NULL |    1 | Using filesort |
|  6 | DERIVED              | TerminalPings | ALL    | NULL          | NULL | NULL    | NULL |    4 |                |
|  7 | DERIVED              | NULL          | NULL   | NULL          | NULL | NULL    | NULL | NULL | No tables used |
|  2 | UNCACHEABLE SUBQUERY | <derived3>    | system | NULL          | NULL | NULL    | NULL |    1 |                |
|  3 | DERIVED              | <derived4>    | system | NULL          | NULL | NULL    | NULL |    1 | Using filesort |
|  3 | DERIVED              | TerminalPings | ALL    | NULL          | NULL | NULL    | NULL |    4 |                |
|  4 | DERIVED              | NULL          | NULL   | NULL          | NULL | NULL    | NULL | NULL | No tables used |
+----+----------------------+---------------+--------+---------------+------+---------+------+------+----------------+

答案 1 :(得分:1)

使用:

   SELECT t.name,
          MAX(CASE WHEN x.rank = 1 THEN x.timeping END) AS timeping1,
          MAX(CASE WHEN x.rank = 2 THEN x.timeping END) AS timeping2
     FROM TERMINALS t
     JOIN (SELECT tp.timeping,
                  @rownum := @rownum + 1 AS rank
             FROM TERMINALPINGS tp
             JOIN (SELECT @rownum := 0) r
         ORDER BY tp.timeping DESC
            LIMIT 2) x
 GROUP BY t.name

我不认为TERMINALSTERMINALPINGS之间没有任何关系。这意味着每个TERMINALS.name值都具有相同的timeping值...

EXPLAIN输出(MySQL 5.1.49)

id  select_type  table        type      possible_keys  key  key_len  ref rows  Extra
------------------------------------------------------------------------------------
1   'PRIMARY'   '<derived2>'  'ALL'     ''             ''            ''  2     'Using temporary; Using filesort'
1   'PRIMARY'   't'           'ALL'     ''             ''            ''  3     'Using join buffer'
2   'DERIVED'   '<derived3>'  'system'  ''             ''            ''  1     'Using filesort'
2,  'DERIVED'   'tp'          'ALL'     ''             ''            ''  4     ''
3   'DERIVED'   ''            ''        ''             ''            ''        'No tables used'

EXPLAIN输出(MySQL 4.1)

id  select_type  table        type      possible_keys  key  key_len  ref rows  Extra
------------------------------------------------------------------------------------
1   'PRIMARY'   't'           'ALL'     ''             ''            ''  3     'Using temporary; Using filesort'
1   'PRIMARY'   '<derived2>'  'ALL'     ''             ''            ''  2     ''
2   'DERIVED'   '<derived3>'  'system'  ''             ''            ''  1     'Using filesort'
2,  'DERIVED'   'tp'          'ALL'     ''             ''            ''  4     ''
3   'DERIVED'   ''            ''        ''             ''            ''        'No tables used'

答案 2 :(得分:0)

为什么使用子查询?这种情况是JOIN的完美候选人。