Reputation: 684
I can't wrap my head around a small (hopefully) MySQL question. I have a table called links. It contains a customer_id field and a linked_id field and basically links customer accounts to each other where customer_id is in the lead. The newly created accounts can spawn accounts themselves and I would like to see all accounts that were created by the logged on user + all the accounts created by subaccounts.
Table looks like this:
+----+-------------+-----------+
| 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 |
+----+-------------+-----------+
So if I am logged in as user with customer_id 1 then I would like to get the userlist with linked_id 5,2,11,13 (because they are a direct connection) and linked_id 14 (because this user was created by a user who is directly connected to 1).
The query needs to be a subquery to get all user details. I currently have:
SELECT username, firstname, lastname, email, active, level FROM customers WHERE id
IN (SELECT linked_id FROM links WHERE customer_id=1) or id=1;
This obviously only returns the direct connections and the user with id=1 directly.
Upvotes: 2
Views: 99
Reputation: 684
Thanks to eggyal for putting me on the right track. Seeing the relative complexity I do not feel so ashamed anymore that I could not crack it in the first go.
I ended up doing some research and found some nice setups to used closure tables in mysql. I ended up creating a stored procedure to populate my closure table and of course the new table cust_closure. I renamed by links table to 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 | |
+-------------+---------+------+-----+---------+-------+
And then added the stored procedure:
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 //
When I then called the stored procedure it produced:
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 |
+-------------+-----------+----------+
So now my original query becomes:
SELECT username, firstname, lastname, email, active, level FROM customers WHERE id
IN (SELECT linked_id FROM cust_closure WHERE customer_id=1);
Thanks again for eggyal and hope this helps someone in the future.
Upvotes: 1