Помогите правильно составить запрос

Blade

Новичок
Помогите правильно составить запрос

Предположим есть таблица в MySQL:
sklad_id, tovar_id, data, ostatok

В эту таблицу вносятся остатки только в том случае, если в конкретный день на конкретном складе по конкретному товару было движение.
Т.е. например остаток на "колбасу" на "первом складе" есть за 1 и 5 января, потому что за период с 2 по 4 колбасу на первый склад не привозили и не увозили, таким образом информация не дублируется.

И вот из это таблицы нужно получить остатки всех товаров по всем складам на определенное число. Т.е. нужно получить результат, сгруппированный по складу (skald_id) и товару (tovar_id) с максимальной датой (data), которая не больше, например 4 января (т.е. получить остаток на 4 января).

Я написал следующий запрос, ожидая получить ошибку:
SELECT max(data), sklad_id, tovar_id, ostatok
FROM ostatki_na_skladah
WHERE data<='2007-01-04'
GROUP BY sklad_id, tovar_id

...и он нормально отработал

Но насколько мне известно, в выбираемые поля запросов с группировками можно подставлять только те поля, по которым сделана группировка, статистические функции и константы. Вот и меня это и беспокоит. Мне может кто-то объяснить данный феномен или подсказать решение задачи без нарушений принципов построения запросов в реляционных БД?
 

Bitterman

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

Blade

Новичок
Нашел один из рабочих вариантов, который выглядит так:

SELECT o2.data, o2.sklad_id, o2.tovar_id, o2.ostatok
FROM (
SELECT MAX(data), sklad_id, tovar_id
FROM ostatki_na_skladah
WHERE data<='2007-01-04'
GROUP BY sklad_id, tovar_id
) AS o1
JOIN ostatki_na_skladah AS o2
ON o1.data=o2.data AND o1.skald=o2.sklad AND o1.tovar=o2.tovar

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

Mols

Новичок
А почему бы не обновлять остаток для связки склад-товар? Таким образом получили бы таблицу содержащую только актуальные остатки. А если надо историю отслеживать - так в отдельную таблицу её писать.
 

Mols

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

Тогда такой вопрос. Всегда ли добавляемая запись содержит в поле data - большую дату чем ранее добавленные ? если это так, то я бы добавил просто поле с автоинкрементом (id), и индекс по нему.
и тогда запрос был бы таким.

SELECT o2.data, o2.sklad_id, o2.tovar_id, o2.ostatok
FROM ostatki_na_skladah WHERE id IN(
SELECT MAX(id)
FROM ostatki_na_skladah
WHERE data<='2007-01-04'
GROUP BY sklad_id, tovar_id)

по идее должно нормально работать
 

Blade

Новичок
Автор оригинала: Bitterman
Blade
explain
Все достаточно просто...
Вложеный запрос возвращает таблицу из трех столбцов: дата, склад и товар - они группированы по паре склад товар, поэтому эти пары уникальны, а дата максимальная для отрезка времени, который задан в условии запроса (меньше либо равно). Вот эта таблица является аргументом для основного запроса, который объеденяет ее с оригинальной полной таблицей по трем полям: дата, склад, товар и таким образом я могу получить все, что мне нужно. Но запрос как-то долго отрабатывается... вложенный отрабатывает менее, чем за секунду. Мне кажется, что тормозит само объединение двух таблиц.

Автор оригинала: Mols
Тогда такой вопрос. Всегда ли добавляемая запись содержит в поле data - большую дату чем ранее добавленные ? если это так, то я бы добавил просто поле с автоинкрементом (id), и индекс по нему.
Идея классная, но дело в том, что в базе должна быть возможность делать что-то задним числом. Т.е. вполне возможна ситуцация, когда сначала проведут операцию за сегодняшний день, а потом нужно будет что-то сделать за какой-то из предыдущих.


Я вообще планировал изначально хранить остатки по каждому складу и товару на каждый день, даже если они не изменяются. Но... во-первых, это значительно увеличит размер таблицы; во-вторых, скажем, если движение товара (как в первом посте) последний раз было 1 января, а потом только пятого. Тогда при любом новом движении, мне нужно програмно смотреть когда же был записан последний остаток и дублировать его с 1 января на 2,3,4... мне почему-то кажется, что это глупая работа. Ведь если остаток не менялся, то зачем его дублировать? За то в таком случае остатки можно было бы очень легко получить за любой день, потому что на каждую дату по каждой паре склад-товар была бы запись. Тут всплывает еще один нюанс... например, было движение 1 января, а следующее будет только пятого (но мы этого не знаем еще), и хотим посмотреть остатки 4 числа. Т.к. после первого движений еще не было, остатки на 2,3 и 4 не продублировались, то мы вообще остатки на 4 не получим, поотму что не будет таких записей в базе. Получается что и просто просмотреть все равно нужно контролировать а есть ли записи по всем парам товар-склад на какое-то число, и если нет, то дублировать их с последней записи.

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

Bitterman

Новичок
Все достаточно просто...
Вложеный запрос возвращает таблицу из трех столбцов: дата, склад и товар - они группированы по паре склад товар, поэтому эти пары уникальны, а дата максимальная для отрезка времени, который задан в условии запроса (меньше либо равно). Вот эта таблица является аргументом для основного запроса, который объеденяет ее с оригинальной полной таблицей по трем полям: дата, склад, товар и таким образом я могу получить все, что мне нужно. Но запрос как-то долго отрабатывается... вложенный отрабатывает менее, чем за секунду. Мне кажется, что тормозит само объединение двух таблиц.
гы, это типа такой ручной explain, да?))))
Боюсь, он не подходит. Смотри мануал по MySQL.
 

Mols

Новичок
Для того, чтобы запрос который Вы написали работал быстро - нужен составной индекс... это тоже увеличит занимаемое место на диске, но будет работать быстро(на выборку). При внесении данных будет естественно занимать больше времени. Думаю по каким полям построить этот индекс Вы разберётесь... ну и в какой последовательности они должны идти эти поля в индексе тоже понятно. Если нет - почитайте про индексы, и научитесь пользоваться explain. Отличная вещь
 

Blade

Новичок
Автор оригинала: Bitterman
гы, это типа такой ручной explain, да?))))
Боюсь, он не подходит. Смотри мануал по MySQL.
Я подумал, что просишь меня "объяснить"))))))))


Пользуюсь PhpMyAdmin, про команду Explain не знал, точнее не знал что это и есть explain. В майадмине эта команда скрывается за [Анализ PHP]

Вот результат ее работы с запросом в моем посте за 26.09.07 12:50:
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 495
1 PRIMARY o2 ALL NULL NULL NULL NULL 109903 Using where
2 DERIVED ostatki_na_skladah ALL NULL NULL NULL NULL 109903 Using where; Using temporary; Using filesort
 
Сверху