复杂SQL查询有助于聚合嵌套子查询的值

时间:2010-03-12 04:59:43

标签: sql postgresql

我有人,公司,员工,活动和活动类型。我正在制作一份报告/后续工作表,其中人员,公司和员工是行,而列是事件种类。

事件种类是简单的价值观,描述:“承诺捐赠”,“收到捐赠”,“打电话”,“跟进”等。订购活动种类:

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,而我不会用于其他事件种类。

注意我没有员工ID参考。该引用存在,但暗示。我有应用程序代码来验证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行,而要求Steve和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 个答案:

答案 0 :(得分:1)

是否可以为同一个人/公司和event_kind_id设置多行(例如比尔盖茨于3月7日和3月9日打电话)?如果没有,你可以这样做:

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