SQL:需要根据子查询限制结果集

时间:2009-03-04 21:05:47

标签: sql mysql subquery

我遇到了一个相当奇怪的问题。我在mysql数据库中使用以下示例数据:

    |key| data| index | total | timestamp           |
    | # |  a | 1     | 2     | 2009-01-02 01:01:32 |
    | $ |  b | 2     | 2     | 2009-01-02 01:03:32 |
    | % |  c | 1     | 3     | 2009-01-03 01:01:32 |
    | ^ |  d | 2     | 3     | 2009-01-03 01:04:32 |
    | & |  e | 3     | 3     | 2009-01-03 01:02:32 |
    | * |  f | 1     | 2     | 2009-01-05 01:01:32 |

正在发生的是另一个进程(不在我的控制之下)正在接收数据包,并将它们直接存储到数据库中,并带有到达时间的时间戳。假设分组以突发方式到达... a,b将彼此靠近并且被索引为1和2,每个分组包含发送的“总”分组数。 key是正常的自动递增主键。

我需要的是一个视图,它将显示已到达的最新列表(部分列表,如果不是所有数据包都已到达,则可以接受)。

对于上面的查询,理想情况下结果应该只是“f”,但我没有办法做到这一点。如果我们不能以另一种方式得到它,那么返回“a”和“f”将是可以接受的。换句话说,select语句捕获的少量额外数据不是一个大问题。在“f”到达之前的一段时间内,正确的回报是c,d和e。

我的一般想法是:

SELECT * FROM table WHERE total = (
    SELECT total FROM table WHERE timestamp = (
        SELECT MAX(timetamp) FROM table
    )
)
ORDER BY DESC timestamp
LIMIT (
    SELECT total FROM table WHERE timestamp = (
        SELECT MAX(timetamp) FROM table
)

正如你们中的一些人可能已经注意到的那样,你们不能在LIMIT子句中做一个子查询(至少使用mysql)。有没有人有另一种方法来解决这个问题?通过将JOIN嵌套到最近的id的小列表中,可以使上面的查询变得更加清晰,但是仍然会在子查询中留下LIMIT子查询问题。

作为一个两阶段的查询,这是相对微不足道的。问题是它需要成为VIEW的定义select语句。

编辑修复错误的sql示例

4 个答案:

答案 0 :(得分:1)

查询我建议:

SELECT *
FROM packets
WHERE total = ( SELECT total
                FROM packets
                WHERE timestamp = ( SELECT MAX(timestamp) FROM packets ))
    AND timestamp >= ( SELECT MAX(timestamp) FROM packets WHERE idx = 1 )
ORDER BY timestamp DESC;

无为:

mysql> create table packets( id bigint(20) AUTO_INCREMENT primary key, data char(1), idx int(10), total int(10), timestamp datetime );
Query OK, 0 rows affected (0.00 sec)

mysql> insert into packets( data, idx, total, timestamp ) values( 'a', 1 ,2,'2009-01-02 01:01:32'),
    ->     ('b' ,2 ,2,'2009-01-02 01:03:32'),
    ->     ('c'  ,1 ,3,'2009-01-03 01:01:32'),
    ->     ('d'  ,2 ,3,'2009-01-03 01:04:32'),
    ->     ('e' ,3 ,3,'2009-01-03 01:02:32'),
    ->     ('f' ,1 ,2,'2009-01-05 01:01:32');
Query OK, 6 rows affected (0.00 sec)
Records: 6  Duplicates: 0  Warnings: 0

mysql> SELECT *
    -> FROM packets
    -> WHERE total = ( SELECT total
    -> FROM packets
    -> WHERE timestamp = ( SELECT MAX(timestamp) FROM packets ))
    -> AND timestamp >= ( SELECT MAX(timestamp) FROM packets WHERE idx = 1 )
    -> ORDER BY timestamp DESC;
+----+------+------+-------+---------------------+
| id | data | idx  | total | timestamp           |
+----+------+------+-------+---------------------+
|  6 | f    |    1 |     2 | 2009-01-05 01:01:32 |
+----+------+------+-------+---------------------+
1 row in set (0.00 sec)

mysql> delete from packets where id = 6;
Query OK, 1 row affected (0.00 sec)

mysql> SELECT * FROM packets WHERE total = ( SELECT total FROM packets WHERE timestamp = ( SELECT MAX(timestamp) FROM packets )) AND timestamp >= ( SELECT MAX(timestamp) FROM packets WHERE idx = 1 ) ORDER BY timestamp DESC;
+----+------+------+-------+---------------------+
| id | data | idx  | total | timestamp           |
+----+------+------+-------+---------------------+
|  4 | d    |    2 |     3 | 2009-01-03 01:04:32 |
|  5 | e    |    3 |     3 | 2009-01-03 01:02:32 |
|  3 | c    |    1 |     3 | 2009-01-03 01:01:32 |
+----+------+------+-------+---------------------+
3 rows in set (0.00 sec)

mysql>

答案 1 :(得分:0)

如果它们按顺序到达而没有在其间写入其他数据包,则以下内容也应该有效。

SELECT *
FROM Total t
     INNER JOIN (
       SELECT Total, Timestamp
       FROM Total t
            INNER JOIN (
              SELECT Timestamp = MAX(Timestamp) 
              FROM Total
              WHERE ID = 1
            ) ts ON ts.Timestamp = t.Timestamp.
     ) tit ON tit.Total = t.Total AND tit.Timestamp <= t.Timestamp

答案 2 :(得分:0)

这是我在sql server中的方法,你可以转换为mysql语法。

SELECT *
FROM table
     INNER JOIN (SELECT TOP 1 * FROM table ORDER BY key DESC) AS t ON (table.timestamp = t.timestamp AND table.total = t.total)

答案 3 :(得分:0)

我最终得到的查询格式略有不同:

CREATE VIEW NewestTimestamps AS
  SELECT index, MAX(timestamp) AS maxTS FROM table GROUP BY index;

CREATE VIEW NewestList AS
  SELECT * FROM table AS t
    JOIN NewestTimestamps sub ON t.timestamp = sub.maxTS AND sub.index = t.index
  WHERE t.total = (SELECT t2.total FROM table AS t2 
    WHERE timestamp = (SELECT MAX(timestamp) FROM table)
  ); 

这个列表并不是我要求的,但实际上似乎无法可靠地区分新旧数据。相反,这将为我提供索引1处的最新元素,然后是索引2等...此外,WHERE子句将视图的大小限制为最近到达的队列的大小。

请注意,第一个视图是必需的,因为mysql不允许视图中FROM子句中的子查询。