如何`SELECT`并从之前的值制造缺失的行?

时间:2011-10-28 18:30:08

标签: mysql sql

我有SELECT * FROM table ORDER BY tick,refid的以下(简化)结果:

tick refid value
----------------
1    1     11
1    2     22
1    3     33
2    1     1111
2    3     3333
3    3     333333

注意refid 1(tick 3)和refid 2(ticks 2和3)的“缺失”行

如果可能,如何使用value的最新优先refid进行查询以添加这些缺失的行? “最近”表示具有与缺失行相同的refid的行的值和最大的tick,使得tick小于缺失行的tick。 e.g。

tick refid value
----------------
1    1     11
1    2     22
1    3     33
2    1     1111
2    2     22
2    3     3333
3    1     1111
3    2     22
3    3     333333

附加条件:

  • 所有refid的值均为tick = 1。
  • 按顺序可能有许多“缺失”的滴答声(如上面的补偿2)。
  • 有许多refid,并且不知道哪些稀疏数据在哪里。
  • 除了3之外还会有很多刻度,但都是连续的。在正确的结果中,每个refid都会有每个tick的结果。
  • 事先不知道缺少的行 - 这将在多个数据库上运行,所有数据库都具有相同的结构,并且不同的“缺失”行。

我正在使用MySQL,现在无法更改数据库。随意用另一种方言发表答案,以帮助讨论,但我会选择MySQL方言中的答案而不是其他方言。

是的,我知道这可以在代码中完成,我已经实现了。我只是好奇是否可以使用SQL。

4 个答案:

答案 0 :(得分:3)

当给定的tick-refid组合不存在时,应该返回value什么?在这个解决方案中,我只返回给定refid的最低值。

<强>修订

我已经更新了逻辑,以确定在null的情况下使用什么值。应该注意的是,我假设ticks + refid在表中是唯一的。

Select Ticks.tick
    , Refs.refid
    , Case
        When Table.value Is Null 
            Then    (
                    Select T2.value
                    From Table As T2
                    Where T2.refid = Refs.refId
                        And T2.tick =  (
                                        Select Max(T1.tick)
                                        From Table As T1
                                        Where T1.tick < Ticks.tick
                                            And T1.refid = T2.refid
                                        )
                    )
        Else Table.value
        End As value
From    (
        Select Distinct refid
        From Table
        ) As Refs
    Cross Join  (
                Select Distinct tick
                From Table
                ) As Ticks
    Left Join Table
        On Table.tick = Ticks.tick
            And Table.refid = Refs.refid

答案 1 :(得分:0)

如果您事先知道'tick'和'refid'值是什么,

  1. 创建一个包含所有可能的tick和refid值的帮助器表。
  2. 然后从帮助表中选择tick并将其重新绑定到数据表。
  3. 如果您不确切知道'tick'和'refid'值是什么,您可能仍然可以使用此方法,但不是静态帮助程序表,而是必须动态生成。

答案 2 :(得分:0)

为了让插入的对(list,refid)列表得到一个完整的列表:

     SELECT a.tick, b.refid
       FROM ( SELECT DISTINCT tick  FROM t) a
 CROSS JOIN ( SELECT DISTINCT refid FROM t) b

现在从该查询中减去现有的:

     SELECT a.tick tick, b.refid refid
       FROM ( SELECT DISTINCT tick  FROM t) a
 CROSS JOIN ( SELECT DISTINCT refid FROM t) b
 MINUS
     SELECT DISTINCT tick, refid FROM t

现在你可以用t加入以获得最终查询(注意我使用内部联接+左联接来获得以前的结果,但你可以适应):

INSERT INTO t(tick, refid, value)
SELECT c.tick, c.refid, t1.value
  FROM (        SELECT a.tick tick, b.refid refid
                  FROM ( SELECT DISTINCT tick  FROM t) a
            CROSS JOIN ( SELECT DISTINCT refid FROM t) b
            MINUS
                SELECT DISTINCT tick, refid FROM t
       ) c
 INNER JOIN t t1 ON t1.refid = c.refid and t1.tick < c.tick
  LEFT JOIN t t2 ON t2.refid = c.refid AND t1.tick < t2.tick AND t2.tick < c.tick
 WHERE t2.tick IS NULL 

答案 3 :(得分:0)

以下有太多的子选择符合我的口味,但它会在MySQL中生成所需的结果,只要每个tick和每个refid在表中至少单独出现一次。

从一个生成每对tick和refid的查询开始。下面使用该表来生成对,因此如果任何tick不会出现在基础表中,它也将从生成的对中丢失。对于refids也是如此,尽管“所有refid将具有tick = 1的值”的限制应该确保后者永远不会发生。

SELECT tick, refid FROM
  (SELECT refid FROM chadwick WHERE tick=1) AS r
  JOIN 
  (SELECT DISTINCT tick FROM chadwick) AS t

使用此方法,通过等同于refid并且θ -joining on tick,生成每个缺失的tick,refid对以及表中存在的最大tick。按生成的tick进行分组,refid,因为每个对只需要一行。过滤掉现有tick,refid对的关键是HAVING子句。严格来说,你可以省略HAVING;生成的查询将返回现有行及其现有值。

SELECT tr.tick, tr.refid, MAX(c.tick) AS ctick
  FROM 
      (SELECT tick, refid FROM
        (SELECT refid FROM chadwick WHERE tick=1) AS r
        JOIN 
        (SELECT DISTINCT tick FROM chadwick) AS t
      ) AS tr
  JOIN chadwick AS c ON tr.tick >= c.tick AND tr.refid=c.refid
  GROUP BY tr.tick, tr.refid
  HAVING tr.tick > MAX(c.tick)

从上面的一个最终选择作为子选择,加入到原始表中以获取给定ctick的值,返回表的新行。

INSERT INTO chadwick
SELECT missing.tick, missing.refid, c.value
  FROM (SELECT tr.tick, tr.refid, MAX(c.tick) AS ctick
    FROM 
      (SELECT tick, refid FROM
        (SELECT refid FROM chadwick WHERE tick=1) AS r
        JOIN 
        (SELECT DISTINCT tick FROM chadwick) AS t
      ) AS tr
    JOIN chadwick AS c ON tr.tick >= c.tick AND tr.refid=c.refid
    GROUP BY tr.tick, tr.refid
  ) AS missing
  JOIN chadwick AS c ON missing.ctick = c.tick AND missing.refid=c.refid
;

示例表上的效果以及(tick, refid)(refid, tick)索引:

+----+-------------+------------+-------+-------------------+----------+---------+----------+------+---------------------------------+
| id | select_type | table      | type  | possible_keys     | key      | key_len | ref      | rows | Extra                           |
+----+-------------+------------+-------+-------------------+----------+---------+----------+------+---------------------------------+
|  1 | PRIMARY     | <derived2> | ALL   | NULL              | NULL     | NULL    | NULL     |    3 |                                 |
|  1 | PRIMARY     | c          | ALL   | tick_ref,ref_tick | NULL     | NULL    | NULL     |    6 | Using where; Using join buffer  |
|  2 | DERIVED     | <derived3> | ALL   | NULL              | NULL     | NULL    | NULL     |    9 | Using temporary; Using filesort |
|  2 | DERIVED     | c          | ref   | tick_ref,ref_tick | ref_tick | 5       | tr.refid |    1 | Using where; Using index        |
|  3 | DERIVED     | <derived4> | ALL   | NULL              | NULL     | NULL    | NULL     |    3 |                                 |
|  3 | DERIVED     | <derived5> | ALL   | NULL              | NULL     | NULL    | NULL     |    3 | Using join buffer               |
|  5 | DERIVED     | chadwick   | index | NULL              | tick_ref | 10      | NULL     |    6 | Using index                     |
|  4 | DERIVED     | chadwick   | ref   | tick_ref          | tick_ref | 5       |          |    2 | Using where; Using index        |
+----+-------------+------------+-------+-------------------+----------+---------+----------+------+---------------------------------+

正如我所说,选择太多了。临时表可能会有所帮助。

检查丢失的刻度:

SELECT clo.tick+1 AS missing_tick
  FROM chadwick AS chi
    RIGHT JOIN chadwick AS clo ON chi.tick = clo.tick+1
  WHERE chi.tick IS NULL;

这将返回至少一行,其中tick等于1 +表中的最大刻度。因此,可以忽略此结果中的最大值。