2

I've got a table structure like (simplified):

content

 - id_content
 - my_string1
 - ...

content_has_content

  - id_content
  - id_subcontent

topic_has_content

  - id_topic
  - id_content

Any topic can have multiple 'content', any 'content' can have multiple 'subcontent' (instance of content). With a given id_topic I'd like to recieve a list of all my_string1 from the linked contents, subcontents, subcontents-of-subcontents, and so on.

I understand "WITH" is not working for mysql but cannot find a nice recursive alternative.

Thanks daniel

2
  • "cannot find a nice recursive alternative" AFAIK MySQL has no recursion built in. Commented Apr 15, 2013 at 15:34
  • 2
    There is no recursive alternative in MySQL - try looking into Nested Sets instead: en.wikipedia.org/wiki/Nested_set_model Commented Apr 15, 2013 at 15:35

2 Answers 2

1

There is no recursion in MySQL and also the result you would get would be flat (no structure). The best way is still a while loop in PHP, Java or whatever programming language you use.

The query could look like this:

SELECT C.*, CHC.ID_SUBCONTENT
FROM CONTENT C
LEFT OUTER JOIN CONTENT_HAS_CONTENT CHC ON CHC.ID_CONTENT = C.ID_CONTENT
WHERE C.ID = ?
... // you get the idea

and in PHP you could repeat the query with the next sub_content_id, until ID_SUBCONTENT is null

Sign up to request clarification or add additional context in comments.

1 Comment

So I'll probably stick to a certain amount of joins inside the query, and limit nesting to this level. I don't want to start so many new queries from php, which would most probably be a lot slower.
0

Solution

PosgreSQL, Oracle, MS-SQL, ... have WITH RECURSIVE to handle such data structures. It internally uses a while loop to get all the parent ids for the current row (we need the child's here instead)

This can achieved in MySQL too, you can create a stored procedure and reproduce the same behavior

Assumed/DDL used

content_has_content has entries as

content_1
  content_2
    content_3
      content_4

I pass the id_content as 2 and get the output as

  content_2
    content_3
      content_4

All of the above are descendants of content_2

Fire call content_details(2); to get all the child rows for the passed id_content

SQL

###### Stored Routine
DELIMITER //  
drop procedure IF EXISTS `content_details`;
CREATE DEFINER=`root`@`localhost` PROCEDURE `content_details`(`_idcontent` INT)
    LANGUAGE SQL
    DETERMINISTIC
    CONTAINS SQL
    SQL SECURITY DEFINER
    COMMENT ''
BEGIN
DECLARE  temp_content_ids varchar(200);
DECLARE idcontent, idcontent2 integer;

SET temp_content_ids= _idcontent;
SET idcontent       = _idcontent;
SET idcontent2      = _idcontent;

WHILE idcontent2 IS NOT NULL DO 
    SET idcontent = NULL;
    SELECT id_subcontent, CONCAT(id_subcontent,',',temp_content_ids) INTO idcontent, temp_content_ids FROM content_has_content WHERE id_content = idcontent2;
    SET idcontent2 = idcontent;
END WHILE;  

SELECT my_string1 FROM content WHERE FIND_IN_SET( id, temp_content_ids );
END//

What I basically do is run a while loop until I have the last child_id, store these ids in a comma separated string format and then fire a query to get all the rows which have a id present in the var I just created

Note: There are chances you could be having invalid values within your tables, such as row having a id_subcontent, which points to its own id_content, which could cause a never ending loop within the procedure, to avoid such situations, you can use a counter and limit the nesting to say around 50 (or any value as per your requirement) and raise a exception if that limit is surpassed

Some data to play with..

###### DLL Statements
CREATE TABLE content
( id_content int,
my_string1 varchar(200));

CREATE TABLE content_has_content
( id_content int,
  id_subcontent  int);

CREATE TABLE  topic_has_content
( id_topic int,
 id_content int);


INSERT INTO content VALUES (1, 'content_1'), (2, 'content_2'), (3, 'content_3'), (4, 'content_4');
INSERT INTO content_has_content VALUES (1, 2), (2, 3), (3, 4);
INSERT INTO topic_has_content VALUES (1, 1);

Hope this helps..

2 Comments

Actually not only Postgres has recursive common table expresions (with recursive...) - all major DBMS except MySQL have it.
@a_horse_with_no_name I guessed Oracle too had it, but was not sure of others, thanks for letting me know this...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.