Ограничение на запрос соединения, используя между - PullRequest
0 голосов
/ 18 октября 2018

Я пытаюсь отфильтровать некоторые результаты, проиндексированные по временной метке, используя другой набор результатов, определяющий допустимые периоды временной метки.

Текущий запрос:

SELECT Measurements.moment AS "moment",
       Measurements.actualValue,
       start,
       stop
FROM Measurements
       INNER JOIN (SELECT COALESCE(@previousValue <> M.actualValue AND @previousResource = M.resourceId, 1) AS "changed",
                          (COALESCE(@previousMoment, ?)) AS "start",
                          M.moment AS "stop",
                          @previousValue AS "actualValue",
                          M.resourceId,
                          @previousMoment := moment,
                          @previousValue := M.actualValue,
                          @previousResource := M.resourceId
                   FROM Measurements `M`
                          INNER JOIN (SELECT @previousValue := NULL, @previousResource := NULL, @previousMoment := NULL) `d`
                   WHERE (M.moment BETWEEN ? AND ?) AND
                         (M.actualValue > ?)
                   ORDER BY M.resourceId ASC, M.moment ASC) `changes` ON Measurements.moment BETWEEN changes.start AND changes.stop
WHERE (Measurements.resourceId = 1) AND
      (Measurements.moment BETWEEN ? AND ?) AND
      (changes.changed)
ORDER BY Measurements.moment ASC;

resourceId, moment это уже индекс.Поскольку это на самом деле данные временных рядов, есть ли способ ограничить объединение всего одним совпадением для повышения производительности?

Образец данных

+-------------+---------------------+------------+
| actualValue | moment              | resourceId |
+-------------+---------------------+------------+
|        0.01 | 2018-09-26 07:50:25 |        1   |
|        0.01 | 2018-09-26 07:52:35 |        1   |
|        0.01 | 2018-09-26 07:52:44 |        2   |
|        0.01 | 2018-09-26 07:52:54 |        1   |
|        0.01 | 2018-09-26 07:53:03 |        1   |
|        0.01 | 2018-09-26 07:53:13 |        2   |
|        0.01 | 2018-09-26 07:53:22 |        1   |
|        0.01 | 2018-09-26 07:54:32 |        1   |
|        0.01 | 2018-09-26 07:55:41 |        1   |
|        0.01 | 2018-09-26 07:56:51 |        1   |
+-------------+---------------------+------------+

Ожидаетсяoutput : Все измерения с resourceId=1, где resourceId=2 имели измерение в ту же минуту (в расширенной версии минута может быть динамической).

+-------------+---------------------+------------+
| actualValue | moment              | resourceId |
+-------------+---------------------+------------+
|        0.01 | 2018-09-26 07:52:35 |        1   |
|        0.01 | 2018-09-26 07:52:54 |        1   |
|        0.01 | 2018-09-26 07:53:03 |        1   |
|        0.01 | 2018-09-26 07:53:22 |        1   |
+-------------+---------------------+------------+

Ответы [ 3 ]

0 голосов
/ 19 октября 2018

Требуется составной индекс:

Measurements:  INDEX(resourceId, moment)  -- in this order

В подзапросе может потребоваться AND (Measurements.moment BETWEEN ? AND ?)

В «производной таблице» (подзапросе), которую оптимизатор может игнорироватьORDER BY.Однако, если вы добавите LIMIT, ORDER BY будет выполнено.

0 голосов
/ 27 октября 2018

Я нашел решение, использующее отмену поворота таблицы:

SELECT moment, value
FROM (SELECT IF(resourceId = ? AND @previousValue = 0, NULL, actualValue)       AS value,
             measurements.moment,
             resourceId,
             @previousValue := IF(resourceId <> ?, actualValue, @previousValue) AS enabled
      FROM (SELECT *
            FROM (SELECT moment,
                         Measurements.actualValue,
                         Measurements.resourceId AS resourceId
                  FROM Measurements
                  WHERE Measurements.resourceId = ?
                    AND moment BETWEEN ? AND ?
                  UNION (SELECT start,
                                periods.actualValue AS actualValue,
                                resourceId
                         FROM (SELECT COALESCE(@previousValue <> M3.actualValue,                                            1)                                                              AS "changed",
                                      (COALESCE(@previousMoment, ?))                                           AS "start",
                                      @previousMoment := M3.moment                                             AS "stop",
                                      COALESCE(@previousValue, IF(M3.actualValue = 1, 0, 1)) AS "actualValue",
                                      M3.resourceId                                                            AS resourceId,
                                      @previousValue := M3.actualValue
                               FROM Measurements `M3`
                                      INNER JOIN (SELECT @previousValue := NULL,
                                                         @previousMoment := NULL) `d`
                               WHERE (M3.moment BETWEEN ? AND ?)
                               ORDER BY M3.resourceId ASC, M3.moment ASC) AS periods
                         WHERE periods.changed)) AS measurements
            ORDER BY moment ASC) AS measurements
             INNER JOIN (SELECT @previousValue := NULL) `k`) AS mixed
WHERE value IS NOT NULL
  AND resourceId = ?;

Это, по сути, запускает таблицу один раз за выборку, выполняя ~ 40k x ~ 4k строк за 100 мс.

0 голосов
/ 18 октября 2018

Когда вы используете независимый подзапрос (в данном случае), он выполняется полностью перед внешним запросом.В вашем случае это может быть потенциально массивным, и, вероятно, большинство строк на самом деле не нужно.

Если вы перефразируете запрос с использованием внутреннего JOIN, тогда вторичный доступ к таблице будет немедленно отфильтрован, избегая необходимости полного сканирования таблицы.

Попробуйте выполнить следующеезапрос:

select 
    m.moment,
    m.actualValue,
    c.moment as start,
    timestampadd(minute, 1, c.moment) as stop
  from Measurements m
  join Measurements c on m.moment
    between c.moment and timestampadd(minute, 1, c.moment)
  where m.resourceId = 1
    and c.resourceId = 2
    and m.moment between ? and ?
  order by m.moment
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...