2009-11-19 2 views
10

Я проектирую корзину покупок. Чтобы обойти проблему старых счетов-фактур, показывающих неточную цену после изменения цены продукта, я переместил поле цены из таблицы Product в таблицу ProductPrice, которая состоит из 3 полей, pid, даты и цены. pid и date образуют первичный ключ для таблицы. Вот пример того, что таблица выглядит следующим образом:SQL "GROUP BY" issue

pid date  price 
1  1/1/09 50 
1  2/1/09 55 
1  3/1/09 54 

Использование SELECT и GROUP BY найти самую последнюю цену каждого продукта, я придумал:

SELECT pid, price, max(date) FROM ProductPrice GROUP BY pid 

Дата и Pid вернулись, были точны. Я получил ровно 1 запись за каждый уникальный pid, и дата, которая сопровождала его, была последней датой для этого pid. Однако неожиданным было то, что цена вернулась. Он возвратил цену первой строки сопоставления PID, который в этом случае был 50.

После переделки моего заявления, я пришел с этим:

SELECT pp.pid, pp.price, pp.date FROM ProductPrice AS pp 
INNER JOIN (
    SELECT pid AS lastPid, max(date) AS lastDate FROM ProductPrice GROUP BY pid 
) AS m 
ON pp.pid = lastPid AND pp.date = lastDate 

Хотя переработано утверждение дает теперь правильную цену (54), кажется невероятным, что для такого простого звукового запроса потребуется выполнить внутреннее соединение. Мой вопрос в том, является ли мое второе заявление самым простым способом выполнить то, что мне нужно сделать? Или я чего-то не хватает? Заранее спасибо!

Джеймс

+0

Простой, хотя и на Postgresql: SELECT DISTINCT ON (pid) pid, date, price FROM ProductPrice ORDER BY pid, date DESC –

ответ

9

Причина, по которой вы получаете произвольную цену, заключается в том, что mysql не может знать, какие столбцы выбрать, если вы GROUP BY что-то. Он знает, что ему нужна a цена и a дата на pid и может получить последнюю дату по вашему запросу с max(date), но выберет для возврата наиболее выгодную для него цену - вы не указали aggregate function за это (ваш первый запрос недействителен SQL, фактически.)

Ваш второй запрос выглядит нормально, но вот короче альтернатива:

SELECT pid, price, date 
FROM ProductPrice p 
WHERE date = (SELECT MAX(date) FROM ProductPrice tmp WHERE tmp.pid = p.pid) 

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

+0

Другие СУБД будут жаловаться на то, что цена не включена как агрегация или как группировка. –

+0

Я должен согласиться, что это намного чище. – Zaid

1

Вы могли бы хотеть попробовать это:

SELECT pid, price, date FROM ProductPrice GROUP BY pid ORDER BY date DESC 

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

+0

Это не сработает. Он заказывает результирующую таблицу по нисходящей дате, а не исходную таблицу, поэтому последняя цена не будет выбрана. – Zaid

3

Я думаю, что вы нарушили схему базы данных.

Чтобы обойти проблему старых счетов-фактур с указанием недостоверной цены после цены товара получает изменился, я переехал поле цен из таблицы Product в таблицу ProductPrice, которая состоит из 3-х полей, PID, дата и цена. pid и date образуют первичный ключ для таблицы.

Как вы указали, вам необходимо сохранить историю изменений цен. Но вы можете сохранить текущую цену в таблице продуктов в дополнение к этой новой таблице. Это сделает вашу жизнь намного проще (и ваши запросы быстрее).

0

Вот еще -possibly inefficient- один:

SELECT pid, substring_index(group_concat(price order by date desc), ',', 1) , max(date) 
    FROM ProductPrice 
GROUP BY pid 
0

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

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

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

1

Вы не можете решить вашу проблему с предложением GROUP BY, потому что для каждой группы pid MySQL просто извлекает первый pid, максимальная дата и первая найденная цена (что вам не нужно).

Вы можете либо использовать подзапрос (который может быть неэффективным):

SELECT pid, date, price 
FROM ProductPrice p1 
WHERE date = (SELECT MAX(p2.date) 
       FROM ProductPrice p2 
       WHERE p1.pid = p2.pid) 

или вы можете просто присоединиться к таблице с собой:

SELECT p1.pid, p1.date, p1.price 
FROM  ProductPrice p1 
LEFT JOIN ProductPrice p2 ON p1.pid = p2.pid 
      AND p1.date < p2.date 
WHERE  p2.pid IS NULL 

Взгляните на this section из MySQL документации.

0

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

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