Estoy trabajando con un sistema de compra / facturación de alimentos en MS Access 2013 y estoy tratando de crear una consulta SQL que devolverá el precio de compra más reciente para cada artículo alimenticio individual.
Aquí hay un diagrama de las tablas con las que estoy trabajando:
Mi comprensión de SQL es muy básica, e intenté la siguiente consulta (incorrecta), con la esperanza de que solo devolvería un registro por artículo (debido al DISTINCT
operador) y que solo devolvería la compra más reciente (ya que lo hice 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;
Sin embargo, la consulta anterior simplemente devuelve todas las compras de alimentos (es decir, múltiples registros para cada registro [Food items]
), con los resultados ordenados por fecha. ¿Alguien puede explicarme lo que estoy entendiendo mal sobre el DISTINCT
operador? Es decir, ¿por qué no devuelve solo un registro para cada elemento [Food items]
?
Y más importante: ¿cuál es la forma más sencilla para mí de obtener los datos de compra de alimentos más recientes para cada artículo de alimentos individual, dada la estructura de la tabla que se muestra arriba ? Realmente no me importa tanto la eficiencia como la simplicidad (la base de datos con la que estoy trabajando es bastante pequeña, pasarán años antes de que esté incluso en el rango de decenas de miles de registros). Me importa más que la consulta sea comprensible para alguien con poco conocimiento de SQL.
ACTUALIZACIÓN: así que lo intenté, las dos respuestas sugeridas a continuación, y ninguna de ellas funciona (simplemente arrojan errores de sintaxis).
Basado en las sugerencias a continuación, y leyendo más en línea, escribí la siguiente nueva consulta, usando la función de agregado max()
y una 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];
Pero sigo teniendo el mismo problema: es decir, sigo viendo más de un resultado para cada alimento. ¿Alguien puede explicar por qué esta consulta no solo devuelve la compra más reciente de cada alimento?
ACTUALIZACIÓN 2 (¡RESUELTO!) :
Ninguna de las respuestas a continuación funcionó, pero en base a algunas modificaciones importantes de la respuesta de Vladimir a continuación , pude crear las siguientes consultas, que parecen estar dando los resultados correctos.
Primero, creé esta vista y la llamé "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
Luego escribí otra consulta para obtener los campos que necesitaba:
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;
¡Gracias a todos los que se tomaron el tiempo para ayudarme con esto!
DISTINCT
devuelve filas que son distintas en todas las columnas de la fila, no en columnas individuales.[
y]
ID
columnas, por lo queID
en laInvoices
tabla se convierteInvoiceID
.DISTINCT
era por columnas individuales. ¿Existe un operador análogo que seleccione solo en función de la unicidad en una sola columna? Además, gracias por los consejos sobre convenciones de nomenclatura: sí, es muy molesto tener que usarlo en[ ... ]
todas partes ... Y puedo ver cómo incluir el nombre de la tabla en la columna ID aumentaría la legibilidad.Respuestas:
MS Access es bastante limitado.
Supongo que es posible tener más de una factura para la misma fecha. En este caso, elegiré una factura con la ID más alta.
Al principio, encontraremos la Fecha máxima de factura para cada artículo alimenticio.
Como es posible que haya varias facturas para la fecha máxima encontrada, elegiremos una factura con la ID máxima por artículo
Basado en la sintaxis de MS Access de uniones anidadas y usando este ejemplo de los documentos:
Tratemos de armarlo:
Ahora tenemos el ItemID y el ID de la última factura para ese artículo. Unir esto a las tablas originales para obtener otros detalles (columnas).
En la práctica, crearía una vista para la primera consulta con una sola combinación. Luego crearía una segunda vista que une la primera vista con las tablas, luego la tercera vista y así sucesivamente, para evitar las uniones anidadas o minimizarlas. La consulta general sería más fácil de leer.
Edite para aclarar lo que quiero decir en función de su solución final que haya puesto en la pregunta.
Un último intento de transmitir mi mensaje.
Esto es lo que escribiste en base a mis sugerencias anteriores:
Esto es lo que quise decir:
¿Ves la diferencia?
Los
InvoicesMaxDate
retornos MAXInvoice Date
para cada unoFood item ID
. Si hay dos facturas para el mismoFood item ID
con el mismo MAXInvoice Date
, deberíamos elegir una factura entre ellas. Esto se hace agrupando porInvoicesMaxDate.ItemID, InvoicesMaxDate.MaxDate
. No debe haber ninguna agrupación porInvoices.[Invoice ID]
aquí, porque queremos recoger la factura con el ID máxima.Una vez que tiene esta consulta guardada como una
LatestInvoices
vista, se usa más a medida que escribió correctamente (tenga en cuenta que la consulta final usaLatestInvoices.[Invoice ID]
yLatestInvoices.ItemID
, pero no usaLatestInvoices.MaxDate
):En cuanto a por qué su última consulta en la pregunta devuelve varias filas por elemento:
Está agrupando aquí por
[Food item ID]
y[Price per unit]
, por lo que obtendrá tantas filas como combinaciones únicas de estas dos columnas.La siguiente consulta devolvería una fila por
[Food item ID]
.Una nota al margen, realmente debería usar explícito en
INNER JOIN
lugar de,
. Esa sintaxis tiene 20 años.fuente
"Syntax error (missing operator) in query expression"
la expresiónINNER JOIN Invoices AS I2 ON I2.ID = FPD2.[Invoice ID]
... Jugaré un poco más para ver si puedo hacer que funcione.(
y)
cuando la consulta usa varias combinaciones y mover laON
cláusula un poco. No tengo acceso para verificar, pero puedo intentar adivinar la sintaxis correcta leyendo los documentos más tarde hoy.LatestInvoices
: la finalGROUP
debe serBY InvoicesMaxDate.ItemID, InvoicesMaxDate.MaxDate
solo, sinInvoices.[Invoice ID]
. En laSELECT
parte debería haberMAX(Invoices.[Invoice ID]) AS [Invoice ID]
. Este es el punto. Al principio (en la consulta interna),GROUP BY [Food item ID]
encontramos la fecha máxima de facturación. Puede haber varias facturas con esta fecha, por lo que hay un segundoGROUP BY
para elegir la factura con la ID máxima entre ellas.ItemID
con la misma fecha grande e intente ambas consultas.Una consulta que simplemente sale de la caja:
fuente
Podría resolverlo con la siguiente consulta:
Como no tengo acceso, probé esto en SQL Server. Espero que esto funcione para tí.
Editar / Consulta adicional : para agregar las otras columnas de la tabla de artículos alimenticios, cambié la consulta. Lo hice de una manera que realmente no me gusta. Si está bien para usted, depende de sus datos y requisitos. Me uní a la tabla FACTURAS nuevamente usando la Fecha del pedido. En caso de que esta sea una fecha que incluya el tiempo de mi trabajo, tenga en cuenta eso. No veo otra forma en tu escenario. ¿Quizás haya una mejor solución usando la consulta recursiva ...?
Pruébalo y avísame si funciona:
fuente
Item
,Price per unit
, etc)?Creo que lo siguiente debería funcionar.
En cuanto a por qué su consulta no devuelve los resultados que desea:
El mayor problema que veo es que realmente no estás haciendo nada para unirte a tus mesas. La "unión" implícita que está presente simplemente enumerando ambas en su cláusula FROM le está dando un producto cartesiano. Básicamente, devolverá todas las combinaciones posibles en su base de datos para los campos que está consultando.
Por ejemplo, si las dos tablas tenían 3 registros cada una en lugar de devolver la fecha más reciente, su consulta devolvería algo como: 1,1 1,2 1,3 2,1 2,2 2,3 3,1 3,2 3 3
Es muy importante que declares explícitamente tus uniones. Las dos formas en que podría hacerlo en su consulta serían:
O
Consultas actualizadas, si todavía no funcionan, intente eliminar los alias y utilice los nombres de columna completos.
fuente
Estoy de acuerdo con las sugerencias de Max sobre su modelo de datos. Implementarlos hará que su SQL sea más legible a largo plazo.
Dicho esto, DISTINCT mostrará filas únicas. Por lo tanto, para mostrar solo los más recientes, debe limitar las columnas que se muestran.
Intenta algo como:
(Traducción: para cada artículo en la tienda, muestre su fecha de factura más reciente).
Puede guardar esto como una vista y usarlo en otra consulta como lo haría con una tabla. Por lo tanto, puede hacer una unión interna en la factura por el precio de compra y unirse en las otras tablas si necesita esos detalles.
(Teóricamente, también podría hacer una consulta anidada, pero dado que solicitó simple, una consulta guardada es más simple).
ACTUALIZACIÓN basada en su actualización:
Voy a usar cláusulas WHERE en lugar de JOINS porque no tengo MS Access a mano. Debería poder usar la GUI para hacer las conexiones entre las tablas en MS Access en función de esta información. (Proporcione un SQLFiddle si realmente necesita ayuda con la resolución de problemas).
Paso 1: guarde esto como una VISTA (por ejemplo, "MostRecentInvoice")
Paso 2: use la vista en una segunda consulta
... y para responder a su pregunta: la segunda consulta en la actualización no funciona porque la columna [Precio por unidad] está en sus declaraciones SELECT y GROUP BY. Esto significa esencialmente que está pidiendo ver TODOS los valores posibles de [Precio por unidad] aunque lo que realmente desea es solo uno: el valor más reciente.
fuente
WHERE [Food purchase data].[Food item ID] = Invoices.ID
... Supongo que quiso decir,WHERE [Food purchase data].[Invoice ID] = Invoices.[Invoice ID]
pero aún así devuelve varias fechas por artículo de comida en lugar de solo la más reciente.