Mysql лимит строк на группу странные результаты - PullRequest
1 голос
/ 02 сентября 2011

Я хотел получить последние 4 даты для каждого symbolid.Я адаптировал код здесь следующим образом:

set @num := 0, @symbolid := '';
select symbolid, date,
@num := if(@symbolid = symbolid, @num + 1, 1) as row_number,
@symbolid := symbolid as dummy
from projections
group by symbolid, date desc
having row_number < 5

и получил следующие результаты:

symbolid                date   row_number   dummy       
1       '2011-09-01 00:00:00'       1       1
1       '2011-08-31 00:00:00'       3       1
1       '2011-08-30 00:00:00'       5       1
2       '2011-09-01 00:00:00'       1       2
2       '2011-08-31 00:00:00'       3       2
2       '2011-08-30 00:00:00'       5       2
3       '2011-09-01 00:00:00'       1       3
3       '2011-08-31 00:00:00'       3       3
3       '2011-08-30 00:00:00'       5       3
4       '2011-09-01 00:00:00'       1       4
...

Очевидный вопрос: почему я получил только 3 строкиза symbolid, а почему они пронумерованы 1,3,5?Несколько подробностей:

  1. Я пытался форсировать индекс, а не (как показано здесь), и получил одинаковые результаты в обоих направлениях.
  2. Даты правильные, т. Е. В листинге правильно отображаются 3 верхние даты для symbolid, но значение row_number отключено
  3. Когда я не использую оператор «has»,номера строк правильные, т. е. самая последняя дата равна 1, следующая самая последняя - 2 и т. д.

Очевидно, что на вычисляемое поле row_number влияет предложение «has», ноЯ не знаю, как это исправить.

Я понимаю, что я мог бы просто изменить «иметь» на «иметь row_number <7» (6 дает то же, что 5), но это очень уродливо и хотелось бызнать, что нужно сделать, чтобы заставить его «вести себя». </p>

Ответы [ 3 ]

2 голосов
/ 02 сентября 2011

Я не уверен на 100%, почему он так себя ведет (возможно, потому, что логически SELECT обрабатывается до ORDER BY), но он должен работать как положено:

SELECT * 
FROM 
(
    select symbolid, date,
    @num := if(@symbolid = symbolid, @num + 1, 1) as row_number,
    @symbolid := symbolid as dummy
    from projections
    INNER JOIN (SELECT @symbolid:=0)c
    INNER JOIN (SELECT @num:=0)d
    group by symbolid, date desc

) a
WHERE row_number < 5
1 голос
/ 02 сентября 2011

Пользовательские переменные не работают должным образом (см. здесь )

Как правило, вы никогда не должны присваивать значение пользовательской переменной и читать значение в том же выражении. Вы можете получить ожидаемые результаты, но это не гарантировано. Порядок вычисления для выражений с участием пользовательских переменных не определен и может изменяться в зависимости от элементов, содержащихся в данном выражении; Кроме того, этот порядок не гарантируется одинаковым между выпусками MySQL Server. В SELECT @a, @a: = @ a + 1, ... вы можете подумать, что MySQL сначала вычислит @a, а затем выполнит присваивание. Однако изменение оператора (например, путем добавления предложения GROUP BY, HAVING или ORDER BY) может привести к тому, что MySQL выберет план выполнения с другим порядком оценки.

Вот мое предложение

select symbolid, 
substring_index(group_concat(date order by date desc), ',', 4) as last_4_dates
from projections
group by symbolid

Недостаток этого подхода в том, что он сгруппирует дату,
и вам нужно взорваться, прежде чем вы сможете его использовать.

0 голосов
/ 02 сентября 2011

Финальный код:

set @num := 0, @symbolid := '';
select d.* from
(
select symbolid, date,
  @num := if(@symbolid = symbolid, @num + 1, 1) as row_number,
  @symbolid := symbolid as dummy
from projections
order by symbolid, date desc
 ) d
where d.row_number < 5
...