2013-07-15 2 views
2

Я не могу обернуть голову вокруг небольшого (надеюсь) вопроса MySQL. У меня есть таблица, называемая ссылками. Он содержит поле customer_id и поле linked_id и в основном связывает счета клиентов друг с другом, в которых лидерство customer_id. Недавно созданные учетные записи могут создавать собственные учетные записи, и мне бы хотелось, чтобы все учетные записи были созданы зарегистрированным пользователем + все учетные записи, созданные субсчетами.MySQL получить связанные результаты из той же таблицы

Таблица выглядит следующим образом:

+----+-------------+-----------+ 
| id | customer_id | linked_id | 
+----+-------------+-----------+ 
| 1 |   1 |   5 | 
| 2 |   1 |   2 | 
| 3 |   1 |  11 | 
| 4 |   1 |  13 | 
| 5 |   13 |  14 | 
| 6 |   3 |   4 | 
| 7 |   7 |   8 | 
+----+-------------+-----------+ 

Так что, если я зарегистрирован как пользователь с Customer_ID 1, то я хотел бы получить список пользователей с linked_id 5,2,11,13 (потому что они являются прямыми соединение) и linked_id 14 (поскольку этот пользователь был создан пользователем, который напрямую связан с 1).

Запрос должен быть подзапросом, чтобы получить все данные пользователя. В настоящее время у меня есть:

SELECT username, firstname, lastname, email, active, level FROM customers WHERE id 
IN (SELECT linked_id FROM links WHERE customer_id=1) or id=1; 

Это, очевидно, только прямые прямые подключения и пользователя с id = 1 напрямую.

+0

Вы хотите видеть только «детей» и «внуков»? Или все потомки, независимо от глубины? – eggyal

+0

Я бы предпочел, чтобы все потомки были независимо от глубины, поэтому, если пользователь с идентификатором 14 создает новые дочерние элементы, они также должны быть перечислены под «основным» идентификатором с помощью 1 – lleto

+5

. Затем вам потребуется MySQL для прохождения вашей существующей модели данных, которая известный как «список смежности *», рекурсивно; однако, в отличие от некоторых других СУБД, MySQL не поддерживает рекурсивные функции. Если возможно, вы можете подумать о ремоделировании для использования другой структуры данных, такой как «* вложенные наборы *» или «* транзитивное закрытие *». – eggyal

ответ

1

Спасибо eggyal за то, что поместили меня на правильный путь. Увидев относительную сложность, мне уже не стыдно, что я не мог взломать ее в первый раз.

Я закончил исследование и нашел несколько полезных настроек для использования таблиц замыкания в mysql. Я закончил создание хранимой процедуры для заполнения таблицы закрытия и, конечно, новой таблицы cust_closure. Я переименовал таблицу ссылок в cust_links.

cust_links:

+-------------+---------+------+-----+---------+----------------+ 
| Field  | Type | Null | Key | Default | Extra   | 
+-------------+---------+------+-----+---------+----------------+ 
| id   | int(11) | NO | PRI | NULL | auto_increment | 
| customer_id | int(11) | YES |  | NULL |    | 
| linked_id | int(11) | YES |  | NULL |    | 
+-------------+---------+------+-----+---------+----------------+ 

cust_closure:

+-------------+---------+------+-----+---------+-------+ 
| Field  | Type | Null | Key | Default | Extra | 
+-------------+---------+------+-----+---------+-------+ 
| customer_id | int(11) | YES |  | NULL |  | 
| linked_id | int(11) | YES |  | NULL |  | 
| distance | int(11) | YES |  | NULL |  | 
+-------------+---------+------+-----+---------+-------+ 

А потом добавил хранимую процедуру:

CREATE PROCEDURE populate_cust_closure() 
BEGIN 
    DECLARE distance int; 
    TRUNCATE TABLE cust_closure; 
    SET distance = 0; 
    -- seed closure with self-pairs (distance 0) 
    INSERT INTO cust_closure (customer_id, linked_id, distance) 
    SELECT customer_id, customer_id, distance 
     FROM cust_links GROUP BY customer_id; 

    -- for each pair (root, leaf) in the closure, 
    -- add (root, leaf->child) from the base table 
    REPEAT 
    SET distance = distance + 1; 
    INSERT INTO cust_closure (customer_id, linked_id, distance) 
     SELECT cust_closure.customer_id, cust_links.linked_id, distance 
     FROM cust_closure, cust_links 
      WHERE cust_closure.linked_id = cust_links.customer_id 
      AND cust_closure.distance = distance - 1; 
    UNTIL ROW_COUNT()=0 
    END REPEAT; 
END // 

Когда я позвонила в хранимую процедуру ее получения d:

mysql> select * from cust_closure; 
+-------------+-----------+----------+ 
| customer_id | linked_id | distance | 
+-------------+-----------+----------+ 
|   1 |   1 |  0 | 
|   3 |   3 |  0 | 
|   7 |   7 |  0 | 
|   13 |  13 |  0 | 
|   1 |   5 |  0 | 
|   1 |   2 |  0 | 
|   1 |  11 |  0 | 
|   1 |  13 |  0 | 
|   13 |  14 |  0 | 
|   1 |  14 |  1 | 
|   3 |   4 |  0 | 
|   7 |   8 |  0 | 
+-------------+-----------+----------+ 

Так что теперь мой первоначальный запрос становится:

SELECT username, firstname, lastname, email, active, level FROM customers WHERE id 
IN (SELECT linked_id FROM cust_closure WHERE customer_id=1); 

Еще раз спасибо за eggyal и надеюсь, что это поможет кому-то в будущем.

Смежные вопросы