我有一张类似于此的表格:
session_id | sku
------------|-----
a | 1
a | 2
a | 3
a | 4
b | 2
b | 3
c | 3
我想将其转换为类似于此的表:
sku1 | sku2 | score
------|------|------
1 | 2 | 1
1 | 3 | 1
1 | 4 | 1
2 | 3 | 2
2 | 4 | 1
3 | 4 | 1
这个想法是存储一个非规范化的表,允许人们查找给定的sku,其他skus与它相关的会话有什么关系,以及两个skus与同一个会话相关的次数。
您可以建议在PostgreSQL或其他技术中实施哪些算法,模式或策略?
我意识到这种查找可以使用计数或使用分面搜索引擎在原始表上完成。但是,我想让读取更高效,并且只想保留整体统计信息。我的想法是,我将在第一个表中的最新几千行上定期执行此转轴,然后将结果存储在第二个表中。我只关心第二张表的近似统计数据。
我有一些可行的SQL,但非常慢。还要考虑使用某种图形数据库的可能性,但希望避免为应用程序的一小部分添加其他技术。
更新:以下SQL似乎足够高效。我可以在第一个表(标签)中将120万行转换为第二个表(product_relations)中的250k行,在我的iMac上大约5分钟内可以使用大约2-3k的sku变量。实际上,我每天最多只能减少10k行的非规范化。问题是这是否真的是最好的方法。对我来说似乎有点脏。
BEGIN;
CREATE
TEMPORARY TABLE working_tags(tag_id int, session_id varchar, sku varchar) ON COMMIT DROP;
INSERT INTO working_tags
SELECT id,
session_id,
sku
FROM tags
WHERE time < now() - interval '12 hours'
AND processed_product_relation IS NULL
AND sku IS NOT NULL LIMIT 200000;
CREATE
TEMPORARY TABLE working_relations (sku1 varchar, sku2 varchar, score int) ON COMMIT DROP;
INSERT INTO working_relations
SELECT a.sku AS sku1,
b.sku AS sku2,
count(DISTINCT a.session_id) AS score
FROM working_tags AS a
INNER JOIN working_tags AS b ON a.session_id = b.session_id
AND a.sku < b.sku
WHERE a.sku IS NOT NULL
AND b.sku IS NOT NULL
GROUP BY a.sku,
b.sku;
UPDATE product_relations
SET score = working_relations.score+product_relations.score
FROM working_relations
WHERE working_relations.sku1 = product_relations.sku1
AND working_relations.sku2 = product_relations.sku2;
INSERT INTO product_relations (sku1, sku2, score)
SELECT working_relations.sku1,
working_relations.sku2,
working_relations.score
FROM working_relations
LEFT OUTER JOIN product_relations ON (working_relations.sku1 = product_relations.sku1
AND working_relations.sku2 = product_relations.sku2)
WHERE product_relations.sku1 IS NULL;
UPDATE tags
SET processed_product_relation = TRUE
WHERE id IN
(SELECT tag_id
FROM working_tags);
COMMIT;
答案 0 :(得分:1)
如果我正确地解释了你的意图(根据评论),应该这样做:
SELECT
s1.sku AS sku1,
s2.sku AS sku2,
count(session_id)
FROM session s1
INNER JOIN session s2 USING (session_id)
WHERE s1.sku < s2.sku
GROUP BY s1.sku, s2.sku
ORDER BY 1,2;
请参阅:http://sqlfiddle.com/#!15/2e0b2/1
换句话说:自加入会话,然后找到每个会话ID的所有SKU配对,不包括左边大于或等于右边的那些,以避免重复配对 - 如果我们有{{1}我们也不想要(1,2,count)
。然后按SKU配对进行分组,并计算每个配对的行数。
如果您的SKU配对可以重复并且您想要排除重复项,则可能需要(2,1,count)
。可能会有更有效的方法,但这是最简单的。
至少count(distinct session_id)
的索引非常有用。您可能还想弄乱计划员成本参数,以确保它选择一个好的计划 - 特别是确保session_id
准确无误,effective_cache_size
vs random_page_cost
反映您的缓存和I / O成本。最后,尽可能多地投入seq_page_cost
。
如果您要创建实体化视图,请work_mem
。 。这样就可以最大限度地减少写入/重写/覆盖的数量。