Группируйте связанные записи, но выбирайте определенные поля только из первой записи - PullRequest
3 голосов
/ 05 мая 2009

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

Пример начальной точки:

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

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

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

Отклоненные решения:

  • Невозможно использовать max () или min (), потому что порядок значим.
  • Сначала подзапрос может работать, но не масштабируется; это очень редкий пример. В моем фактическом запросе есть десятки полей, таких как referral_code, который я хочу получить только в первом экземпляре, и десятки предложений WHERE, которые, если их дублировать в подзапросе, могут стать кошмаром обслуживания.

Ответы [ 6 ]

1 голос
/ 05 мая 2009

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

Сначала напишем запрос, который будет выполнять агрегирование:

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

Теперь давайте напишем запрос, который вернет 1-й код ссылки и дату создания для заданного идентификатора клиента:

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 голосов
/ 05 мая 2009
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
0 голосов
/ 05 мая 2009

Может ли что-то подобное сделать?

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

Это не требует, чтобы вы поддерживали предложение WHERE в двух местах и ​​сохранял значимость порядка, но получилось бы довольно странно, если бы вам понадобились "десятки полей", например, 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 голосов
/ 05 мая 2009

Если 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')

[замените мои хромые имена таблиц на что-нибудь получше :))

SELECT
  orderAgg.customer_id,
  orderAgg.order_sum,
  referral.referral_code as first_referral_code
FROM (
        SELECT
          customer_id,
          sum(order_total) as order_sum
        FROM ordertable
        GROUP BY customer_id
    ) as orderAgg join (
        SELECT
          customer_id,
          min(date_created) as first_date
        FROM ordertable
        GROUP BY customer_id
    ) as dateAgg on orderAgg.customer_id = dateAgg.customer_id
    join ordertable as referral 
        on dateAgg.customer_id = referral.customer_id
            and dateAgg.first_date = referral.date_created
0 голосов
/ 05 мая 2009

Возможно что-то вроде:

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
0 голосов
/ 05 мая 2009

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

...