Je travaille avec un système d'achat / de facturation d'aliments dans MS Access 2013 et j'essaie de créer une requête SQL qui retournera le prix d'achat le plus récent pour chaque aliment individuel.
Voici un schéma des tables avec lesquelles je travaille:
Ma compréhension de SQL est très basique, et j'ai essayé la requête (incorrecte) suivante, dans l'espoir qu'elle ne retournerait qu'un seul enregistrement par article (à cause de l' DISTINCT
opérateur) et qu'elle ne retournerait que l'achat le plus récent (puisque je l'ai fait 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;
Cependant, la requête ci-dessus renvoie simplement tous les achats de nourriture (c'est-à-dire plusieurs enregistrements pour chaque enregistrement [Food items]
), les résultats étant triés par date. Quelqu'un peut-il m'expliquer ce que je comprends mal de l' DISTINCT
opérateur? Autrement dit, pourquoi ne renvoie-t-il pas un seul enregistrement pour chaque élément dans [Food items]
?
Et plus précisément - quel est le moyen le plus simple pour moi de simplement extraire les données d'achat de nourriture les plus récentes pour chaque aliment, compte tenu de la structure du tableau ci-dessus ? Je ne me soucie pas vraiment de l'efficacité autant que de la simplicité (la base de données avec laquelle je travaille est plutôt petite - il faudra des années avant qu'elle ne soit même dans les dizaines de milliers d'enregistrements). Je m'inquiète davantage que la requête soit compréhensible pour quelqu'un qui a peu de connaissances en SQL.
MISE À JOUR: J'ai donc essayé, les deux réponses suggérées ci-dessous, et aucune d'entre elles ne fonctionne (elles ne font que lancer des erreurs de syntaxe).
Sur la base des suggestions ci-dessous et de la lecture en ligne, j'ai écrit la nouvelle requête suivante, en utilisant la fonction d'agrégation max()
et une GROUP BY
clause:
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];
Mais j'ai toujours le même problème: c'est-à-dire que je vois toujours plus d'un résultat pour chaque aliment. Quelqu'un peut-il expliquer pourquoi cette requête ne renvoie pas seulement l'achat le plus récent pour chaque aliment?
MISE À JOUR 2 (RÉSOLU!) :
Aucune des réponses ci-dessous n'a tout à fait fonctionné mais sur la base d'une modification importante de la réponse de Vladimir ci - dessous , j'ai pu créer les requêtes suivantes, qui semblent donner les bons résultats.
Tout d'abord, j'ai créé cette vue et l'ai nommée "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
Ensuite, j'ai écrit une autre requête pour insérer les champs dont j'avais besoin:
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;
Merci à tous ceux qui ont pris le temps de m'aider!
DISTINCT
renvoie des lignes distinctes sur toutes les colonnes de la ligne, et non sur des colonnes uniques.[
et]
ID
colonnes, doncID
dans laInvoices
table devientInvoiceID
.DISTINCT
c'était par colonnes uniques. Existe-t-il un opérateur analogue qui sélectionnera uniquement en fonction de l'unicité dans une seule colonne? Aussi, merci pour les conseils sur les conventions de dénomination - oui, c'est très ennuyeux de devoir l'utiliser[ ... ]
partout ... Et je peux voir comment l'inclusion du nom de la table dans la colonne ID augmenterait la lisibilité.Réponses:
MS Access est plutôt limité.
Je suppose qu'il est possible d'avoir plusieurs factures pour la même date. Dans ce cas, je choisirai une facture avec l'ID le plus élevé.
Au début, nous trouverons la date de facturation maximale pour chaque aliment.
Puisqu'il est possible qu'il y ait plusieurs factures pour la date max trouvée, nous choisirons une facture avec l'ID max par article
Basé sur la syntaxe MS Access des jointures imbriquées et en utilisant cet exemple à partir des documents:
Essayons de le mettre ensemble:
Nous avons maintenant à la fois ItemID et ID de la dernière facture pour cet article. Joignez-le aux tableaux d'origine pour récupérer d'autres détails (colonnes).
En pratique, je créerais une vue pour la première requête avec une seule jointure. Ensuite, je créais une deuxième vue qui joint la première vue aux tables, puis la troisième vue et ainsi de suite, pour éviter les jointures imbriquées ou les minimiser. La requête globale serait plus facile à lire.
Modifiez pour clarifier ce que je veux dire en fonction de votre solution finale que vous avez mise dans la question.
Une dernière tentative pour transmettre mon message.
Voici ce que vous avez écrit sur la base de mes suggestions ci-dessus:
Voici ce que je voulais dire:
Voyez-vous la différence?
Le
InvoicesMaxDate
retourne MAXInvoice Date
pour chacunFood item ID
. S'il y a deux factures pour le mêmeFood item ID
avec le même MAX,Invoice Date
nous devons choisir une facture parmi elles. Cela se fait en regroupant parInvoicesMaxDate.ItemID, InvoicesMaxDate.MaxDate
. Il ne devrait pas y avoir de regroupementInvoices.[Invoice ID]
ici, car nous voulons sélectionner la facture avec l'ID maximum.Une fois que vous avez enregistré cette requête en tant que
LatestInvoices
vue, elle est utilisée plus loin comme vous l'avez correctement écrit (notez que la requête finale utiliseLatestInvoices.[Invoice ID]
etLatestInvoices.ItemID
, mais n'utilise pasLatestInvoices.MaxDate
):Quant à, pourquoi votre dernière requête dans la question renvoie plusieurs lignes par article:
Vous regroupez ici par
[Food item ID]
et[Price per unit]
, vous obtiendrez donc autant de lignes qu'il y a de combinaisons uniques de ces deux colonnes.La requête suivante retournerait une ligne par
[Food item ID]
.Une remarque, vous devriez vraiment utiliser explicite
INNER JOIN
au lieu de,
. Cette syntaxe a 20 ans.la source
"Syntax error (missing operator) in query expression"
l'expressionINNER JOIN Invoices AS I2 ON I2.ID = FPD2.[Invoice ID]
... Je vais jouer avec elle plus pour voir si je peux la faire fonctionner.(
et)
lorsque la requête utilise plusieurs jointures et de déplacerON
un peu la clause. Je n'ai pas accès à vérifier, mais je peux essayer de deviner la syntaxe correcte en lisant les documents plus tard dans la journée.LatestInvoices
: la finaleGROUP
ne devrait êtreBY InvoicesMaxDate.ItemID, InvoicesMaxDate.MaxDate
que, sansInvoices.[Invoice ID]
. Dans laSELECT
partie il devrait y en avoirMAX(Invoices.[Invoice ID]) AS [Invoice ID]
. C'est tout l'intérêt. Au début (dans la requête interne), nousGROUP BY [Food item ID]
trouvons la date de facturation maximale. Il peut y avoir plusieurs factures avec cette date, il y a donc une secondeGROUP BY
pour choisir la facture avec un ID maximum parmi elles.ItemID
avec la même grande date et essayez les deux requêtes.Une requête qui fonctionne juste hors de la boîte:
la source
Je pourrais le résoudre avec la requête suivante:
Parce que je n'ai pas d'accès, j'ai testé cela sur SQL Server. J'espère que cela fonctionnera pour vous.
Modifier / Requête supplémentaire : Afin d'ajouter les autres colonnes du tableau des aliments, j'ai modifié la requête. Je l'ai fait d'une manière que je n'aime pas vraiment. Si cela vous convient, cela dépend de vos données et de vos besoins. J'ai de nouveau rejoint la table INVOICES en utilisant la date de commande. Dans le cas où il s'agit d'une date, y compris l'heure de mon travail, veuillez en être conscient. Je ne vois pas d'autre moyen dans votre scénario. Peut-être existe-t-il une meilleure solution en utilisant la requête récursive ...?
Veuillez essayer et faites-moi savoir si cela fonctionne:
la source
Item
,Price per unit
etc.)?Je crois que ce qui suit devrait fonctionner.
Quant à savoir pourquoi votre requête ne renvoie pas les résultats que vous souhaitez:
Le plus gros problème que je vois est que vous ne faites rien pour rejoindre vos tables. La "jointure" implicite qui est présente en listant simplement les deux dans votre clause FROM vous donne un produit cartésien. Fondamentalement, il renverra toutes les combinaisons possibles dans votre base de données pour les champs que vous interrogez.
Par exemple, si les deux tables ont chacune 3 enregistrements au lieu de renvoyer la date la plus récente, votre requête renvoie quelque chose comme: 1,1 1,2 1,3 2,1 2,2 2,3 3,1 3,2 3 , 3
Il est très important que vous déclariez explicitement vos jointures. Vous pouvez le faire de deux manières dans votre requête:
OU
Requêtes mises à jour, si celles-ci ne fonctionnent toujours pas, essayez de supprimer les alias et d'utiliser les noms de colonne complets.
la source
Je suis d'accord avec les suggestions de Max concernant votre modèle de données. Leur mise en œuvre rendra votre SQL plus lisible à long terme.
Cela dit, DISTINCT affichera des lignes uniques. Donc, pour ne montrer que les plus récents, vous devez limiter les colonnes affichées.
Essayez quelque chose comme:
(Traduction: pour chaque article du magasin, affichez sa date de facture la plus récente.)
Vous pouvez l'enregistrer en tant que vue et l'utiliser dans une autre requête comme vous le feriez pour une table. Vous pouvez donc faire une jointure interne sur la facture pour le prix d'achat et les jointures sur les autres tables si vous avez besoin de ces détails.
(Théoriquement, vous pouvez également effectuer une requête imbriquée, mais comme vous avez demandé une requête simple, une requête enregistrée est plus simple.)
MISE À JOUR en fonction de votre mise à jour:
Je vais utiliser des clauses WHERE au lieu de JOINS car je n'ai pas MS Access à portée de main. Vous devez pouvoir utiliser l'interface graphique pour établir les connexions entre les tables dans MS Access en fonction de ces informations. (Veuillez fournir un SQLFiddle si vous avez vraiment besoin d'aide pour un dépannage supplémentaire.)
Étape 1: enregistrer ceci en tant que VUE (par exemple, "MostRecentInvoice")
Étape 2: utiliser la vue dans une 2e requête
... et pour répondre à votre question: La 2e requête de la mise à jour ne fonctionne pas car la colonne [Prix par unité] se trouve dans vos instructions SELECT et GROUP BY. Cela signifie essentiellement que vous demandez à voir TOUTES les valeurs possibles de [Prix par unité] même si ce que vous voulez vraiment n'est qu'une seule: la valeur la plus récente.
la source
WHERE [Food purchase data].[Food item ID] = Invoices.ID
... Je suppose que vous vouliez direWHERE [Food purchase data].[Invoice ID] = Invoices.[Invoice ID]
mais cela renvoie toujours plusieurs dates par aliment au lieu de la plus récente.