存储用于推荐营销的分层数据 (MySQL)

发布于 2024-11-06 18:12:50 字数 1009 浏览 2 评论 0原文

我需要为网站注册的用户提供 5 级层次结构。每个用户都受到另一个用户的邀请,我需要知道一个用户的所有后代。还有用户的祖先。

我想到了2个解决方案。

  1. 以这种方式保持关系表。闭包表:

    ancestor_id  descendant_id  distance
    1            1              0
    2            2              0
    3            3              0
    4            4              0
    5            5              0
    6            6              0
    2            3              1
  1. 拥有此关系表。表中保存有5级祖先。 “祖先”表:

   user_id ancestor_level1_id ancestor_level2_id ancestor_level3_id ancestor_level4_id ancestor_level5_id
   10      9                  7                  4                  3                  2
   9       7                  4                  3                  2                  1

这些是好主意吗?

我了解“邻接列表模型”和“修改后的先序树遍历算法”,但是这些对于“推荐”系统来说是很好的解决方案吗?

我需要在这棵树上执行的查询是:

  • 当用户购买东西时经常添加新用户
  • ,他们的推荐人获得百分比佣金
  • 每个用户应该能够找出他们推荐了多少人(以及有多少人被推荐)由他们推荐的人推荐......)在每个级别

I need to have a 5 levels hierarchy for the users registered to a website. Every user is invited by another, and I need to know all descendants for a user. And also ancestors for a user.

I have in mind 2 solution.

  1. Keeping a table with relationships this way. A closure table:

    ancestor_id  descendant_id  distance
    1            1              0
    2            2              0
    3            3              0
    4            4              0
    5            5              0
    6            6              0
    2            3              1
  1. Having this table for relationships. Keeping in a table 5 levels ancestors. A "ancestors" table:

   user_id ancestor_level1_id ancestor_level2_id ancestor_level3_id ancestor_level4_id ancestor_level5_id
   10      9                  7                  4                  3                  2
   9       7                  4                  3                  2                  1

Are these good ideas?

I know about "the adjacency list model" and "the modified preorder tree traversal algorithm", but are these good solutions for a "referral" system?

The queries that I need to perform on this tree are:

  • frequently adding a new users
  • when a user buys something, their referrers get a percentage commission
  • every user should be able to find out how many people they've referred (and how many people were referred by people who they referred....) at each level

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

北音执念 2024-11-13 18:12:50

关闭表

ancestor_id  descendant_id  distance
    1            1              0
    2            2              0
    3            3              0
    4            4              0
    5            5              0
    6            6              0
    2            3              1

要添加由用户 3 引用的用户 10。(我认为您不需要在这两个插入之间锁定表):

insert into ancestor_table
select ancestor_id, 10, distance+1
from ancestor_table
where descendant_id=3;

insert into ancestor_table values (10,10,0);

查找用户 3 引用的所有用户。

select descendant_id from ancestor_table where ancestor_id=3;

对这些用户进行计数按深度:

select distance, count(*) from ancestor_table where ancestor_id=3 group by distance;

查找用户 10 的祖先。

select ancestor_id, distance from ancestor_table where descendant_id=10;

此方法的缺点是该表将占用大量存储空间。

Closure Table

ancestor_id  descendant_id  distance
    1            1              0
    2            2              0
    3            3              0
    4            4              0
    5            5              0
    6            6              0
    2            3              1

To add user 10, referred by user 3. (I don't think you need to lock the table between these two insertions):

insert into ancestor_table
select ancestor_id, 10, distance+1
from ancestor_table
where descendant_id=3;

insert into ancestor_table values (10,10,0);

To find all users referred by user 3.

select descendant_id from ancestor_table where ancestor_id=3;

To count those users by depth:

select distance, count(*) from ancestor_table where ancestor_id=3 group by distance;

To find the ancestors of user 10.

select ancestor_id, distance from ancestor_table where descendant_id=10;

The drawback to this method is amount of storage space this table will take.

明月松间行 2024-11-13 18:12:50

使用 OQGRAPH 存储引擎。

您可能想要跟踪任意数量的级别,而不仅仅是 5 个级别。获取支持 QGRAPH 引擎 的 MySQL 分支之一(例如例如 MariaDB 或 OurDelta),并使用它来存储您的树。它实现了邻接表模型,但通过使用名为 latch 的特殊列向存储引擎发送命令,告诉它要执行哪种查询,您可以获得闭包表的所有优点无需在每次有人注册您的网站时进行簿记工作。

以下是您在 OQGRAPH 中使用的查询。请参阅以下位置的文档:
http://openquery.com/graph-computation-engine-documentation

我们要去使用 origid 作为引用者,并使用 destid 作为引用者。

添加用户 11,由用户 10 引用

insert into ancestors_table (origid,destid) values (10,11)

查找用户 3 引用的所有用户。 查找

SELECT linkid FROM ancestors_table WHERE latch = 2 AND origid = 3;

用户 10 的祖先。

SELECT linkid FROM ancestors_table WHERE latch = 2 AND destid = 10;

查找每个级别的用户数量,由用户 3 引用:

SELECT count(linkid), weight
FROM ancestors_table
WHERE latch = 2 AND origid = 3
GROUP BY weight;

Use the OQGRAPH storage engine.

You probably want to keep track of an arbitrary number of levels, rather than just 5 levels. Get one of the MySQL forks that supports the QGRAPH engine (such as MariaDB or OurDelta), and use that to store your tree. It implements the adjacency list model, but by using a special column called latch to send a command to the storage engine, telling it what kind of query to perform, you get all of the advantages of a closure table without needing to do the bookkeeping work each time someone registers for your site.

Here are the queries you'd use in OQGRAPH. See the documentation at
http://openquery.com/graph-computation-engine-documentation

We're going to use origid as the referrer, and destid as the referree.

To add user 11, referred by user 10

insert into ancestors_table (origid,destid) values (10,11)

To find all users referred by user 3.

SELECT linkid FROM ancestors_table WHERE latch = 2 AND origid = 3;

To find the ancestors of user 10.

SELECT linkid FROM ancestors_table WHERE latch = 2 AND destid = 10;

To find the number of users at each level, referred by user 3:

SELECT count(linkid), weight
FROM ancestors_table
WHERE latch = 2 AND origid = 3
GROUP BY weight;
爱已欠费 2024-11-13 18:12:50

管理 MySQL 中的分层数据

一般来说,我喜欢“嵌套集” ”,特别是。 MySQL 并没有真正提供对分层数据的语言支持。
它速度很快,但如果易于维护很重要,您需要确保您的开发人员阅读该文章。它非常灵活——这对于你的情况来说似乎并不重要。

这似乎很适合您的问题 - 在推荐模型中,您需要找到推荐人树,这在嵌套集合模型中速度很快;您还需要知道给定用户的 ~children@ 是谁,以及他们的关系深度;这也很快。

Managing Hierarchical Data in MySQL

In general, I like the "nested set", esp. in MySQL which doesn't really have language support for hierarchical data.
It's fast, but you'll need to make sure your developers read that article if ease of maintenance is a big deal. It's very flexible - which doesn't seem to matter much in your case.

It seems a good fit for your problem - in the referral model, you need to find the tree of referrers, which is fast in the nested set model; you also need to know who are the ~children@ of a given user, and the depth of their relationship; this is also fast.

蝶…霜飞 2024-11-13 18:12:50

祖先的分隔字符串

如果您强烈考虑使用 5 级关系表,则使用祖先的分隔字符串而不是 5 个单独的列可能会简化事情。

user_id  depth   ancestors
10       7       9,7,4,3,2,1
9        6       7,4,3,2,1
...
2        2       1
1        1       (empty string)

以下是您在此模型中使用的一些 SQL 命令:

添加用户 10 引用的用户 11

insert into ancestors_table (user_id, depth, ancestors)
select 11, depth+1, concat(10,',',ancestors)
from ancestors_table
where user_id=10;

查找用户 3 引用的所有用户。(请注意,此查询不能使用索引。)

select user_id
from ancestors_table
where ancestors like '%,3,%' or ancestors like '3,%' or ancestors like '%,3';

查找用户的祖先10. 您需要在客户端程序中分解字符串。在 Ruby 中,代码为ancestorscolumn.split(",").map{|x| x.to_i}。没有好的方法可以在 SQL 中分解字符串。

select ancestors from ancestors_table where user_id=10;

要查找用户 3 引用的每个级别的用户数量:

select
   depth-(select depth from ancestors_table where user_id=3),
   count(*)
from ancestors_table
where ancestors like '%,3,%' or ancestors like '3,%' or ancestors like '%,3'
group by depth;

您可以使用 like concat( 来避免这些查询的 like '%,3,%' 部分中的 SQL 注入攻击'%,', ?, ',%') 而是将用户号码的整数绑定到占位符。

Delimited String of Ancestors

If you're strongly considering the 5-level relationship table, it may simplify things to use a delimited string of ancestors instead of 5 separate columns.

user_id  depth   ancestors
10       7       9,7,4,3,2,1
9        6       7,4,3,2,1
...
2        2       1
1        1       (empty string)

Here are some SQL commands you'd use with this model:

To add user 11, referred by user 10

insert into ancestors_table (user_id, depth, ancestors)
select 11, depth+1, concat(10,',',ancestors)
from ancestors_table
where user_id=10;

To find all users referred by user 3. (Note that this query can't use an index.)

select user_id
from ancestors_table
where ancestors like '%,3,%' or ancestors like '3,%' or ancestors like '%,3';

To find the ancestors of user 10. You need to break up the string in your client program. In Ruby, the code would be ancestorscolumn.split(",").map{|x| x.to_i}. There's no good way to break up the string in SQL.

select ancestors from ancestors_table where user_id=10;

To find the number of users at each level, referred by user 3:

select
   depth-(select depth from ancestors_table where user_id=3),
   count(*)
from ancestors_table
where ancestors like '%,3,%' or ancestors like '3,%' or ancestors like '%,3'
group by depth;

You can avoid SQL injection attacks in the like '%,3,%' parts of these queries by using like concat('%,', ?, ',%') instead and binding the an integer for the user number to the placeholder.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文