SQLinfo.ru - Все о MySQL

Форум пользователей MySQL

Задавайте вопросы, мы ответим

Вы не зашли.

#1 23.08.2011 14:59:04

kolombetam
Участник
Зарегистрирован: 26.04.2010
Сообщений: 7

MySQL+GROUP BY WEEK()+GROUP_CONCAT()

Всем привет

столкнулся с проблемой сортировки выборки в MySQL. Почти весь день просидел, испробовал разные варианты, но нужного результата не добился. Возможно, кто-то подскажет правильное решение или просто еще один вариант "попробовать", потому что мне явно нужен свежий взгляд на проблему.

Итак, имеем таблицу MySQL (пожалуйста, смотрите вложение)

Имеем упрощенный до минимума ЗАПРОС 1

Код:

SELECT
  order_id,
  cost,
  rd_id,
  product_order_date
FROM report_data
GROUP BY WEEK(product_order_date)

Возвращает результат (РЕЗУЛЬТАТ 1)

order_id   cost   rd_id   product_order_date
88164        0         1         0000-00-00 00:00:00
143747      0         3         2011-07-18 11:40:42
149491     10.1     19        2011-07-25 04:27:24
151797     138      66        2011-08-02 14:45:06
152271     94.14   109      2011-08-08 15:49:20
155702     94.14   166      2011-08-17 06:00:03

теперь выполним ЗАПРОС 2

Код:

SELECT
  order_id,
  cost,
  rd_id,
  product_order_date
FROM report_data

вернет все строки (РЕЗУЛЬТАТ 2) (публиковать не буду, так как слишком много записей)


смотрим РЕЗУЛЬТАТ 2. Видим, что действительно в неделю с 8-08 п- 14-08 первая запись была сделана 2011-08-08 15:49:20 и имеет rd_id=109, а в неделю с 15-08 по 21-08 первая запись сделана 2011-08-17 06:00:03 и имеет rd_id=166.

Теперь сравним с РЕЗУЛЬТАТом 1 и увидим, что ЗАПРОС 1 отработал правильно


Теперь в ЗАПРОС 1 добавим одну строку и получим ЗАПРОС 3

Код:

SELECT
  GROUP_CONCAT(order_id) AS ids,
  rd_id,
  order_id,
  cost,
  product_order_date
FROM report_data
GROUP BY WEEK(product_order_date)

вернет РЕЗУЛЬТАТ 3

ids                   rd_id     order_id     cost     product_order_date
88164,88165           1       88164        0        0000-00-00 00:00:00
143829,1438...        12     143829      0        2011-07-19 10:19:19
150957,1509...        52     150957     33.48   2011-07-29 12:00:20
152246,1522...        97     152246     42.54   2011-08-05 17:26:18
152358,1523...        146   152358     12.92   2011-08-11 12:05:02
155726,1557...        175   155726     63.72   2011-08-17 12:04:53

Видим, что для предпоследней строки почему-то выбрана запись с rd_id=146 и датой 2011-08-11 12:05:02, а для последней строки запись rd_id=175 и датой 2011-08-17 12:04:53

Таким образом получается, что GROUP_CONCAT() изменяет порядок группы

по ссылке http://lists.mysql.com/mysql/2664 прочел, что WEEK() не гарантирует выборку именно первой записи в группе. Но при этом, без GROUP_CONCAT() группировка правильная.

пробовал следующие запросы
ЗАПРОС 4

Код:

SELECT
  rd_id,
  order_id,
  cost,
  product_order_date,
  WEEK(product_order_date) AS _week
FROM report_data
GROUP BY _week

ЗАПРОС 5

Код:

SELECT
  GROUP_CONCAT(order_id) AS ids,
  rd_id,
  order_id,
  cost,
  product_order_date,
  WEEK(product_order_date) AS _week
FROM report_data
GROUP BY _week

ЗАПРОС 4 возвращает результат, аналогичный РЕЗУЛЬТАТу 1,  а ЗАПРОС 5 - аналогичный РЕЗУЛЬТАТу 3.

Заметил, что такой (баг?) наблюдается только при использовании функций дат в предложении группировки. Т.е замена WEEK() на  DAY(), DAYOFYEAR(), MONTH() и другие функции даты не спасает - GROUP_CONCAT() изменяет порядок выборки


MIN(), MAX() на подходят по той причине, что дата может изменяться
Т.е, например:

запись с rd_id=2 имеет дату 2011-08-09 12:12:12, а запись с rd=3 имеет дату 2011-08-12 23:23:23. Это дата, когда запись была добавлена в таблицу или последний раз обновлялась.

rd_id     date
     2        2011-08-09 12:12:12
     3        2011-08-12 23:23:23

Т.е. администратор может отредактировать запись с rd_id=2, например, 13 августа. Тогда таблица будет иметь вид
rd_id     date
     2        2011-08-13 12:12:12
     3        2011-08-12 23:23:23

Значит MIN(rd_id) вернет неправильный результат.

Кстати, пробовал MIN() и MAX() применять к дате, но результата никакого: без GROUP_CONCAT() выборка правильная, с
GROUP_CONCAT() - неправильная



Пожалуйста, если кто знает решение проблемы или видит мою ошибку, подскажите.

Заранее спасибо


Прикрепленные файлы:
Attachment Icon 1.zip, Размер: 2,074 байт, Скачано: 576

Неактивен

 

#2 23.08.2011 16:38:09

vasya
Архат
MySQL Authorized Developer
Откуда: Орел
Зарегистрирован: 07.03.2007
Сообщений: 5842

Re: MySQL+GROUP BY WEEK()+GROUP_CONCAT()

kolombetam написал:

смотрим РЕЗУЛЬТАТ 2. Видим, что действительно в неделю с 8-08 п- 14-08 первая запись была сделана 2011-08-08 15:49:20 и имеет rd_id=109, а в неделю с 15-08 по 21-08 первая запись сделана 2011-08-17 06:00:03 и имеет rd_id=166.

Теперь сравним с РЕЗУЛЬТАТом 1 и увидим, что ЗАПРОС 1 отработал правильно

Если Вы указали в SELECT поля, по которым не производится группировка без аггрегирующих функций (MAX, SUM, AVG, GROUP_CONCAT, ...), то MySQL выберет произвольного представителя группы. Стандарт SQL запрещает такие запросы, но MySQL, к сожалению, их обрабатывает из-за чего возникают не предсказуемые результаты.

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

Неактивен

 

#3 23.08.2011 16:57:56

kolombetam
Участник
Зарегистрирован: 26.04.2010
Сообщений: 7

Re: MySQL+GROUP BY WEEK()+GROUP_CONCAT()

Спасибо за ответ

Если я правильно понял, то нужно в SELECT использовать аггрегирующие функции. Изменил запрос


SELECT
  CONCAT(t._date)
FROM (SELECT
        order_id,
        product_order_date AS _date
      FROM report_data) AS t
GROUP BY WEEK(t._date)
 


Подзапрос нужен для того, чтобы сортировать порядок в группе. Иногда нужно выбрать первую запись в группе, иногда последнюю.
Такой запрос отрабатывает правильно (по крайней мере, результат возвращает верный)

concat(t._date)
0000-00-00 00:00:00
2011-07-18 11:40:42
2011-07-25 04:27:24
2011-08-02 14:45:06
2011-08-08 15:49:20
2011-08-17 06:00:03

если же добавить GROUP_CONCAT(),

SELECT
  GROUP_CONCAT(t.order_id) AS ids,
  CONCAT(t._date)
FROM (SELECT
        order_id,
        product_order_date AS _date
      FROM report_data) AS t
GROUP BY WEEK(t._date)
 


то результат меняется (становится неверным) - обратите внимание на даты

ids    concat(t._date)
88164,88165    0000-00-00 00:00:00
143831,143...    2011-07-19 11:12:55
149694,150...    2011-07-26 14:41:20
152212,152...    2011-08-05 11:19:04
152271,152...    2011-08-08 15:49:20
155723,155...    2011-08-17 11:41:01

Каким образом GROUP_CONCAT() влияет на результат? И можно ли управлять этим процессом? Использование синтаксиса GROUP_CONCAT(field_name ORDER BY filed_name2) изменяет только лишь порядок в строке конкатенации

Неактивен

 

#4 23.08.2011 17:13:52

vasya
Архат
MySQL Authorized Developer
Откуда: Орел
Зарегистрирован: 07.03.2007
Сообщений: 5842

Re: MySQL+GROUP BY WEEK()+GROUP_CONCAT()

kolombetam написал:

Если я правильно понял, то нужно в SELECT использовать аггрегирующие функции.

Да.

kolombetam написал:

Изменил запрос


SELECT
  CONCAT(t._date)
FROM (SELECT
        order_id,
        product_order_date AS _date
      FROM report_data) AS t
GROUP BY WEEK(t._date)
 


Подзапрос нужен для того, чтобы сортировать порядок в группе.

Что-то от меня ускользает смысл данного запроса.
1. Ваш подзапрос ничего не сортирует.
2. Даже если добавить в него order by это ничего не изменит.

Используйте min, max по отношению к дате, чтобы выбрать 1ую/последнюю запись за неделю.

Неактивен

 

#5 23.08.2011 17:16:37

vasya
Архат
MySQL Authorized Developer
Откуда: Орел
Зарегистрирован: 07.03.2007
Сообщений: 5842

Re: MySQL+GROUP BY WEEK()+GROUP_CONCAT()

Например:

SELECT min(product_order_date) as `первая запись за неделю`, max(product_order_date) as `последняя запись за неделю`
FROM report_data GROUP BY WEEK(product_order_date)

Неактивен

 

#6 23.08.2011 17:22:12

evgeny
Гуру
Зарегистрирован: 04.05.2009
Сообщений: 335

Re: MySQL+GROUP BY WEEK()+GROUP_CONCAT()

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

Неактивен

 

#7 23.08.2011 19:02:45

kolombetam
Участник
Зарегистрирован: 26.04.2010
Сообщений: 7

Re: MySQL+GROUP BY WEEK()+GROUP_CONCAT()

Спасибо за ответы! Совет с использованием MIN(), MAX() очень выручил. Действительно, свежий взгляд - новое решение.

В итоге все оказалось намного проще по сравнению с теми дебрями, куда я залез. Подзапрос, как и Вы и говорили, vasya, оказался не нужен


SELECT
  GROUP_CONCAT(DISTINCT order_id) AS order_ids,
  MIN(order_id) AS per_order_id,
  MIN(product_order_date) AS per_product_order_date,
  FORMAT(SUM(cost),2) AS sum_cost
FROM report_data
GROUP BY WEEK(product_order_date)
 


Еще раз спасибо

Неактивен

 

#8 23.08.2011 19:32:01

vasya
Архат
MySQL Authorized Developer
Откуда: Орел
Зарегистрирован: 07.03.2007
Сообщений: 5842

Re: MySQL+GROUP BY WEEK()+GROUP_CONCAT()

kolombetam написал:

В итоге все оказалось намного проще по сравнению с теми дебрями, куда я залез. Подзапрос, как и Вы и говорили, vasya, оказался не нужен

Это вы ещё не успели залезть smile И подзапрос вам понадобится, только другой.


Если администратор отредактирует запись с rd_id=2, например, 13 августа. Тогда таблица будет иметь вид
rd_id     date
     2        2011-08-13 12:12:12
     3        2011-08-12 23:23:23

И MIN(r_id) не будет соответствовать MIN(date), т.е. ваш последней запрос вернет вам ошибочный результат.

Правильный вариант:


select r.order_id, t.* from report_data r join
(SELECT GROUP_CONCAT(DISTINCT order_id) AS order_ids, MIN(product_order_date) AS per_product_order_date, FORMAT(SUM(cost),2) AS sum_cost FROM report_data GROUP BY WEEK(product_order_date)) t on r.product_order_date=t.per_product_order_date;

Неактивен

 

#9 24.08.2011 16:42:54

kolombetam
Участник
Зарегистрирован: 26.04.2010
Сообщений: 7

Re: MySQL+GROUP BY WEEK()+GROUP_CONCAT()

Спасибо за помощь и правильное направление!

итоговый запрос получился следующий. #A#, #B#, #C#, #D#, #X#, #Y#, #Z# - произвольные названия полей. Всего в таблице 62 поля, по каждому из них пожет быть/не быть сортировка, группировка, конкатенация, сумма и т.п.

SELECT
[#E,]
...
[#F#]  
t.*
FROM report_data AS r
  JOIN (SELECT
          [GROUP_CONCAT(#X#) AS con_#X#,]
          [GROUP_CONCAT(#Y#) AS con_#Y#,]
          [FORMAT(SUM(#Z#),2) AS sum_#Z#,]
          []
          ...
          []
          GROUP_CONCAT(DISTINCT order_id [ORDER BY #A# DESC[,#B# DESC[...]]]) AS order_ids,
          SUBSTRING_INDEX(GROUP_CONCAT(rd_id [ORDER BY #A# DESC[,#B# DESC[...]]]), ",", 1) AS first_rd_id
        FROM report_data
        [GROUP BY #C#[,#D#[...]]]) AS t
    ON r.rd_id = t.first_rd_id
[ORDER BY sum_#A# DESC[,#B# DESC[...]]]

Отредактированно kolombetam (24.08.2011 17:20:43)

Неактивен

 

#10 24.08.2011 17:06:51

vasya
Архат
MySQL Authorized Developer
Откуда: Орел
Зарегистрирован: 07.03.2007
Сообщений: 5842

Re: MySQL+GROUP BY WEEK()+GROUP_CONCAT()

А зачем в вашем примере JOIN?

Неактивен

 

#11 24.08.2011 17:22:14

kolombetam
Участник
Зарегистрирован: 26.04.2010
Сообщений: 7

Re: MySQL+GROUP BY WEEK()+GROUP_CONCAT()

отредактировал сообщение выше. Выбраются еще некоторые значения без аггрегирующих функий

Неактивен

 

Board footer

Работает на PunBB
© Copyright 2002–2008 Rickard Andersson