在我的PostgreSQL数据库中,我有一个id列,显示进入的每个唯一线索。我还有一个connected_lead_id列,显示帐户是否彼此关联(即丈夫和妻子,父母和子女,朋友组) ,一组投资者等)。
当我们计算一个时间段内创建的ID的数量时,我们想查看一个时间段内connectd_id的唯一“组”的数量。换句话说,我们不想同时计算一对夫妻,我们只想计算一对,因为他们确实是一个领先者。
我们希望能够创建一个仅基于“ created_at”日期具有“第一个” ID的视图,然后在末尾包含“ connected_lead_id_1”,“ connected_lead_id_2”,“ connected_lead_id_3”等其他列。
我们要添加其他逻辑,以便我们采用“第一个” ID的源(除非为空),然后采用“第二个” connected_lead_id的源,除非其为空,依此类推。最后,我们想从connected_lead_id组中获取最早的on_boarded_date。
id | created_at | connected_lead_id | on_boarded_date | source |
2 | 9/24/15 23:00 | 8 | |
4 | 9/25/15 23:00 | 7 | |event
7 | 9/26/15 23:00 | 4 | |
8 | 9/26/15 23:00 | 2 | |referral
11 | 9/26/15 23:00 | 336 | 7/1/17 |online
142 | 4/27/16 23:00 | 336 | |
336 | 7/4/16 23:00 | 11 | 9/20/18 |referral
最终目标:
id | created_at | on_boarded_date | source |
2 | 9/24/15 23:00 | | referral |
4 | 9/25/15 23:00 | | event |
11 | 9/26/15 23:00 | 7/1/17 | online |
理想情况下,我们还会在末尾有i个额外的列,以显示附加到基本ID的每个connected_lead_id。
感谢您的帮助!
答案 0 :(得分:1)
主要思想-草图:
遍历有序集。获取所有id
中从未见过的所有connected_lead_id
( cli )。这些是您进行递归的起点。
问题是您的电话号码142
以前没有见过,但由于其期限,它与11
属于同一组。因此,最好获得看不见的ID的片段。使用这些值,在递归部分中稍后更容易计算组的ID。由于存在循环,因此需要一个函数/存储过程。
递归部分:第一步是获取起始cli的id。使用created_at
时间戳计算第一个推荐ID。之后,可以在cli上进行简单的树递归。
1。功能:
CREATE OR REPLACE FUNCTION filter_groups() RETURNS int[] AS $$
DECLARE
_seen_values int[];
_new_values int[];
_temprow record;
BEGIN
FOR _temprow IN
-- 1:
SELECT array_agg(id ORDER BY created_at) as ids, connected_lead_id FROM groups GROUP BY connected_lead_id ORDER BY MIN(created_at)
LOOP
-- 2:
IF array_length(_seen_values, 1) IS NULL
OR (_temprow.ids || _temprow.connected_lead_id) && _seen_values = FALSE THEN
_new_values := _new_values || _temprow.connected_lead_id;
END IF;
_seen_values := _seen_values || _temprow.ids;
_seen_values := _seen_values || _temprow.connected_lead_id;
END LOOP;
RETURN _new_values;
END;
$$ LANGUAGE plpgsql;
_new_values
)中。在这两种情况下,都将id和cli添加到存储所有尚未看到的id(_seen_values
)的变量中到目前为止,结果为{8, 7, 336}
(相当于ID {2,4,11,142}
!)
2。递归:
-- 1:
WITH RECURSIVE start_points AS (
SELECT unnest(filter_groups()) as ids
),
filtered_groups AS (
-- 3:
SELECT DISTINCT
1 as depth, -- 3
first_value(id) OVER w as id, -- 4
ARRAY[(MIN(id) OVER w)] as visited, -- 5
MIN(created_at) OVER w as created_at,
connected_lead_id,
MIN(on_boarded_date) OVER w as on_boarded_date -- 6,
first_value(source) OVER w as source
FROM groups
WHERE connected_lead_id IN (SELECT ids FROM start_points)
-- 2:
WINDOW w AS (PARTITION BY connected_lead_id ORDER BY created_at)
UNION
SELECT
fg.depth + 1,
fg.id,
array_append(fg.visited, g.id), -- 8
LEAST(fg.created_at, g.created_at),
g.connected_lead_id,
LEAST(fg.on_boarded_date, g.on_boarded_date), -- 9
COALESCE(fg.source, g.source) -- 10
FROM groups g
JOIN filtered_groups fg
-- 7
ON fg.connected_lead_id = g.id AND NOT (g.id = ANY(visited))
)
SELECT DISTINCT ON (id) -- 11
id, created_at,on_boarded_date, source
FROM filtered_groups
ORDER BY id, depth DESC;
WITH
部分给出了函数的结果。 unnest()
将id数组扩展为每个id的每一行。created_at
时间戳对窗口排序。在您的示例中,除分组的11
和142
以外,所有值都在各自的窗口中。first_value()
给出有序窗口框架的第一个值。假设142
的时间戳created_at较小,则结果为142
。但这还是11
。2-8-2-8-2-8-2-8-...
142
的日期比11
短,将是结果)。现在计算递归的起始查询。以下描述了递归部分:
on_boarded_date
较早,则采用该方法。COALESCE
给出第一个NOT NULL
值。因此,第一个NOT NULL
source
在整个递归过程中都是安全的在给出所有递归步骤的结果的递归之后,我们只想筛选出每个起始ID的最深访问。
DISTINCT ON (id)
给出第一个出现id的行。要获取最后一个,整个集合按depth
变量降序排列。答案 1 :(得分:1)
好吧,目前我能提出的最好的办法是,首先建立最大数量的相关ID组,然后再加入到潜在客户表中以获取其余数据(有关详细信息,请参见此SQL Fiddle设置,完整的查询和结果)。
要获取最大组,您可以使用递归公用表表达式来首先扩展组,然后执行查询以将CTE结果过滤为最大组:
with recursive cte(grp) as (
select case when l.connected_lead_id is null then array[l.id]
else array[l.id, l.connected_lead_id]
end from leads l
union all
select grp || l.id
from leads l
join cte
on l.connected_lead_id = any(grp)
and not l.id = any(grp)
)
select * from cte c1
上面的CTE输出几个相似的组以及中间组。下面的查询谓词修剪掉非最大的组,并将结果限制为每个可能组的一个排列:
where not exists (select 1 from cte c2
where c1.grp && c2.grp
and ((not c1.grp @> c2.grp)
or (c2.grp < c1.grp
and c1.grp @> c2.grp
and c1.grp <@ c2.grp)));
Results :
| grp |
|------------|
| 2,8 |
| 4,7 |
| 14 |
| 11,336,142 |
| 12,13 |
接下来,将上面的最终查询返回到Leads表,并使用窗口函数获取剩余的列值,并使用不同的运算符将其修剪到最终结果集:
with recursive cte(grp) as (
...
)
select distinct
first_value(l.id) over (partition by grp order by l.created_at) id
, first_value(l.created_at) over (partition by grp order by l.created_at) create_at
, first_value(l.on_boarded_date) over (partition by grp order by l.created_at) on_boarded_date
, first_value(l.source) over (partition by grp
order by case when l.source is null then 2 else 1 end
, l.created_at) source
, grp CONNECTED_IDS
from cte c1
join leads l
on l.id = any(grp)
where not exists (select 1 from cte c2
where c1.grp && c2.grp
and ((not c1.grp @> c2.grp)
or (c2.grp < c1.grp
and c1.grp @> c2.grp
and c1.grp <@ c2.grp)));
Results :
| id | create_at | on_boarded_date | source | connected_ids |
|----|----------------------|-----------------|----------|---------------|
| 2 | 2015-09-24T23:00:00Z | (null) | referral | 2,8 |
| 4 | 2015-09-25T23:00:00Z | (null) | event | 4,7 |
| 11 | 2015-09-26T23:00:00Z | 2017-07-01 | online | 11,336,142 |
| 12 | 2015-09-26T23:00:00Z | 2017-07-01 | event | 12,13 |
| 14 | 2015-09-26T23:00:00Z | (null) | (null) | 14 |