Я работаю с системой закупок и выставления счетов за продукты в MS Access 2013 и пытаюсь создать SQL-запрос, который будет возвращать самую последнюю цену покупки для каждого отдельного продукта.
Вот схема таблиц, с которыми я работаю:
Мое понимание SQL очень простое, и я попробовал следующий (неправильный) запрос, в надежде, что он вернет только одну запись на элемент (из-за DISTINCT
оператора) и что он вернет только самую последнюю покупку (так как я сделал ORDER BY [Invoice Date] DESC
)
SELECT DISTINCT ([Food items].Item),
[Food items].Item, [Food purchase data].[Price per unit], [Food purchase data].[Purchase unit], Invoices.[Invoice Date]
FROM Invoices
INNER JOIN ([Food items]
INNER JOIN [Food purchase data]
ON [Food items].ID = [Food purchase data].[Food item ID])
ON Invoices.ID = [Food purchase data].[Invoice ID]
ORDER BY Invoices.[Invoice Date] DESC;
Однако приведенный выше запрос просто возвращает все покупки продуктов питания (т.е. несколько записей для каждой записи в [Food items]
), а результаты сортируются по убыванию. Может кто-нибудь объяснить мне, что я неправильно понимаю в отношении DISTINCT
оператора? То есть, почему он не возвращает только одну запись для каждого элемента в [Food items]
?
И что еще важнее - какой самый простой способ для меня - просто собрать самые последние данные о покупке продуктов питания для каждого отдельного продукта, учитывая структуру таблицы, показанную выше ? На самом деле мне важна не столько простота, сколько эффективность (база данных, с которой я работаю, довольно мала - пройдут годы, прежде чем она достигнет десятков тысяч записей). Меня больше волнует вопрос, понятный для человека, мало знакомого с SQL.
ОБНОВЛЕНИЕ: Итак, я попытался, оба из предложенных ниже ответов, и ни один из них не работает (они просто выдают синтаксические ошибки).
Основываясь на приведенных ниже предложениях и дальнейшем чтении в Интернете, я написал следующий новый запрос, используя функцию агрегирования max()
и GROUP BY
предложение:
SELECT [Food purchase data].[Food item ID], [Food purchase data].[Price per unit], max(Invoices.[Invoice Date]) AS MostRecentInvoiceDate
FROM [Food purchase data], Invoices
GROUP BY [Food purchase data].[Food item ID], [Food purchase data].[Price per unit];
Но у меня все еще есть та же проблема: то есть, я все еще вижу более одного результата для каждого продукта. Кто-нибудь может объяснить, почему этот запрос не только возвращает самую последнюю покупку для каждого продукта питания?
ОБНОВЛЕНИЕ 2 (решено!) :
Ни один из приведенных ниже ответов не был полностью проработан, но, основываясь на некоторой серьезной модификации ответа Владимира ниже , я смог создать следующие запросы, которые, по-видимому, дают правильные результаты.
Сначала я создал это представление и назвал его «LatestInvoices»:
SELECT InvoicesMaxDate.ItemID, InvoicesMaxDate.MaxDate, InvoicesMaxDate.MaxID
FROM [Food purchase data], Invoices, (SELECT [Food purchase data].[Food item ID] AS ItemID, MAX(Invoices.[Invoice Date]) AS MaxDate, MAX(Invoices.[Invoice ID]) AS MaxID
FROM [Food purchase data], Invoices
WHERE Invoices.[Invoice ID] = [Food purchase data].[Invoice ID]
GROUP BY [Food purchase data].[Food item ID]
) AS InvoicesMaxDate
WHERE InvoicesMaxDate.MaxID = [Food purchase data].[Invoice ID] AND
InvoicesMaxDate.ItemID = [Food purchase data].[Food item ID] AND
InvoicesMaxDate.MaxDate = Invoices.[Invoice Date]
GROUP BY InvoicesMaxDate.ItemID, InvoicesMaxDate.MaxDate, InvoicesMaxDate.MaxID
Затем я написал еще один запрос, чтобы получить необходимые поля:
SELECT [Food items].ID AS FoodItemID, [Food items].Item AS FoodItem, [Food purchase data].[Price], [Food purchase data].[Price per unit], [Food purchase data].[Purchase unit], LatestInvoices.MaxDate as InvoiceDate
FROM [Food items], [Food purchase data], LatestInvoices
WHERE LatestInvoices.[MaxID] = [Food purchase data].[Invoice ID] AND
LatestInvoices.ItemID = [Food purchase data].[Food item ID] AND
LatestInvoices.ItemID = [Food items].ID
ORDER BY [Food items].Item;
Спасибо всем, кто нашел время, чтобы помочь мне с этим!
DISTINCT
возвращает строки, которые различаются по всем столбцам в строке, а не по одному столбцу.[
и]
ID
столбцах, поэтомуID
вInvoices
таблице становитсяInvoiceID
.DISTINCT
были отдельные столбцы. Есть ли аналогичный оператор, который будет выбирать только на основе уникальности в одном столбце? Кроме того, спасибо за советы по соглашениям об именах - да, это очень раздражает, когда приходится использовать[ ... ]
везде ... И я вижу, как включение имени таблицы в столбец ID может улучшить читаемость.Ответы:
MS Access довольно ограничен.
Я предполагаю, что возможно иметь более одного счета на одну и ту же дату. В этом случае я выберу счет с самым высоким ID.
Сначала мы найдем максимальную дату выставления счета для каждого предмета питания.
Поскольку возможно, что для найденной максимальной даты существует несколько счетов, мы выберем один счет с максимальным идентификатором для каждой позиции.
На основе синтаксиса MS Access вложенных объединений и использования этого примера из документов:
Давайте попробуем собрать это вместе:
Теперь у нас есть ItemID и ID последнего Счета для этого Предмета. Присоединитесь к исходным таблицам, чтобы получить другие подробности (столбцы).
На практике я бы создал представление для первого запроса с помощью одного соединения. Затем я создал бы второе представление, которое объединяет первое представление с таблицами, затем третье представление и т. Д., Чтобы избежать вложенных объединений или минимизировать их. Общий запрос будет легче читать.
Изменить, чтобы уточнить, что я имею в виду, исходя из вашего окончательного решения, которое вы поставили в вопросе.
Последняя попытка передать мое сообщение.
Это то, что вы написали на основе моих предложений выше:
Вот что я имел в виду:
Вы видите разницу?
InvoicesMaxDate
Возвращает MAXInvoice Date
для каждогоFood item ID
. Если есть два счета для одногоFood item ID
и того же MAX,Invoice Date
мы должны выбрать один счет из них. Это делается путем группировкиInvoicesMaxDate.ItemID, InvoicesMaxDate.MaxDate
. Здесь не должно быть никакой группировкиInvoices.[Invoice ID]
, потому что мы хотим выбрать счет с максимальным идентификатором.Как только вы сохранили этот запрос в виде
LatestInvoices
представления, он будет использован в дальнейшем, как вы правильно написали (обратите внимание, что последний запрос используетLatestInvoices.[Invoice ID]
иLatestInvoices.ItemID
, но не используетLatestInvoices.MaxDate
):Что касается того, почему ваш последний запрос в вопросе возвращает несколько строк на элемент:
Здесь вы группируете по
[Food item ID]
и[Price per unit]
, поэтому вы получите столько строк, сколько есть уникальных комбинаций этих двух столбцов.Следующий запрос будет возвращать одну строку на
[Food item ID]
.Примечание, вы действительно должны использовать явное
INNER JOIN
вместо,
. Этому синтаксису 20 лет.источник
"Syntax error (missing operator) in query expression"
выражениеINNER JOIN Invoices AS I2 ON I2.ID = FPD2.[Invoice ID]
... Я поэкспериментирую с ним, чтобы узнать, смогу ли я заставить его работать.(
а)
когда запрос использует несколько объединений иON
немного перемещать предложение. У меня нет доступа для проверки, но я могу попытаться угадать правильный синтаксис, прочитав документы сегодня.LatestInvoices
: финалGROUP
должен бытьBY InvoicesMaxDate.ItemID, InvoicesMaxDate.MaxDate
только безInvoices.[Invoice ID]
. ВSELECT
части должно бытьMAX(Invoices.[Invoice ID]) AS [Invoice ID]
. Это весь смысл. Сначала (во внутреннем запросе) мыGROUP BY [Food item ID]
и находим максимальную дату счета. С этой датой может быть несколько счетов, поэтому есть секунда,GROUP BY
чтобы выбрать счет с максимальным идентификатором среди них.ItemID
и той же большой даты и попробуйте оба запроса.Запрос, который просто работает из коробки:
источник
Я мог бы решить это с помощью следующего запроса:
Поскольку у меня нет Access, я проверил это на SQL Server. Я надеюсь, что это будет работать для вас.
Редактировать / Дополнительный запрос : чтобы добавить другие столбцы таблицы продуктов питания, я изменил запрос. Я сделал это способом, который мне не очень нравится. Если это нормально для вас, зависит от ваших данных и требований. Я снова присоединился к таблице счетов-фактур, используя дату заказа. В случае, если это дата, включая время моей работы, пожалуйста, помните об этом. Я не вижу другого пути в вашем сценарии. Может быть, есть лучшее решение, использующее рекурсивный запрос ...?
Пожалуйста, попробуйте и дайте мне знать, если это работает:
источник
Item
,Price per unit
и т. Д.)?Я считаю, что ниже должно работать.
Что касается того, почему ваш запрос не возвращает результаты, которые вам нравятся:
Самая большая проблема, которую я вижу, это то, что вы ничего не делаете, чтобы присоединиться к вашим столам. Неявное «соединение», которое присутствует при простом перечислении обоих в предложении FROM, дает вам декартово произведение. По сути, он вернет все возможные комбинации в вашей базе данных для полей, которые вы запрашиваете.
Например, если в двух таблицах было по 3 записи, а не возвращалась самая последняя дата, ваш запрос вернул бы что-то вроде: 1,1 1,2 1,3 2,1 2,2 2,3 3,1 3,2 3 , 3
Очень важно, чтобы вы явно объявили свои объединения. В вашем запросе вы можете сделать это двумя способами:
ИЛИ
Обновленные запросы, если они все еще не работают, попробуйте удалить псевдонимы и использовать полные имена столбцов.
источник
Я согласен с предложениями Макса о вашей модели данных. Реализация этого сделает ваш SQL более читабельным в долгосрочной перспективе.
С учетом сказанного, DISTINCT будет отображать уникальные строки. Таким образом, чтобы показывать только самые последние, вы должны ограничить отображаемые столбцы.
Попробуйте что-то вроде:
(Перевод: для каждого товара в магазине отображается его самая последняя дата выставления счета.)
Вы можете сохранить это как представление и использовать его в другом запросе, как в таблице. Таким образом, вы можете выполнить внутреннее объединение в счете на закупочную цену и в других таблицах, если вам нужны эти данные.
(Теоретически, вы также можете сделать вложенный запрос, но так как вы запросили простой, сохраненный запрос проще.)
ОБНОВЛЕНИЕ на основе вашего обновления:
Я собираюсь использовать предложения WHERE вместо JOINS, потому что у меня нет под рукой MS Access. Вы должны быть в состоянии использовать графический интерфейс для создания соединений между таблицами в MS Access на основе этой информации. (Пожалуйста, предоставьте SQLFiddle, если вам действительно нужна помощь в дальнейшем устранении неполадок.)
Шаг 1. Сохраните это как ВИД (например, «MostRecentInvoice»)
Шаг 2: Используйте представление во втором запросе
... и ответить на ваш вопрос: 2-й запрос в обновлении не работает, поскольку столбец [Цена за единицу] находится в ваших операторах SELECT и GROUP BY. По сути, это означает, что вы просите просмотреть ВСЕ возможные значения [Цена за единицу], хотя на самом деле вам нужно только одно: самое последнее значение.
источник
WHERE [Food purchase data].[Food item ID] = Invoices.ID
... Я предполагаю, что вы имели в виду,WHERE [Food purchase data].[Invoice ID] = Invoices.[Invoice ID]
но она по-прежнему возвращает несколько дат для каждого продукта, а не только самые последние.