Как `ВЫБРАТЬ` и создать недостающие строки из предыдущих значений? - PullRequest
4 голосов
/ 28 октября 2011

У меня следующий (упрощенный) результат из 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 (отметка 3) и refid 2 (отметка 2 и 3)

Если возможно, как я могу сделать запрос, чтобы добавить эти пропущенные строки, используя самый последний предыдущий value для этого refid?«Самый последний» означает значение для строки с тем же значением refid, что и для отсутствующей строки, и наибольшим тиком, чтобы тик был меньше, чем тик для отсутствующей строки.например,

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

Дополнительные условия:

  • Все рефиды будут иметь значения при тике = 1.
  • Может быть много «пропущенных» галочек для рефида вsequence (как указано выше для refid 2).
  • Существует много refids, и неизвестно, где будут редкие данные.
  • Будет много тиков за пределами 3, но все последовательные.В правильном результате каждый рефид будет иметь результат для каждого тика.
  • Пропущенные строки заранее неизвестны - они будут выполняться в нескольких базах данных, с одинаковой структурой и разными «пропущенными» строками.

Я использую MySQL и не могу изменить db сейчас.Не стесняйтесь размещать ответ на другом диалекте, чтобы помочь обсуждению, но я выберу ответ на диалекте MySQL поверх других.

Да, я знаю, что это можно сделать в коде, который я реализовал.Мне просто любопытно, можно ли это сделать с помощью SQL.

Ответы [ 4 ]

3 голосов
/ 28 октября 2011

Что value должно быть возвращено, если данной комбинации тик-рефид не существует?В этом решении я просто вернул самое низкое значение для данного refid.

Revision

Я обновил логику, чтобы определить, какое значение использовать в случаеноль.Следует отметить, что я предполагаю, что 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
0 голосов
/ 28 октября 2011

Следующее имеет слишком много подвыборов на мой вкус, но оно генерирует желаемый результат в MySQL, если каждый тик и каждый рефид происходят отдельно, по крайней мере, один раз в таблице.

Начните с запроса, который генерирует каждую пару тиков и рефидов. Следующее использует таблицу для генерации пар, поэтому, если какой-либо тик никогда не появится в базовой таблице, он также будет отсутствовать в генерируемых парах. То же самое относится и к рефидам, хотя ограничение «Все рефиды будут иметь значения при тике = 1» должно гарантировать, что последний никогда не произойдет.

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

Используя это, сгенерируйте каждый отсутствующий тик, пару refid, а также самый большой тик, который существует в таблице, путем выравнивания по refid и присоединению & theta; & ge; к тику. Сгруппируйте по сгенерированному тику, выполните рефид, так как требуется только одна строка для каждой пары. Ключом к фильтрации существующих тиковых пар 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)

Один последний выбор из вышеперечисленного как дополнительный выбор, присоединенный к исходной таблице для получения значения для данного клика, возвращает новые строки для таблицы.

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;

Это вернет хотя бы одну строку с отметкой, равной 1 + наибольшая отметка в таблице. Таким образом, наибольшее значение в этом результате можно игнорировать.

0 голосов
/ 28 октября 2011

Чтобы получить список пар (тик, 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 
0 голосов
/ 28 октября 2011

Если вы заранее знаете, каковы ваши значения 'tick' и 'refid',

  1. Создайте вспомогательную таблицу, которая содержит все возможные значения тиков и реид.
  2. Затем оставьте соединение со вспомогательной таблицей на отметке и добавьте данные в таблицу данных.

Если вы точно не знаете, каковы ваши значения 'tick' и 'refid', вы, возможно, все еще можете использовать этот метод, но вместо статической вспомогательной таблицы он должен генерироваться динамически.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...