Выберите самую новую запись из объединенной таблицы MySQL - PullRequest
1 голос
/ 17 декабря 2009

У меня есть информация о количестве товара в моей базе данных.
1 таблица, "stock", содержит productid (sku) вместе с количеством и именем файла, откуда он пришел.

Другая таблица, «stockfile», содержит все обработанные имена файлов вместе с датами.

Теперь мне нужно получить все продукты с их последними значениями количества на складе.

Это дает мне ВСЕ продукты несколько раз со всем их количеством запаса (в результате 300 000 записей)

ВЫБРАТЬ stock.stockid, stock.sku, stock.quantity, stockfile.filename, stockfile.date
Со склада
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Стокфайл НА stock.stockfileid = stockfile.stockfileid
ЗАКАЗАТЬ НА stock. sku ASC

Я уже пробовал это:

ВЫБРАТЬ * ИЗ СКЛАДА
ВНУТРЕННЕЕ СОЕДИНЕНИЕ Стокфайл НА stock.stockfileid = stockfile.stockfileid
GROUP BY sku
ИМЕЕТ stockfile.date = MAX (stockfile.date)
ЗАКАЗАТЬ stock. sku ASC

Но это не сработало

ПОКАЗАТЬ CREATE TABLE сток:

CREATE TABLE stock (
stockid bigint (20) NOT NULL AUTO_INCREMENT,
sku char (25) NOT NULL,
quantity int (5) NOT NULL,
creationdate datetime НЕ NULL,
stockfileid smallint (5) без знака NOT NULL,
touchdate datetime НЕ NULL,
ПЕРВИЧНЫЙ КЛЮЧ (stockid)
) ENGINE = MyISAM AUTO_INCREMENT = 315169 CHARSET ПО УМОЛЧАНИЮ = latin1

ПОКАЗАТЬ CREATE TABLE stockfile:

CREATE TABLE stockfile (
stockfileid smallint (5) без знака NOT NULL AUTO_INCREMENT,
filename varchar (25) NOT NULL,
creationdate datetime DEFAULT NULL,
touchdate datetime DEFAULT NULL,
date datetime ПО УМОЛЧАНИЮ NULL,
begindate datetime DEFAULT NULL,
enddate datetime DEFAULT NULL,
ПЕРВИЧНЫЙ КЛЮЧ (stockfileid)
) ENGINE = MyISAM AUTO_INCREMENT = 265 CHARSET ПО УМОЛЧАНИЮ = латинский 1

Ответы [ 4 ]

6 голосов
/ 17 декабря 2009

Это пример часто задаваемого вопроса " наибольшее число групп * ", который мы видим каждую неделю в StackOverflow. Воспользуйтесь этим тегом, чтобы увидеть другие похожие решения.

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;

Если в stockfile есть несколько строк с максимальной датой, вы получите их обе в наборе результатов. Чтобы решить эту проблему, вам нужно добавить некоторые условия прерывания связей в объединение на f2.


Спасибо за добавление информации CREATE TABLE. Это очень полезно, когда вы задаете вопросы по SQL.

Я вижу из параметров таблицы AUTO_INCREMENT, что у вас есть 315 тыс. Строк в stock и только 265 строк в stockfile. Ваша таблица stockfile является родительской в ​​отношениях, а таблица stock является дочерней, со столбцом stockfileid, который ссылается на первичный ключ stockfile.

Итак, ваш первоначальный вопрос вводил в заблуждение. Вы хотите самую последнюю строку из stock, а не самую последнюю строку из 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;

Я предполагаю, что вы хотите, чтобы "последний" относился к touchdate, поэтому, если вы хотите использовать creationdate вместо этого, вы можете выполнить редактирование.

Я добавил термин в объединение, чтобы он разрешал связи. Я знаю, что вы сказали, что даты "практически уникальны", но, как говорится, " один на миллион в следующий вторник ."


Хорошо, я думаю, что понимаю, что вы пытаетесь сделать сейчас. Вы хотите самую последнюю строку для sku, но date, по которой их сравнивать, находится в ссылочной таблице 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;

Это само-соединение stock с самим собой, ища строку с таким же sku и более новым date. Если ничего не найдено, то s1 содержит самую последнюю строку для sku. И каждый экземпляр stock должен присоединиться к своему stockfile, чтобы получить date.


Комментарий по поводу оптимизации: мне сложно тестировать, потому что у меня нет таблиц, заполненных данными, совпадающими с вашими, но я думаю, у вас должны быть следующие индексы:

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

Я бы предложил использовать EXPLAIN для анализа запроса без индексов, а затем создавать по одному индексу за раз и повторно анализировать с помощью EXPLAIN, чтобы увидеть, какой из них дает наиболее прямое преимущество.

2 голосов
/ 17 декабря 2009

Использование:

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 голосов
/ 17 декабря 2009
select *
from   stock
where  stockfileid in (
           select top 1 stockfileid
           from   stockfile
           order by date desc
       )
0 голосов
/ 17 декабря 2009

Для этого есть два распространенных способа: подзапрос или самостоятельное соединение.

См. этот пример выбора группового максимума на сайте MySQL.

Изменить, пример с использованием подзапроса:

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);
...