2013-09-13 2 views
3

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

Вот мой пример таблицы:

CREATE TABLE IF NOT EXISTS `orderTable` (
    `customer_id` varchar(10) NOT NULL, 
    `order_id` varchar(4) NOT NULL, 
    `date_added` date NOT NULL, 
    PRIMARY KEY (`customer_id`,`order_id`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1; 

INSERT INTO `orderTable` (`customer_id`, `order_id`, `date_added`) VALUES 
('1234', '5A', '1997-01-22'), 
('1234', '88B', '1992-05-09'), 
('0487', 'F9', '2002-01-23'), 
('5799', 'A12F', '2007-01-23'), 
('1234', '3A', '2009-01-22'), 
('3333', '7FHS', '2009-01-22'), 
('0487', 'Z33', '2004-06-23'), 
('3333', 'FF44', '2013-09-11'), 
('3333', '44f5', '2013-09-02'); 

Этот запрос возвращает более двух строк:

SELECT customer_id, order_id, date_added 
FROM orderTable T1 
WHERE (
    select count(*) FROM orderTable T2 
    where T2.order_id = T1.order_id AND T2.date_added <= T1.date_added 
) <= 2; 

Поскольку я не ищу для одной строки, это не стандартный запрос greatest-n-per-group типа.

Что мне не хватает, что я могу получить первые два заказа для каждого customer_id?

+0

Вы имеете в виду LIMIT 2 и не <= 2? – SenorAmor

+0

'для каждого клиента'. Ограничение до 2 для всего запроса не вернет результаты, которые мне нужны. –

+0

@acoder, проверьте мой ответ ниже, вы хотите получить два первых клиента, но вы подсчитываете строки в order_id, которые возвращают 1 для каждой строки. –

ответ

3

Лучший (то есть наиболее эффективный) подход заключается в использовании в запросе переменной, определяемой пользователем.

SELECT tmp.customer_id, tmp.date_added 
FROM ( 
    SELECT 
    customer_id, date_added, 
    IF (@prev <> customer_id, @rownum := 1, @rownum := @rownum+1) rank, 
    @prev := customer_id 
    FROM orderTable t 
    JOIN (SELECT @rownum := NULL, @prev := 0) r 
    ORDER BY t.customer_id 
) tmp 
WHERE tmp.rank <= 2 
ORDER BY customer_id, date_added 

Результаты:

| CUSTOMER_ID |      DATE_ADDED | 
|-------------|----------------------------------| 
|  0487 | January, 23 2002 00:00:00+0000 | 
|  0487 |  June, 23 2004 00:00:00+0000 | 
|  1234 |  May, 09 1992 00:00:00+0000 | 
|  1234 | January, 22 1997 00:00:00+0000 | 
|  3333 | January, 22 2009 00:00:00+0000 | 
|  3333 | September, 02 2013 00:00:00+0000 | 
|  5799 | January, 23 2007 00:00:00+0000 | 

скрипку here.

Обратите внимание, что соединение используется только для инициализации переменных.

3

Ваш исходный запрос должен быть (использование customer_id в подзапрос)

SELECT customer_id, order_id, date_added 
FROM orderTable T1 
WHERE (
    select count(*) FROM orderTable T2 
    where T2.customer_id = T1.customer_id AND T2.date_added <= T1.date_added 
) <= 2; 

Вы также можете использовать переменные:

SELECT customer_id, order_id, date_added FROM (
SELECT customer_id, order_id, date_added, 
@rownum := if(@prev_cust = customer_id, @rownum + 1,1) as rn, 
@prev_cust := customer_id cust_var 
FROM orderTable T1, 
    (SELECT @rownum := 0) r, 
    (SELECT @prev_cust := '') c 
order by customer_id, date_added 
) o where o.rn < 3; 

SQL DEMO

0

Вот другой (заведомо неполный) метод, хотя другие может иметь точку зрения о производительности ...

SELECT x.* 
    , COUNT(*) rank 
    FROM ordertable x 
    JOIN ordertable y 
    ON y.customer_id = x.customer_id 
    AND y.date_added <= x.date_added 
GROUP 
    BY x.customer_id 
    , x.date_added; 
+0

Quick (Off topic) комментарий к вашему форматированию, никогда не видел, чтобы запятые опустились примерно так, или зарезервированные слова, выровненные в одном столбце .. это удивительно легко читать. –

+0

:-) Я думаю, что это называется «выравнивание по центру». У него свои хулители, но мне это нравится :-) – Strawberry

0

Это должно привести к результатам, которые вы после, но внешний SELECT не будет наиболее эффективным, поскольку он фильтрует производную таблицу.

SELECT ranked.* 
FROM (
    SELECT ot.* , 
     @rownum := IF(ot.customer_id = @previous , @rownum +1, 1) rank, 
     @previous := ot.customer_id 
    FROM orderTable ot, 
     (SELECT @rownum :=1, @previous := NULL) init 
    ORDER BY customer_id, date_added 
) ranked 
WHERE rank <=2 
Смежные вопросы