2009-12-17 4 views
1

J'ai des informations sur la quantité en stock dans ma base de données.
1 table, "stock", détient le productid (sku) avec la quantité et le nom de fichier d'où il vient.Sélectionner l'entrée la plus récente d'une table MySQL jointe

L'autre table, "stockfile", contient tous les noms de fichiers traités ainsi que les dates.

Maintenant, je dois obtenir tous les produits avec leurs dernières valeurs en stock.

Cela me donne tous les produits plusieurs fois avec toute leur quantité d'actions (résultat 300,000 records)

SELECT stock.stockid, stock.sku, stock.quantity, stockfile.filename, stockfile.date
DE stock
INNER JOIN stockfile ON stock.stockfileid = stockfile.stockfileid
COMMANDER PAR stock. sku ASC

Je l'ai déjà essayé ceci:

SELECT * FROM stock
INNER JOIN stockfile SUR stock.stockfileid = stockfile.stockfileid
GROUP BY sku
AYANT stockfile.date = MAX (stockfile.date)
COMMANDER PAR stock.sku ASC

Mais ça n'a pas

SHOW CREATE TABLE stock:

CREATE TABLE stock (
stockid bigint (20) NOT AUTO_INCREMENT NULL,
sku char (25) NOT NULL,
quantity int (5) NOT NULL ,
creationdate datetime NOT NULL,
stockfileid smallint (5) non signé NOT NULL,
touchdate datetime NOT NULL,
PRIMARY KEY (stockid)
) MOTEUR = MyISAM AUTO_INCREMENT = 315169 DEFAULT CHARSET = latin1

SHOW CREATE TABLE stockfile:

CREATE TABLE stockfile (
stockfileid smallint (5) non signé NOT AUTO_INCREMENT NULL,
filename varchar (25) NOT NULL,
creationdate datetime NULL DEFAULT,
touchdate datetime NULL DEFAULT,
date datetime NULL DEFAULT,
begindate datetime DEFAUT NULL,
enddate date/heure DEFAUT NULL,
PRIMARY KEY (stockfileid)
) MOTEUR = MyISAM AUTO_INCREMENT = 265 DÉFAUT charset = latin1

+0

S'il vous plaît modifier votre question avec la sortie de «SHOW CREATE TABLE stock» et «SHOW CREATE TABLE stockfile». –

Répondre

6

Ceci est un exemple du fréquemment demandé "greatest-n-per-group" que nous voyons chaque semaine StackOverflow. Suivez ce tag pour voir d'autres solutions similaires.

SELECT s.*, f1.* 
FROM stock s 
INNER JOIN stockfile f1 
    ON (s.stockfileid = f1.stockfileid) 
LEFT OUTER JOIN stockfile f2 
    ON (s.stockfileid = f2.stockfileid AND f1.date < f2.date) 
WHERE f2.stockfileid IS NULL; 

S'il y a plusieurs lignes stockfile qui ont la date de max, vous les obtenez à la fois dans le jeu de résultats. Pour résoudre ce problème, vous devez ajouter des conditions de départage dans la jointure sur f2.


Merci d'avoir ajouté l'information CREATE TABLE. C'est très utile lorsque vous posez des questions SQL.

Je vois à partir des options de table AUTO_INCREMENT que vous avez 315k lignes dans stock et seulement 265 lignes dans stockfile. Votre table stockfile est le parent dans la relation, et la table stock est l'enfant, avec une colonne stockfileid qui fait référence à la clé primaire de stockfile.

Donc, votre question initiale était trompeuse. Vous voulez la dernière ligne de stock, pas la dernière ligne de stockfile.

SELECT f.*, s1.* 
FROM stockfile f 
INNER JOIN stock s1 
    ON (f.stockfileid = s1.stockfileid) 
LEFT OUTER JOIN stock s2 
    ON (f.stockfileid = s2.stockfileid AND (s1.touchdate < s2.touchdate 
     OR s1.touchdate = s2.touchdate AND s1.stockid < s2.stockid)) 
WHERE s2.stockid IS NULL; 

Je suppose que vous voulez « dernier » d'être par rapport à touchdate, donc si vous voulez utiliser à la place creationdate, vous pouvez l'éditer.

J'ai ajouté un terme à la jointure afin qu'il résout les liens. Je sais que vous avez dit que les dates sont "pratiquement uniques", mais comme le dit le dicton, "one in a million is next Tuesday".


Bon, je pense que je comprends ce que vous essayez de faire maintenant. Vous voulez la ligne la plus récente par sku, mais le date par lequel les comparer est dans la table référencée stockfile.

SELECT s1.*, f1.* 
FROM stock s1 
JOIN stockfile f1 ON (s1.stockfileid = f1.stockfileid) 
LEFT OUTER JOIN (stock s2 JOIN stockfile f2 ON (s2.stockfileid = f2.stockfileid)) 
    ON (s1.sku = s2.sku AND (f1.date < f2.date OR f1.date = f2.date AND f1.stockfileid < f2.stockfileid)) 
WHERE s2.sku IS NULL; 

Cette commande fait une autojointure de stock à lui-même, à la recherche d'une ligne avec la même sku et une date plus récente. Quand aucun n'est trouvé, alors s1 contient la ligne la plus récente pour son sku. Et chaque instance de stock doit se joindre à son stockfile pour obtenir le date.


Re commentaire sur l'optimisation: Il est difficile pour moi de tester parce que je n'ai pas tables peuplées avec des données correspondant à la vôtre, mais je suppose que vous devriez avoir les indices suivants:

CREATE INDEX stock_sku ON stock(sku); 
CREATE INDEX stock_stockfileid ON stock(stockfileid); 
CREATE INDEX stockfile_date ON stockfile(date); 

Je suggère d'utiliser EXPLAIN pour analyser la requête sans les index, puis de créer un index à la fois et de ré-analyser avec EXPLAIN pour voir lequel donne le plus d'avantages directs.

+0

Je sais, j'ai effectivement résolu un problème similaire grâce à stackoverflow. Mais votre requête me donne toujours 315.000 résultats:/ – skerit

+0

Ensuite, je suppose qu'il y a beaucoup de liens pour la date maximale par stock. –

+0

Beaucoup de cravates? Le champ "date" est pratiquement unique dans la table de stock. Par date, il peut y avoir environ 100 enregistrements dans la table de stock – skerit

0

Il y a deux façons communes pour y parvenir: une requête secondaire ou une auto-jointure.

Voir this example of selecting the group-wise maximum sur le site MySQL.

Edition, un exemple en utilisant un sous-requête:

SELECT stock.stockid, stock.sku, stock.quantity, 
     stockfile.filename, stockfile.date 
FROM stock 
INNER JOIN stockfile ON stock.stockfileid = stockfile.stockfileid 
WHERE stockfile.date = (SELECT MAX(date) FROM stockfile); 
0
select * 
from stock 
where stockfileid in (
      select top 1 stockfileid 
      from stockfile 
      order by date desc 
     ) 
+0

Cela ne fonctionnera pas tel quel - vous devez corréler la sous-requête. Actuellement, la sous-requête ne renvoie qu'une seule ligne, et vous obtenez le stockfileid, pas la date qui vous intéresse vraiment. –

+0

Ce n'est pas SQL valide pour MySQL: http://dev.mysql.com/doc/refman/5.4/fr/select.html – charstar

+0

Cela aussi - besoin d'utiliser 'LIMIT', pas' TOP' –

2

Utilisation: question

SELECT DISTINCT s.stockid, 
     s.sku, 
     s.quantity, 
     sf.filename, 
     sf.date 
    FROM STOCK s 
    JOIN STOCKFILE sf ON sf.stockfileid = s.stockfileid 
    JOIN (SELECT t.stockfileid, 
       MAX(t.date) 'max_date' 
      FROM STOCKFILE t 
     GROUP BY t.stockfileid) x ON x.stockfileid = sf.stockfileid 
           AND x.max_date = sf.date 
+0

Je crains que cela me donne encore tous les 315 000 enregistrements. – skerit

+0

J'ai mis à jour pour ajouter le 'DISTINCT', parce que la raison la plus probable est que les lignes sont dupliquées à cause des JOINs. –

+0

Hmm, même avec le distinct, il me donne toujours tous les enregistrements en double. – skerit

Questions connexes