1

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 ComponentIDs, 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!

Ben Holness
  • 2,457
  • 3
  • 28
  • 49

0 Answers0