Имитация функции задержки в MySQL

| time                | company | quote |
+---------------------+---------+-------+
| 0000-00-00 00:00:00 | GOOGLE  |    40 |
| 2012-07-02 21:28:05 | GOOGLE  |    60 |
| 2012-07-02 21:28:51 | SAP     |    60 |
| 2012-07-02 21:29:05 | SAP     |    20 |

Как сделать задержку в этой таблице в MySQL, чтобы вывести разницу в кавычках, например:

GOOGLE | 20
SAP    | 40  

person javanx    schedule 03.07.2012    source источник
comment
Только два на company? или это переменная?   -  person Michael Berkowski    schedule 03.07.2012
comment
Я вижу, у вас здесь две компании, но всегда ли там только две строки на компанию? В таком случае вы можете тривиально использовать агрегаты MAX() - MIN(). Если на company больше 2 строк, все сложнее.   -  person Michael Berkowski    schedule 03.07.2012
comment
Мне просто нужны две последние метки времени ... может быть много записей для одной и той же компании, но мне просто нужно взять две последние метки времени и распечатать разницу котировок   -  person javanx    schedule 03.07.2012
comment
Если компания представлена ​​только одной строкой, хотите ли вы вернуть эту компанию в результаты? Если да, то какую разницу вернуть за него?   -  person Andriy M    schedule 03.07.2012
comment
И, наконец, в чем конкретная проблема заключается в этом вопросе: как вернуть последние две строки для каждой компании или как рассчитать разницу между двумя строками? Я не думаю, что вам следует смешивать эти две проблемы в одном вопросе. Вы, вероятно, получите более качественные ответы, если зададите два отдельных вопроса (в случае, если вам действительно нужно решить обе проблемы).   -  person Andriy M    schedule 03.07.2012
comment
В вашем примере, почему для одной из компаний результат не является отрицательным? Google переходит с 40 до 60, а SAP - с 60 до 20. sqlfiddle.com/ #! 2 / b62e1 / 1/0 Или вам нужно только абсолютное движение независимо от направления (в этом случае возьмите _ 1_)?   -  person eggyal    schedule 03.07.2012
comment
Большое спасибо за вашу помощь. Я просто хочу представить дельту, например, это 20 для Google ab -40 для Sap, но теперь он делает обратное   -  person javanx    schedule 03.07.2012
comment
Предложение Эггиала возвращает именно те результаты. В чем проблема? (Кстати, @eggyal, тебе, наверное, стоит ответить именно так.)   -  person Andriy M    schedule 03.07.2012
comment
@AndriyM: Я совершенно уверен, что должен быть способ получше (который включает полное сканирование таблицы), но я изо всех сил пытаюсь его увидеть. Я полагаю, можно было бы взять максимум группы дважды и получить что-то вроде sqlfiddle.com/#!2 / e28e7 / 1/0 ... но это так уродливо и требует доступа к таблице 5 раз - неужели это лучший вариант? Я оставлял его открытым в надежде, что кто-то другой может прийти с чем-то гораздо более проницательным!   -  person eggyal    schedule 03.07.2012


Ответы (3)


Это мой любимый прием MySQL.

Вот как вы имитируете функцию задержки:

SET @quot=-1;
select time,company,@quot lag_quote, @quot:=quote curr_quote
  from stocks order by company,time;
  • lag_quote содержит значение цитаты предыдущей строки. Для первой строки @quot равно -1.
  • curr_quote содержит значение цитаты текущей строки.

Примечания:

  1. Предложение order by важно здесь, как и в обычной оконной функции.
  2. Вы также можете использовать задержку для company, чтобы быть уверенным, что вы вычисляете разницу в котировках одного и того же company.
  3. Таким же образом можно реализовать счетчики строк @cnt:=@cnt+1

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

РЕДАКТИРОВАТЬ:

Теперь перейдем к вашему вопросу о получении результата в указанном вами формате:

SET @quot=0,@latest=0,company='';
select B.* from (
select A.time,A.change,IF(@comp<>A.company,1,0) as LATEST,@comp:=A.company as company from (
select time,company,quote-@quot as change, @quot:=quote curr_quote
from stocks order by company,time) A
order by company,time desc) B where B.LATEST=1;

Вложенность не связана, так что не так плохо (вычислительно), как выглядит (синтаксически) :)

Дайте мне знать, если вам понадобится помощь с этим.

person Dojo    schedule 03.07.2012
comment
Я получаю сообщение об ошибке. Операторы DDL и DML не разрешены в панели запросов для MySQL; разрешены только операторы SELECT. Поместите DDL и DML в панель схемы. - person javanx; 03.07.2012
comment
Хотя ошибка не указывает на это, попробуйте включить allowMultiQueries. Это параметр соединителя. Информацию о соединителе JDBC см .: dev. mysql.com/doc/refman/5.1/en/. Можете ли вы успешно запустить его из клиента MySQL? - person Dojo; 03.07.2012
comment
Вы также можете попробовать выполнить два оператора по отдельности, но в одном сеансе. - person Dojo; 03.07.2012
comment
@javanx Привет, я автор SQL Fiddle. Указанное вами сообщение об ошибке на самом деле было ошибкой в ​​том, как я обрабатывал определенные типы запросов MySQL. Благодаря вашему сообщению здесь я распознал это как таковое и разработал решение, которое его исправляет (см., Например, здесь: sqlfiddle.com / #! 2 / 4f8a1 / 2). Спасибо! - person Jake Feasel; 04.07.2012
comment
Это сработало для меня! Но я не понимаю механизма. Могли бы вы объяснить? - person David Rubinger; 16.01.2019
comment
@symobol указывает, что это переменная сеанса. Итак, @quot - это переменная. SET @quot=0 инициализирует его значением 0. Представьте остальную часть запроса как цикл for, который проходит по набору результатов. всякий раз, когда вы делаете @quot:=x, значение столбца x текущей строки копируется в @quot. В чем разница между SET @quot=something и @quot:=something (обратите внимание на двоеточие)? Не знаю, они хотят, чтобы синтаксис был таким. Когда вы используете SET, он не ожидает двоеточия, когда вы просто делаете @variable_name = something, ему нужно двоеточие перед знаком равенства. так что это @quot:=something. - person Dojo; 18.01.2019
comment
Обратите внимание на столбцы заказов / операции @quot lag_quote, @quot:=quote curr_quote. Сначала я просто прочитал значение @quot и назвал столбец lag_quote. @quot по-прежнему сохраняет ранее назначенное значение, то есть значение quote предыдущей строки, поскольку мы еще не выполнили @quot:=quote для текущей строки. Затем мы выполняем @quot:=quote, результатом которого является обновленное значение @quot, то есть цитата из текущей строки. И мы называем этот столбец столбцом как curr_quote - person Dojo; 18.01.2019
comment
ПРИМЕЧАНИЕ. Тип данных переменной должен соответствовать типу данных, которые вы пытаетесь запаздывать. Например, если вы хотите накапливать поле FLOAT, вы должны инициализировать свою переменную как @quot=0.0, иначе это не сработает. У меня ушла минута, чтобы понять это! - person quickshiftin; 15.11.2019

Начиная с MySQL 8.0 и выше нет необходимости моделировать LAG. Он изначально поддерживается,

Оконная функция:

Возвращает значение expr из строки, которая отстает (предшествует) текущей строке на N строк в ее разделе. Если такой строки нет, возвращается значение по умолчанию. Например, если N равно 3, возвращаемое значение будет по умолчанию для первых двух строк. Если N или default отсутствуют, значения по умолчанию равны 1 и NULL соответственно.

SELECT
     company,
     quote,
     LAG(quote) OVER(PARTITION BY company ORDER BY time) AS prev_quote
FROM tab;

DBFiddle Demo

person Lukasz Szozda    schedule 06.04.2018

Для достижения желаемого результата сначала нужно найти последнюю и предпоследнюю временные метки для каждой компании. Это довольно просто со следующим запросом:

SELECT c.company, c.mts, max(l.ts) AS lts
  FROM (SELECT company, max(ts) AS mts FROM cq GROUP BY company) AS c
  LEFT JOIN cq l
    ON c.company = l.company AND c.mts > l.ts
 GROUP BY c.company, c.mts;

Теперь вам нужно объединить этот подзапрос с исходной таблицей, чтобы получить желаемые результаты:

SELECT c.company, l.quote, coalesce(l1.quote, 0),
       (l.quote - coalesce(l1.quote, 0)) AS result
  FROM (SELECT c.company, c.mts, max(l.ts) AS lts
      FROM (SELECT company, max(ts) AS mts FROM cq GROUP BY company) AS c
      LEFT JOIN cq l
        ON c.company = l.company AND c.mts > l.ts
     GROUP BY c.company, c.mts) AS c
  LEFT JOIN cq AS l ON l.company = c.company AND l.ts = c.mts
  LEFT JOIN cq AS l1 ON l1.company = c.company AND l1.ts = c.lts;

Вы можете увидеть результаты на SQL Fiddle.

Этот запрос использует только стандартные возможности SQL и должен работать в любой СУБД.

person vyegorov    schedule 03.07.2012
comment
Хороший ответ, особенно для людей, готовящихся к техническим собеседованиям, поскольку они часто не поощряют использование оконных функций. - person Evan Zamir; 19.04.2021