Estou trabalhando com um sistema de compra / fatura de alimentos no MS Access 2013 e estou tentando criar uma consulta SQL que retornará o preço de compra mais recente para cada item de comida individual.
Aqui está um diagrama das tabelas com as quais estou trabalhando:
Meu entendimento do SQL é muito básico e tentei a seguinte consulta (incorreta), na esperança de que ele retornasse apenas um registro por item (por causa do DISTINCT
operador) e que retornasse apenas a compra mais recente (desde que eu fiz isso) 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;
No entanto, a consulta acima simplesmente retorna todas as compras de alimentos (ou seja, vários registros para cada registro [Food items]
), com os resultados classificados por data. Alguém pode me explicar o que estou entendendo mal sobre o DISTINCT
operador? Ou seja, por que não está retornando apenas um registro para cada item [Food items]
?
E, mais importante: qual é a maneira mais simples de extrair os dados mais recentes de compra de alimentos para cada item alimentar, dada a estrutura da tabela mostrada acima ? Eu realmente não me importo tanto com eficiência quanto com simplicidade (o banco de dados com o qual estou trabalhando é bastante pequeno - levará anos até que esteja na faixa de dezenas de milhares de registros). Preocupo-me mais com o fato de a consulta ser compreensível para alguém com pouco conhecimento de SQL.
UPDATE: Tentei, ambas as respostas sugeridas abaixo, e nenhuma delas funciona (elas apenas lançam erros de sintaxe).
Com base nas sugestões abaixo, e lendo mais on-line, escrevi a nova consulta a seguir, usando a função agregada max()
e uma GROUP BY
cláusula:
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];
Mas ainda estou tendo o mesmo problema: ou seja, continuo vendo mais de um resultado para cada item alimentar. Alguém pode explicar por que essa consulta não está retornando apenas a compra mais recente para cada item de alimento?
ATUALIZAÇÃO 2 (RESOLVIDO!) :
Nenhuma das respostas abaixo deu muito certo, mas com base em algumas modificações pesadas da resposta de Vladimir abaixo , consegui criar as seguintes consultas, que parecem estar fornecendo os resultados corretos.
Primeiro, criei essa exibição e o nomeiei como "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
Em seguida, escrevi outra consulta para obter os campos necessários:
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;
Obrigado a todos vocês que dedicaram um tempo para me ajudar com isso!
DISTINCT
retorna linhas que são distintas em todas as colunas da linha, não em colunas únicas.[
e]
ID
colunas, para queID
aInvoices
tabela se torneInvoiceID
.DISTINCT
era por colunas únicas. Existe um operador análogo que selecionará apenas com base na exclusividade em uma única coluna? Além disso, obrigado pelas dicas sobre convenções de nomenclatura - sim, é muito chato ter que usar em[ ... ]
qualquer lugar ... E posso ver como a inclusão do nome da tabela na coluna ID aumentaria a legibilidade.Respostas:
O MS Access é bastante limitado.
Presumo que seja possível ter mais de uma fatura para a mesma data. Nesse caso, selecionarei uma fatura com o ID mais alto.
Inicialmente, encontraremos a Data máxima da fatura para cada item alimentar.
Como é possível que haja várias faturas para a data máxima encontrada, escolheremos uma fatura com o ID máximo por item
Com base na sintaxe do MS Access de junções aninhadas e usando este exemplo dos documentos:
Vamos tentar juntar tudo:
Agora, temos o ItemID e o ID da última fatura para esse item. Junte-se a tabelas originais para buscar outros detalhes (colunas).
Na prática, eu criaria uma visualização para a primeira consulta com uma única junção. Depois, criava uma segunda visualização que une a primeira visualização às tabelas, depois a terceira e assim por diante, para evitar as junções aninhadas ou minimizá-las. A consulta geral seria mais fácil de ler.
Edite para esclarecer o que quero dizer com base na sua solução final que você colocou na pergunta.
Uma última tentativa de transmitir minha mensagem.
Isto é o que você escreveu com base nas minhas sugestões acima:
Isto é o que eu quis dizer:
Você vê a diferença?
O
InvoicesMaxDate
retorno MAXInvoice Date
para cada umFood item ID
. Se houver duas faturas para a mesmaFood item ID
com o mesmo MAXInvoice Date
, devemos escolher uma fatura entre elas. Isso é feito agrupando porInvoicesMaxDate.ItemID, InvoicesMaxDate.MaxDate
. Não deve haver nenhum agrupamento porInvoices.[Invoice ID]
aqui, porque queremos pegar a factura com o ID máximo.Depois de salvar esta consulta como uma
LatestInvoices
visualização, ela será usada ainda mais conforme você escreveu corretamente (observe que a consulta final usaLatestInvoices.[Invoice ID]
eLatestInvoices.ItemID
, mas não usaLatestInvoices.MaxDate
):Quanto a, por que sua última consulta na pergunta retorna várias linhas por item:
Você está agrupando aqui por
[Food item ID]
e[Price per unit]
, para obter tantas linhas quanto houver combinações únicas dessas duas colunas.A consulta a seguir retornaria uma linha por
[Food item ID]
.Uma nota lateral, você realmente deve usar explícito em
INNER JOIN
vez de,
. Essa sintaxe tem 20 anos.fonte
"Syntax error (missing operator) in query expression"
a expressãoINNER JOIN Invoices AS I2 ON I2.ID = FPD2.[Invoice ID]
... Vou brincar mais com ela para ver se consigo fazê-la funcionar.(
e)
quando a consulta usa várias junções e mover aON
cláusula um pouco. Não tenho acesso para verificar, mas posso tentar adivinhar a sintaxe correta lendo os documentos ainda hoje.LatestInvoices
: a finalGROUP
deve serBY InvoicesMaxDate.ItemID, InvoicesMaxDate.MaxDate
apenas, semInvoices.[Invoice ID]
. NaSELECT
parte deve haverMAX(Invoices.[Invoice ID]) AS [Invoice ID]
. Este é o ponto inteiro. Inicialmente (na consulta interna),GROUP BY [Food item ID]
encontramos a Data máxima da fatura. Pode haver várias faturas com essa data; portanto, existe um segundoGROUP BY
para escolher a fatura com o ID máximo entre elas.ItemID
com a mesma data grande e tente as duas consultas.Uma consulta que simplesmente funciona imediatamente:
fonte
Eu poderia resolvê-lo com a seguinte consulta:
Como não tenho acesso, testei isso no SQL Server. Espero que isso funcione para você.
Editar / consulta adicional : para adicionar as outras colunas da tabela de itens alimentares, alterei a consulta. Fiz de uma maneira que realmente não gosto. Se tudo estiver bem para você, depende de seus dados e requisitos. Entrei na tabela FATURAS novamente usando a Data do pedido. Caso esta seja uma data que inclui o horário em que meu trabalho termina, esteja ciente disso. Não vejo outra maneira no seu cenário. Talvez exista uma solução melhor usando consulta recursiva ...?
Faça uma tentativa e deixe-me saber se funciona:
fonte
Item
,Price per unit
, etc)?Eu acredito que o abaixo deve funcionar.
Quanto ao motivo pelo qual sua consulta não está retornando os resultados que você deseja:
O maior problema que vejo é que você realmente não está fazendo nada para se juntar às suas mesas. A "junção" implícita que está presente simplesmente listando as duas na cláusula FROM está fornecendo um produto cartesiano. Basicamente, ele retornará todas as combinações possíveis no seu banco de dados para os campos que você está consultando.
Por exemplo, se as duas tabelas tivessem três registros cada, em vez de retornar a data mais recente, sua consulta retornaria algo como: 1,1 1,2 1,3 2,1 2,2 2,3 3,1 3,2 3 3
É muito importante que você declare explicitamente suas associações. Duas maneiras de fazer isso na sua consulta seriam:
OU
Consultas atualizadas, se elas ainda não funcionarem, tente remover os aliases e usar os nomes de colunas totalmente qualificados.
fonte
Concordo com as sugestões de Max sobre o seu modelo de dados. Implementá-las tornará seu SQL mais legível a longo prazo.
Com isso dito, DISTINCT exibirá linhas exclusivas. Portanto, para mostrar apenas os mais recentes, você deve limitar as colunas exibidas.
Tente algo como:
(Tradução: para cada item da loja, exiba a data da fatura mais recente.)
Você pode salvar isso como uma exibição e usá-lo em outra consulta, como faria em uma tabela. Assim, você pode fazer uma junção interna na fatura para o preço de compra e ingressar nas outras tabelas, se precisar desses detalhes.
(Teoricamente, você também pode fazer uma consulta aninhada, mas como solicitou simples, uma consulta salva é mais simples.)
ATUALIZAÇÃO com base na sua atualização:
Vou usar cláusulas WHERE em vez de JOINS porque não tenho o MS Access à mão. Você poderá usar a GUI para fazer as conexões entre as tabelas no MS Access com base nessas informações. (Forneça um SQLFiddle se você realmente precisar de ajuda com outras soluções de problemas.)
Etapa 1: Salve como uma VIEW (por exemplo, "MostRecentInvoice")
Etapa 2: use a visualização em uma segunda consulta
... e para responder à sua pergunta: A segunda consulta na atualização não funciona porque a coluna [Preço por unidade] está nas suas instruções SELECT e GROUP BY. Isso significa essencialmente que você está pedindo para ver TODOS os valores possíveis de [Preço por unidade], mesmo que o que você realmente deseja seja apenas um: o valor mais recente.
fonte
WHERE [Food purchase data].[Food item ID] = Invoices.ID
... Suponho que você quis dizer,WHERE [Food purchase data].[Invoice ID] = Invoices.[Invoice ID]
mas que ainda retorna várias datas por item de comida, em vez das mais recentes.