Справка по сложному SQL-запросу при агрегировании значений для вложенного подзапроса - PullRequest
1 голос
/ 12 марта 2010

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

Виды событий - это простые значения, описывающие: «Обещанное пожертвование», «Полученное пожертвование», «Обзвонено», «Отслежено» и так далее. Виды мероприятий упорядочены:

CREATE TABLE event_kinds (
  id,
  name,
  position);

События содержат фактическую ссылку на событие:

CREATE TABLE events (
  id,
  person_id,
  company_id,
  referrer_id,
  event_kind_id,
  created_at);

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

Обратите внимание, у меня нет ссылки на идентификатор сотрудника. Ссылка существует, но подразумевается. У меня есть код приложения для проверки того, что person_id и company_id действительно ссылаются на запись сотрудника. Остальные таблицы довольно простые:

CREATE TABLE people (
  id, name);

CREATE TABLE companies (
  id, name);

CREATE TABLE employees (
  id, person_id, company_id);

Я пытаюсь получить следующий отчет:

                         Referrer       Phoned     Promised   Donated
    Francois                            Feb 16th   Feb 20th   Mar 1st
    Apple (Steve Jobs)   Steve Ballmer                        Mar 3rd
    IBM                  Bill Gates     Mar 7th

Первый ряд - это запись о людях, второй - сотрудник, а третий - компания. Если бы я попросил реферера Билла Гейтса о типах событий Phoned, я бы увидел только 3-ю строку, а запрос Стива и Phoned не вернул бы никаких строк.

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

SELECT companies.id,
       companies.name,
       (SELECT events.id FROM events WHERE events.referrer_id = 1470 AND events.company_id = companies.id AND events.person_id IS NULL AND events.event_kind_id = 9 ORDER BY created_at DESC LIMIT 1) event_kind_9,
       (SELECT events.id FROM events WHERE events.company_id = companies.id AND events.person_id IS NULL AND events.event_kind_id = 10 ORDER BY created_at DESC LIMIT 1) event_kind_10,
       (SELECT events.created_at FROM events WHERE events.referrer_id = 1470 AND events.company_id = companies.id AND events.person_id IS NULL AND events.event_kind_id = 9 ORDER BY created_at DESC LIMIT 1) event_kind_9_order
FROM "companies"

SELECT people.id,
       people.name,
       (SELECT events.id FROM events WHERE events.referrer_id = 1470 AND events.company_id IS NULL AND events.person_id = people.id AND events.event_kind_id = 9 ORDER BY created_at DESC LIMIT 1) event_kind_9,
       (SELECT events.id FROM events WHERE events.company_id IS NULL AND events.person_id = people.id AND events.event_kind_id = 10 ORDER BY created_at DESC LIMIT 1) event_kind_10,
       (SELECT events.created_at FROM events WHERE events.referrer_id = 1470 AND events.company_id IS NULL AND events.person_id = people.id AND events.event_kind_id = 9 ORDER BY created_at DESC LIMIT 1) event_kind_9_order
FROM "people"

SELECT employees.id,
       employees.company_id,
       employees.person_id,
       (SELECT events.id FROM events WHERE events.referrer_id = 1470 AND events.company_id = employees.company_id AND events.person_id = employees.person_id AND events.event_kind_id = 9 ORDER BY created_at DESC LIMIT 1) event_kind_9,
       (SELECT events.id FROM events WHERE events.company_id = employees.company_id AND events.person_id = employees.person_id AND events.event_kind_id = 10 ORDER BY created_at DESC LIMIT 1) event_kind_10,
       (SELECT events.created_at FROM events WHERE events.referrer_id = 1470 AND events.company_id = employees.company_id AND events.person_id = employees.person_id AND events.event_kind_id = 9 ORDER BY created_at DESC LIMIT 1) event_kind_9_order
FROM "employees"

Я скорее подозреваю, что я делаю это неправильно. Должен быть более простой способ сделать это.

Еще одним критерием фильтрации будет фильтрация по именам людей / компаний: WHERE LOWER (companies.name) LIKE '% apple%'.

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

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

Для справки, я использую PostgreSQL от Ruby, ActiveRecord / Rails. Решение - чистый SQL.

1 Ответ

1 голос
/ 12 марта 2010

Можно ли иметь несколько строк для одного человека / компании и event_kind_id (например, Билл Гейтс, звонивший 07 марта и 09 марта)? Если нет, вы можете сделать что-то вроде:

Select Coalesce(People.name, Companies.name) As Name
    , Referrers.name
    , Min(Case When EventKinds.name = 'Phoned' Then Events.created_at End) As Phoned
    , Min(Case When EventKinds.name = 'Promised' Then Events.created_at End) As Promised
    , Min(Case When EventKinds.name = 'Donated' Then Events.created_at End) As Donated
From Events
    Join EventKinds
        On EventKinds.id = Events.event_kind_id
    Left Join People As Referrers
        On Referrers.id = Events.referrer_id
    Left Join People
        On People.id = Events.person_id
    Left Join Companies
        On Companies.id = Events.company_id
-- Where Companies.Name Like 'foo%'
Group By Coalesce(People.name, Companies.name), Referrers.name
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...