#mysql #group-by
#mysql #group-by
Вопрос:
Я запускаю следующий запрос, чтобы получить статистику для пользователя, на основе которой я им плачу.
SELECT hit_paylevel, sum(hit_uniques) as day_unique_hits
, (sum(hit_uniques)/1000)*hit_paylevel as day_earnings
, hit_date
FROM daily_hits
WHERE hit_user = 'xxx' AND hit_date >= '2011-05-01' AND hit_date < '2011-06-01'
GROUP BY hit_user
Рассматриваемая таблица выглядит следующим образом:
CREATE TABLE IF NOT EXISTS `daily_hits` (
`hit_itemid` varchar(255) NOT NULL,
`hit_mainid` int(11) NOT NULL,
`hit_user` int(11) NOT NULL,
`hit_date` date NOT NULL,
`hit_hits` int(11) NOT NULL DEFAULT '0',
`hit_uniques` int(11) NOT NULL,
`hit_embed` int(11) NOT NULL,
`hit_paylevel` int(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`hit_itemid`,`hit_date`),
KEY `hit_user` (`hit_user`),
KEY `hit_mainid` (`hit_mainid`,`hit_date`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Проблема в вычислении связана с hit_paylevel, который действует как множитель. По умолчанию используется значение one, другой вариант — 2 или 3, что по существу удваивает или утраивает прибыль за этот день.
Если я перебираю дни, значение daily day_earnings
верно, просто, когда я их группирую, все вычисляется как paylevel 1. Это происходит, если вначале пользователь был paylevel 1, а позже был повышен до более высокого уровня. если пользователь изначально имеет 2-й уровень оплаты, он также все вычисляет правильно.
Комментарии:
1. Это хороший пример того, почему мне не нравится способ обработки MySQL
GROUP BY
.hit_paylevel
Зависит от строки в вашей группе, и MySQL произвольно выбирает одну для использования.2. @Joe: на самом деле, это действительно проблема MySQL. PostgreSQL полностью отклонит запрос, жалуясь, что он не знает, какой hit_paylevel выбрать…
3. @Denis: Да, это была моя точка зрения. Мне не нравится, что MySQL считает это законным запросом.
4. @Joe, мне нравится, как MySQL обрабатывает group by, тебе просто нужно быть в курсе этого, чтобы тебя не укусили. Однако у этого есть свое применение.
Ответ №1:
Разве это не должно быть sum(hit_uniques * hit_paylevel) / 1000
?
Ответ №2:
Как сказал @Denis:
Измените запрос на
SELECT hit_paylevel, sum(hit_uniques) as day_unique_hits
, sum(hit_uniques * hit_paylevel) / 1000 as day_earnings
, hit_date
FROM daily_hits
WHERE hit_user = 'xxx' AND hit_date >= '2011-05-01' AND hit_date < '2011-06-01'
GROUP BY hit_user;
Почему это устраняет проблему
Делая hit_paylevel
вне суммы, сначала суммирует все hit_uniques
, а затем выбирает случайное hit_paylevel
значение для умножения на него.
Не то, что вы хотите. Если вы выполните оба столбца внутри суммы, MySQL соединит правильные hit_uniques
и hit_paylevels
.
Опасности group by
Это важная вещь, которую следует помнить в MySQL.
Это group by
предложение работает иначе, чем в других базах данных.
В MSSQL * (или Oracle, или PostgreSQL) вы бы получили сообщение об ошибке
неагрегатное выражение должно отображаться в предложении group by
Или слова на этот счет.
В вашем исходном запросе hit_paylevel
нет агрегированного значения (sum), и его также нет в group by
предложении, поэтому MySQL просто выбирает значение случайным образом.
Комментарии:
1. Попробовал это. Сумма выглядит намного ближе к значению, которым она должна быть, но она все еще немного отличается от числа, которое я должен иметь, если я вручную добавлю числа, просматривая результаты.
2. @Егор, если либо
hit_uniques
либоhit_paylevel
может быть null, то выражениеhit_uniques * hit_paylevel
принимает значение 0 (потому что сумма их не учитывает).3. ни один из них не выполняет. Они будут как минимум равны 1