Хранение иерархических данных (MySQL) для реферального маркетинга - PullRequest
5 голосов
/ 13 мая 2011

Мне нужно иметь иерархию из 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

Это хорошие идеи?

Я знаю о «модели списка смежности» и «модифицированном алгоритме обхода дерева предзаказов», но являются ли эти хорошие решения для «реферальной» системы?

Запросы, которые мне нужно выполнить для этого дерева:

  • частое добавление новых пользователей
  • когда пользователь что-то покупает, его рефералы получают процентную комиссию
  • каждый пользователь должен иметь возможность узнать, сколько человек они указали (и сколько людей было указано людьми, которых они называли ....) на каждом уровне

Ответы [ 4 ]

8 голосов
/ 16 мая 2011

Закрытие стола

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

Чтобы добавить пользователя 10, по ссылке пользователя 3. (Я не думаю вам нужно заблокировать таблицу между этими двумя вставками):

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;

Недостатком этого метода является объем памяти, который займет эта таблица.

4 голосов
/ 16 мая 2011

Используйте механизм хранения OQGRAPH.

Вы, вероятно, хотите отслеживать произвольное количество уровней, а не только 5 уровней. Получите одну из вилок MySQL, которая поддерживает механизм QGRAPH (например, 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;
2 голосов
/ 15 мая 2011

Управление иерархическими данными в MySQL

В общем, мне нравится "вложенный набор", особенно. в MySQL, который на самом деле не имеет языковой поддержки для иерархических данных. Это быстро, но вам нужно убедиться, что ваши разработчики прочитали эту статью, если простота обслуживания важна. Он очень гибкий - что, кажется, не имеет большого значения в вашем случае.

Кажется, это хорошо подходит для вашей задачи - в модели рефералов вам нужно найти дерево рефералов, которое является быстрым в модели с вложенными множествами; вам также необходимо знать, кто является ~ детьми @ данного пользователя, и глубину их отношений; это тоже быстро.

1 голос
/ 16 мая 2011

Строка предков с разделителями

Если вы строго рассматриваете 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, которые вы бы использовали с этой моделью:

Чтобы добавить пользователя 11, по ссылке пользователя 10

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;

Вы можете избежать внедрения SQLатакует на части like '%,3,%' этих запросов, используя вместо этого like concat('%,', ?, ',%') и привязывая целое число для номера пользователя к заполнителю.

...