Рекурсивный выбор с условиями

Моя проблема заключается в следующем:

  1. Мне нужно проверить, является ли одно из значений (среднее) выбора нулевым
  2. Если это так, запустите выбор, ища первое ненулевое значение в другом периоде (дате).
  3. После этого выполните операцию, чтобы проверить, за какой период (дата - диапазон) это
  4. После этого сделайте среднее

Человеческий язык

Я хочу получить среднюю стоимость всех товаров, сгруппированных по периодам.

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

Периоды не являются месяцами, они определяются заказчиком и могут перекрывать 2 месяца, например:

2012-01-01 - 2012-01-29
2012-01-30 - 2012-02-27

Как я могу сделать это в ОДНОМ запросе?
Запрос примерно выглядит следующим образом (average – это столбец, значение которого я хочу сравнить):

select
    p.id
    ,(select 
        avg(cost)
    from 
        invoices i 
    where 
        i.product_id = p.id 
        and i.add_date between $start_date
        and $end_date
    ) as average
from
    products p;

Таблицы / ДАННЫЕ / Запросы

См. эту суть (это не исходная база данных, я делаю этот тест сейчас): https://gist.github.com/4520123


person Patrick Maciel    schedule 11.01.2013    source источник
comment
Пожалуйста, напишите название, более точно описывающее проблему.   -  person Oded    schedule 12.01.2013
comment
Теперь все в порядке? Рекурсивный выбор с условиями в SQL-запросе Postgre   -  person Patrick Maciel    schedule 12.01.2013
comment
Предоставьте образцы данных и ожидаемый результат. Вы можете подумать, что вопрос ясен, но большинство людей не понимают, о чем вы пытаетесь спросить.   -  person Gordon Linoff    schedule 12.01.2013
comment
@GordonLinoff Сейчас я редактирую пост. Я думаю, что это хорошая информация в то время.   -  person Patrick Maciel    schedule 12.01.2013
comment
Извините, меня приучили читать много бреда, но в данном случае, боюсь, у меня ничего не получится. Что такое определение таблицы? Каковы ваши намерения? Где рекурсия?   -  person wildplasser    schedule 12.01.2013
comment
@wildplasser Успокойся, мой друг. Таблицы: products/inфактура_products. Рекурсивный? Я не знаю, это вопрос. И извините за неясность... Я стараюсь.   -  person Patrick Maciel    schedule 12.01.2013
comment
Кстати, когда мы запросили пример данных, мы надеялись на что-то подобное (настройка теста слева, а не невозможный запрос справа):   -  person Erwin Brandstetter    schedule 12.01.2013
comment
@ErwinBrandstetter, к моим ограниченным знаниям в sql, в основном в postgresql (потому что я всегда работал с MySQL), этот запрос из другого мира.   -  person Patrick Maciel    schedule 12.01.2013
comment
@PatrickMaciel: Инопланетянин все равно совершил аварийную посадку. Жалко однако. :) youtube.com/watch?v=muMcWMKPEWQ   -  person Erwin Brandstetter    schedule 12.01.2013
comment
If in the specific period, the product not have invoice, I need get the average for the previous period IIUC, предыдущий период подразумевает, что соответствующие периоды хранятся где-то в таблице? Могут ли периоды совпадать?   -  person wildplasser    schedule 12.01.2013
comment
@wildplasser Да, таблица date_period, но эта таблица не имеет связи (pk/fk) с какой-либо другой таблицей. Но не волнуйтесь сейчас, я могу решить проблему и опубликовать решение выше (посмотрите, пожалуйста, и прокомментируйте)   -  person Patrick Maciel    schedule 12.01.2013
comment
Я не вижу никакого DDL. Также: полностью отсутствуют определения для счетов и напитков. Имея правильное определение проблемы и полный набор таблиц и тестовых данных, большинство из нас, вероятно, могли бы решить вашу проблему за 5-10 минут. Ваше собственное решение выглядит субоптимальным, но на данный момент я не хочу его изучать.   -  person wildplasser    schedule 12.01.2013
comment
@wildplasser Да, я вас понимаю, но этот вопрос из-за моей работы, другими словами, одна проблема в моей работе. Итак, я не могу предоставить некоторую информацию из-за безопасности. Поэтому я попытался предоставить как можно больше информации, основываясь на другой модели данных. Вы можете понять мою точку зрения? Я знаю, что это так сложно понять, но можно абстрагироваться от информации и создать что-то «своими руками», как я это делаю, когда мне нужно решить проблему кого-то здесь, на StackOverflow.   -  person Patrick Maciel    schedule 12.01.2013
comment
@wildplasser Для помощи вам, я сейчас делаю одну тестовую базу данных своими руками и отправляю в суть. Если у вас есть какие-либо вопросы, скажите мне.   -  person Patrick Maciel    schedule 12.01.2013


Ответы (3)


Другой способ решить вашу проблему:

  1. Найдите последний месяц с доступными данными.
  2. Получите avg() месяца с первого шага.

Запрос:

SELECT i.product_id, 
       max(date_trunc('month',i.add_date)) as last_month
FROM invoices i 
GROUP BY i.product_id

Даст вам последний месяц с данными, доступными для каждого продукта.

Затем:

SELECT p.id,
       avg(inv.cost)
FROM products p
JOIN invoices inv 
  ON inv.product_id = p.id
JOIN (SELECT i.product_id, 
             max(date_trunc('month',i.add_date)) as last_month
      FROM invoices i 
      GROUP BY i.product_id) last_inv 
  ON last_inv.product_id = inv.product_id
  AND last_inv.last_month = date_trunc('month',inv.add_date)

Чтобы получить avg за последний месяц.

person Ihor Romanchenko    schedule 11.01.2013
comment
Игорь, хорошее решение. попробую запустить. Но в моем случае лучшим решением будет выполнение всех запросов одним запросом. - person Patrick Maciel; 12.01.2013
comment
@PatrickMaciel Вам нужно только запустить второй запрос. Он имеет первый в качестве подзапроса. Я написал это только для того, чтобы объяснить, как это работает. - person Ihor Romanchenko; 12.01.2013
comment
@PatrickMaciel: вы можете легко упаковать это в один запрос. Используйте подзапрос или CTE. Это лучшее решение, поскольку вычисляются только релевантные средние значения. То, что вы имели в виду (когда я прочитал ваш вопрос), рекурсивный запрос, вычисление средних значений просто невозможно. Я пробовал, но не смог с ERROR: aggregate functions not allowed in a recursive query's recursive term.. - person Erwin Brandstetter; 12.01.2013
comment
@ErwinBrandstetter спасибо, мой друг. Я постараюсь следовать этому решению и вашему комментарию. Спасибо еще раз. - person Patrick Maciel; 12.01.2013

Думаю, я понял. Вы хотите последовательно проверить средние значения за разные периоды. Ниже приведен пример для трех периодов:

select p.id,
       coalesce(cost_period1, cost_period2, cost_period3) as average
from products p left outer join
     (select i.product_id, 
             avg(case when i.add_date between $start_date1 and $end_date1 then cost
                 end) as cost_period1,
             avg(case when i.add_date between $start_date2 and $end_date2 then cost
                 end) as cost_period2,
             avg(case when i.add_date between $start_date3 and $end_date3 then cost
                 end) as cost_period3
      from invoices i
      group by i.product_id
     ) ip
     on p.id = ip.product_id

Это непроверенный запрос. Вычисляется среднее значение для каждого периода в подзапросе, а затем выбирается первое значение, отличное от NULL.

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

select p.id, avgcost
from products p left outer join
     (select ip.*, row_number() over (partition by product_id order by yearmon desc) as seqnum
      from (select i.product_id, year(add_date)*12+month(add_date) as yearmon,
                   avg(cost) as avgcost
            from invoices i
            group by i.product_id, year(add_date)*12+month(add_date)
           ) ip
      where seqnum = 1
     ) ip
     on p.id = ip.product_id
person Gordon Linoff    schedule 11.01.2013
comment
Я ценю вашу помощь, но разве это не так, потому что у меня нет точной даты периода. Мне нужно обратно, месяц за месяцем, пока не найду результат. - person Patrick Maciel; 12.01.2013

Я решаю вопрос, используя этот запрос с помощью моего друга по работе.

Я взял последнюю покупку (счет), конкретный продукт (напиток) и вычислил среднее значение.

    select (sum(aux.price * aux.quantity) / sum(aux.quantity))
    from (select    inp.price, inp.quantity, prd.product, drk.drink_num, inv.add_date
                        from    invoices                                inv
            inner join invoice_products inp on inp.invoice = inv.invoice
            inner join products                 prd on prd.product = inp.product
            inner join drinks                       drk on drk.product = prd.product) aux,
    date_period dtp
    where 
            aux.add_date between dtp.starting_date and dtp.ending_date
            and aux.drink_num = 1836 -- example id
            and dtp.year <= 2012 -- current year search
    group by
            dtp.year,
            dtp.period
    order by 
            dtp.year desc,
            dtp.period desc
    limit 1

В любом случае, спасибо, ребята!

person Patrick Maciel    schedule 12.01.2013