2009-05-04 2 views
3

Я выполняю агрегатную функцию для нескольких записей, которые сгруппированы по общему идентификатору. Проблема в том, что я также хочу экспортировать некоторые другие поля, которые могут быть разными в сгруппированных записях, но я хочу получить эти определенные поля из одной из записей (первая, в соответствии с ORDER BY запроса).Связанные с группой записи, но выберите определенные поля только из первой записи

Начиная пример точка:

SELECT 
    customer_id, 
    sum(order_total), 
    referral_code 
FROM order 
GROUP BY customer_id 
ORDER BY date_created 

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

Это в PostgreSQL, но, возможно, синтаксис из других БД может быть достаточно похож на работу.

Забракованные решения:

  • не может использовать не более() или мин(), поскольку порядок является существенным.
  • Подзапрос может работать сначала, но не масштабируется; это чрезвычайно сокращенный пример. Мой фактический запрос содержит десятки таких полей, как referral_code, для которых мне нужен только первый экземпляр, и десятки предложений WHERE, которые, если они будут дублированы в подзапросе, сделают для кошмара обслуживания.

ответ

0

Вам понадобится window functions. Это своего рода GROUP BY, но вы можете получить доступ к отдельным строкам. Используется только эквивалент Oracle.

+0

... Интересно выглядит как новая функция для 8.4? К сожалению, нам нужно некоторое время, чтобы перейти к новым версиям после их выпуска, сейчас мы все еще застряли в 8.2 (хотя, надеюсь, не намного дольше ...): \ – David

0

Может быть что-то вроде:

SELECT 
    O1.customer_id, 
    O1.referral_code, 
    SQ.total 
FROM 
    Orders O1 
LEFT OUTER JOIN Orders O2 ON 
    O2.customer_id = O1.customer_id AND 
    O2.date_created < O1.date_created 
INNER JOIN (
    SELECT 
      customer_id, 
      SUM(order_total) AS total 
    FROM 
      Orders 
    GROUP BY 
      customer_id 
    ) SQ ON SQ.customer_id = O1.customer_id 
WHERE 
    O2.customer_id IS NULL 
+2

Вам нужно добавить «GROUP BY customer_id» до конца вашего подзапроса. Затем ваш запрос дает последний реферальный_код. Chagne - больше, чем меньше, чем для критериев соединения, и он получит первый ссылочный_код. – ahains

+0

Спасибо, похоже, что я оставил GROUP BY в моей нарезке-в-пасте –

0

Если date_created гарантированно будет уникальным для каждого Customer_ID, то вы можете сделать это:

[простая таблица]

create table ordertable (customer_id int, order_total int, referral_code char, date_created datetime) 
insert ordertable values (1,10, 'a', '2009-01-01') 
insert ordertable values (2,15, 'b', '2009-01-02') 
insert ordertable values (1,35, 'c', '2009-01-03') 

[заменить мой наименее яркие имена таблиц с чем-то лучше :)]

1

Ну, это на самом деле довольно просто.

Во-первых, давайте напишем запрос, который будет делать агрегацию:

select customer_id, sum(order_total) 
from order 
group by customer_id 

теперь давайте напишем запрос, который будет возвращать 1-referral_code и DATE_CREATED для данного Customer_ID:

select distinct on (customer_id) customer_id, date_created, referral_code 
from order 
order by customer_id, date_created 

Теперь вам может просто соединить 2 выбирает:

select 
    x1.customer_id, 
    x1.sum, 
    x2.date_created, 
    x2.referral_code 
from 
    (
     select customer_id, sum(order_total) 
     from order 
     group by customer_id 
    ) as x1 
    join 
    (
     select distinct on (customer_id) customer_id, date_Created, referral_code 
     from order 
     order by customer_id, date_created 
    ) as x2 using (customer_id) 
order by x2.date_created 

Я не тестировал его, поэтому быть опечатками в нем, но в целом он должен работать.

+0

+1, но это все еще страдает от необходимости добавления дополнительных предложений WHERE в 2-х местах. –

+0

Ну, это можно сделать без этого требования, но для этого потребуется пользовательский агрегат (первый). Не то, чтобы это сложно. – 2009-05-05 13:49:17

0

Может, что-то вроде этого сделать трюк?

SELECT 
    customer_id, 
    sum(order_total), 
    (SELECT referral_code 
    FROM order o 
    WHERE o.customer_id = order.customer_id 
    ORDER BY date_created 
    LIMIT 1) AS customers_referral_code 
FROM order 
GROUP BY customer_id, customers_referral_code 
ORDER BY date_created 

Это не требует от вас, чтобы сохранить статью, где в двух местах и ​​сохраняет значение порядка, но было бы получить довольно волосатым, если вам нужно «десятки полей», как referral_code. Это также довольно медленно (по крайней мере, в MySQL).

Это звучит для меня как referral_code, и десятки таких полей должны быть в таблице клиентов, а не в таблице заказов, поскольку они логически связаны с клиентом 1: 1, а не с порядком. Перемещение их там сделало бы запрос МНОГО более простым.

Это может также сделать трюк:

SELECT 
    o.customer_id, 
    sum(o.order_total), 
    c.referral_code, c.x, c.y, c.z 
FROM order o LEFT JOIN (
    SELECT referral_code, x, y, z 
    FROM orders c 
    WHERE c.customer_id = o.customer_id 
    ORDER BY c.date_created 
    LIMIT 1 
) AS c 
GROUP BY o.customer_id, c.referral_code 
ORDER BY o.date_created 
+0

В настоящее время ваш запрос содержит два поля, называемых referral_code (один из которых является подзапросом), ни один из которых не указан в GROUP BY. –

+0

Первый файл referral_code действительно был ошибкой. Отсутствие в GROUP BY было просто потому, что некоторые диалекты SQL не требуют этого. Спасибо, что указали это, исправлено. –

0
SELECT customer_id, order_sum, 
     (first_record).referral, (first_record).other_column 
FROM (
     SELECT customer_id, 
       SUM(order_total) AS order_sum, 
       (
       SELECT oi 
       FROM order oi 
       WHERE oi.customer_id = o.customer_id 
       LIMIT 1 
       ) AS first_record 
     FROM order o 
     GROUP BY 
       customer_id 
     ) q