I have a MySQL database that includes a table of components that are hierarchical (each non-top level component has a parent component)
It includes an SQL function GetParents(ComponentID)
, which returns a comma separated list of parent ComponentID
s, starting with the direct parent and going up. This is working correctly and as intended.
I copied that function to GetPath(ComponentID
and tried to get it to return the Title
instead of the ID, as well as changing the order so it starts with the top parent and going down, but got stuck. I managed to change the order, and the ,
to a ->
, as well as changing the top level ID (0) to (Top Level)
(except for top level assets!), but couldn't figure out how to get the titles instead of the IDs.
Unfortunately sqlfiddle doesn't like DELIMITER
(any alternatives?) but below is the code, including table schema and inserts that satisfy the following examples:
SELECT GetParents(11);
should return 4,1,0
. This is working correctly right now and I only include it for completeness.
SELECT GetPath(11);
currently returns (Top Level) -> 1 -> 4
, but I want to make it return (Top Level) -> Cars -> Car 1
DROP TABLE IF EXISTS `Components`;
CREATE TABLE `Components` (
`ComponentID` int(11) NOT NULL,
`ParentComponentID` int(11) NOT NULL,
`Title` varchar(250) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `Components`
ADD PRIMARY KEY (`ComponentID`);
ALTER TABLE `Components`
MODIFY `ComponentID` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;
INSERT INTO Components (ParentComponentID, Title) VALUES
(0, 'Cars'),(0,'Trucks'),(0,'Boats');
INSERT INTO Components (ParentComponentID, Title) VALUES
(1, 'Car 1'),(1,'Car 2'),(1,'Car 3'),(2,'Truck 1'),(3,'Boat 3');
INSERT INTO Components (ParentComponentID, Title) VALUES
(4, 'Wheel 1'),(4,'Wheel 2'),(4,'Wheel 3'),(4,'Wheel 4');
INSERT INTO Components (ParentComponentID, Title) VALUES
(5, 'Wheel 1'),(5,'Wheel 2'),(5,'Wheel 3'),(5,'Wheel 4');
-- Functions
DELIMITER $$
DROP FUNCTION IF EXISTS `GetParents` $$
CREATE FUNCTION `GetParents` (GivenID VARCHAR(1024)) RETURNS varchar(1024) CHARSET latin1
DETERMINISTIC
BEGIN
DECLARE rv,q,queue,queue_children,front_id VARCHAR(1024);
DECLARE queue_length,pos INT;
SET rv = '';
SET queue = GivenID;
SET queue_length = 1;
WHILE queue_length > 0 DO
SET front_id = queue;
IF queue_length = 1 THEN
SET queue = '';
ELSE
SET pos = LOCATE(',',queue) + 1;
SET q = SUBSTR(queue,pos);
SET queue = q;
END IF;
SET queue_length = queue_length - 1;
SELECT IFNULL(qc,'') INTO queue_children
FROM (SELECT GROUP_CONCAT(ParentComponentID) qc
FROM Components WHERE ComponentID = front_id AND ParentComponentID!=ComponentID) A;
IF LENGTH(queue_children) = 0 THEN
IF LENGTH(queue) = 0 THEN
SET queue_length = 0;
END IF;
ELSE
IF LENGTH(rv) = 0 THEN
SET rv = queue_children;
ELSE
SET rv = CONCAT(rv,',',queue_children);
END IF;
IF LENGTH(queue) = 0 THEN
SET queue = queue_children;
ELSE
SET queue = CONCAT(queue,',',queue_children);
END IF;
SET queue_length = LENGTH(queue) - LENGTH(REPLACE(queue,',','')) + 1;
END IF;
END WHILE;
RETURN rv;
END $$
DROP FUNCTION IF EXISTS `GetPath` $$
CREATE FUNCTION `GetPath` (GivenID VARCHAR(1024)) RETURNS varchar(1024) CHARSET latin1
DETERMINISTIC
BEGIN
DECLARE rv,q,queue,queue_children,front_id VARCHAR(1024);
DECLARE queue_length,pos INT;
SET rv = '';
SET queue = GivenID;
SET queue_length = 1;
WHILE queue_length > 0 DO
SET front_id = queue;
IF queue_length = 1 THEN
SET queue = '';
ELSE
SET pos = LOCATE(',',queue) + 1;
SET q = SUBSTR(queue,pos);
SET queue = q;
END IF;
SET queue_length = queue_length - 1;
SELECT IFNULL(qc,'') INTO queue_children
FROM (SELECT GROUP_CONCAT(ParentComponentID) qc
FROM Components WHERE ComponentID = front_id AND ParentComponentID!=ComponentID) A;
IF LENGTH(queue_children) = 0 THEN
IF LENGTH(queue) = 0 THEN
SET queue_length = 0;
END IF;
ELSE
IF LENGTH(rv) = 0 THEN
SET rv = queue_children;
ELSE
IF (queue_children = 0) THEN
SET rv = CONCAT('(Top Level) -> ',rv);
ELSE
SET rv = CONCAT(queue_children,' -> ',rv);
END IF;
END IF;
IF LENGTH(queue) = 0 THEN
SET queue = queue_children;
ELSE
SET queue = CONCAT(queue,',',queue_children);
END IF;
SET queue_length = LENGTH(queue) - LENGTH(REPLACE(queue,',','')) + 1;
END IF;
END WHILE;
RETURN rv;
END $$
DELIMITER ;
The function will be called in the context of SELECT getPath(ComponentID) FROM Components
, a table which could have many entries, so it would be good to have an efficient algorithm!
Thanks!