2016-06-04 4 views
1

Возможно ли написать запрос MySQL, который выбирает все элементы, которые находятся в одной строке, из тех, которые соответствуют определенным критериям?MySQL: выберите строки выше

Например, чтобы получить цены, где «Оценка» больше чем 5:

SELECT Price FROM table WHERE Valuation > 5 

Но как бы вы идти о получении цену, которую день после оценка поднялась выше 5?

Date  Price Valuation 
01/06/2016 9.2  9 
31/05/2016 9.3  5 
27/05/2016 9.5  8 
26/05/2016 9.7  7 
25/05/2016 9.5  8 
24/05/2016 9.3  4 
23/05/2016 9.2  3 
20/05/2016 9.2  5 
19/05/2016 8.9  9 
18/05/2016 9.1  4 
17/05/2016 9.2  9 

В этом примере, это будет 9,3, 9,5, 9,7, 9,2 и 9,1

редактировать: столбец индекса может быть добавлен в таблицу, если это поможет.

редактировать: на «один день позже» я имел в виду следующую строку в таблице (а не на следующий день в хронологическом порядке)

+1

Не легко. MySQL не имеет встроенной поддержки функций row-number'ish и других представлений. Есть рабочие места. В любом случае первым шагом является установление плотного упорядочения, затем объединение в prev + next rows (на основе упорядочения), а затем выберите только те объединенные записи, которые удовлетворяют условию. Для более мелких наборов данных зависимый подзапрос * также будет работать соответствующим образом. – user2864740

+0

Спасибо за быстрый ответ. Если бы был добавлен столбец ключа индекса, это поможет? –

+1

Первый/следующий должен быть детерминированным, так что он может быть соединением 'on a.row_rn = b.row_rn + 1' или аналогичным. В зависимом подзапросе это требование отсутствует, так как внутри него можно использовать 'min \ max' с внутренним фильтром' where'. – user2864740

ответ

0

Если просто хотите найти дату/цена/ценность сразу после любой даты, в которой оценка была выше пяти, независимо от оценки в дни предшествующего его использовать это:

select * from T 
where Date in (
    select min(t2.Date) 
    from T t1 inner join T t2 on t2.Date > t1.Date 
    where t1.Valuation > 5 
    group by t1.Date 
) 

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

Если вы ищете для переходов в оценках это сложнее:

select * from T 
where `Date` in (
    select t2.`Date` 
    from T t1 inner join T t2 
     on  t2.`Date` > t1.`Date` 
      and t2.`Date` < date_add(t1.`Date`, interval 7 day) /* bound for performance */ 
    where 
     /* valuation starts at or below 5 */ 
     t1.Valuation <= 5 

     /* did we find the day after the next day? */ 
     and 3 = (
      select count(*) from T t3 
      where t3.`Date` between t1.`Date` and t2.`Date` 
     ) 

     /* did the valuation rise above 5 on the day in between */ 
     and 5 < (
      select t4.Valuation from T t4 
      where t4.`Date` > t1.`Date` and t4.`Date` < t2.`Date` 
     ) 
) 

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

Здесь вы можете проверить запросы на SQL Server. http://rextester.com/BQFJDE14701

+0

возвращает одну «нулевую», «нулевую», «нулевую» запись для меня на моем тестовом наборе (те же данные, что и выше) – EagleRainbow

+0

@EagleRainbow Я просто собирался работать над его тестированием. Но я не вижу, как вы могли получить какие-либо нулевые результаты с фильтрами в запросе. – shawnt00

+1

использовать его сверху: create table quotes ( «date» date not null первичный ключ, цена числовое (5,1) не null, оценка целое число не null ); вставки в кавычки значения ('2016-06-01', 9.2, 9), ('2016-05-31', 9.3, 5), ('2016-05-27', 9.5, 8), ('2016-05-26', 9.7, 7), ('2016-05-25', 9.5, 8), ('2016-05-24', 9.3, 4), ('2016-05-23', 9.2, 3), ('2016-05-20', 9.2, 5), ('2016-05-19', 8,9, 9), ('2016-05-18', 9.1, 4), ('2016-05-17', 9.2, 9); – EagleRainbow

1

Давайте предположим, что мы имеем

CREATE TABLE quotes( 
    `date` date not null primary key, 
    price numeric(5,1) not null, 
    valuation integer not null 
); 


INSERT INTO quotes VALUES 
('2016-06-01', 9.2, 9), 
('2016-05-31', 9.3, 5), 
('2016-05-27', 9.5, 8), 
('2016-05-26', 9.7, 7), 
('2016-05-25', 9.5, 8), 
('2016-05-24', 9.3, 4), 
('2016-05-23', 9.2, 3), 
('2016-05-20', 9.2, 5), 
('2016-05-19', 8.9, 9), 
('2016-05-18', 9.1, 4), 
('2016-05-17', 9.2, 9); 

Тогда следующий SELECT оператор делает трюк:

SELECT basedate, quotes.* 
FROM 
(
    SELECT basedate, min(date) AS nextDate 
    FROM 
    (
     SELECT * FROM 
     (SELECT date as basedate FROM quotes WHERE Valuation > 5) as dates 
     LEFT JOIN quotes ON dates.basedate < quotes.date 
    ) as aggrtable 
    GROUP BY basedate 
) as fullTable 
LEFT JOIN quotes ON fullTable.nextDate = quotes.date; 

Давайте проверим это один из внутреннего к внешнему:

  • innerst Оператор SELECT извлекает цитаты из заинтересованы вообще VALUATION > 5. Эти даты, где это условие выполняется, называются здесь basedate.
  • Следующий слой соединяет неэквивалент с самой таблицей, определяя все возможные даты «после» наших базовых дат.
  • Псевдоним aggrtable определяет даты, которые являются «после» базовой даты, но остаются ближе к базовой дате (с использованием функции агрегации min).
  • Чтобы снова получить значения из quotes, результат снова соединяется с исходной таблицей, что делает условие соединения «минимальной ближайшей датой» базовой даты.

NB: Это неравновесное соединение между ними может стать уродливым на больших таблицах. Предположим, что в базе данных имеется несколько десятков лет. На второй день оценка прошла выше 5. Тогда это неравновесное объединение будет генерировать все даты в результате, кроме первых двух. Таким образом, если вам не повезло, это может привести к записи O(n²), которые необходимо создать временно, но затем снова сгущаются за счет агрегации более min. Таким образом, ваш результирующий набор будет иметь максимум O(n) записей, но может потребоваться время O(n²). Вы также можете перекрестно проверить это обсуждение, используя инструкцию EXPLAIN; там вы найдете EXTRA = "Using temporary, Using filesort", который является чем-то очень злым в отношении производительности.

+0

Этот запрос ищет даты, где оценка была уже больше 5, но переход не найден. – shawnt00

+0

@ shawnt00 Это именно то, что запрашивается в вопросе: «каждый« ОЦЕНКА> 5 »независимо от запуска механизма. Кроме того, этот вопрос также подтверждает это предположение, если посмотреть на пример: '9.3, 9.5, 9.7, 9.2' и' 9.1' (не говоря, что я пойму, где его использовать :)) – EagleRainbow

+0

В нем говорится, что * поднялся выше *. Я читал это как переход снизу. Очевидно, что это требует некоторого уточнения. – shawnt00

0

Вы в основном хотите предыдущую оценку. Один метод использует переменные, а другой использует связанный подзапрос:

select p.*, 
     (select p2.valuation 
     from prices p2 
     where p2.date < p.date 
     order by p2.date desc 
     limit 1 
    ) as prev_valuation 
from prices p; 

Теперь вы просто хотите, чтобы добавить условие на предыдущей оценки больше, чем 5. MySQL давайте вы делаете это с пунктом having:

select p.*, 
     (select p2.valuation 
     from prices p2 
     where p2.date < p.date 
     order by p2.date desc 
     limit 1 
    ) as prev_valuation 
from prices p 
having prev_valuation > 5; 
Смежные вопросы