简体   繁体   中英

MySQL: How to do a match all on linked items with our new table structure?

We're migrating from seperate relational tables for Many-To-Many, One-To-Many and Many-To-One relations to one single relational table which can be read bidirectional.

We've converted almost all of the code and it seems to work perfectly, except for the GROUP BY HAVING COUNT as we do have 2 different columns in the new situation to check (instead of 1 in the old situation) and we don't know how to do this correctly.

Old tables:

`news`:
id | ...
1  | ...
2  | ...
3  | ...

`news_link`:
news_id | link_module | link_id
1       | category    | 1
1       | category    | 3
2       | category    | 1
3       | category    | 3

Old query:

SELECT * FROM `news` 
JOIN `news_link` 
    ON `news_link`.`news_id` = `news`.`id` 
    AND `news_link`.`link_id` IN (1,3) 
    AND `news_link`.`link_module` = 'category' 
GROUP BY `news`.`id` 
HAVING COUNT(DISTINCT `news_link`.`link_id`) = 2;

Old result:

`news`:
id | ...
1  | ...

Current tables:

`news`:
id | ...
1  | ...
2  | ...
3  | ...

`link`:
source_module | source_id | destination_module | destination_id
news          | 1         | category           | 1
news          | 1         | category           | 3
news          | 2         | category           | 1
news          | 3         | category           | 3

Current query:

SELECT * FROM `news` 
WHERE `id` IN (
    SELECT `link`.`source_id` AS `link_id`
    FROM `link`
    WHERE (`link`.`destination_module` = 'category' AND `link`.`destination_id` IN (1,3) AND `link`.`source_module` = 'news')
    UNION
    SELECT `link`.`destination_id` AS `link_id`
    FROM `link`
    WHERE (`link`.`source_module` = 'category' AND `link`.`source_id` IN (1,3) AND `link`.`destination_module` = 'news')
);

Current result:

`news`:
id | ...
1  | ...
2  | ...
3  | ...

At this point we're missing the GROUP BY HAVING COUNT which we did use to do a match all. For example, we only want to retrieve the news items which are linked to category 1 as well as 3. Not if they are only linked to category 1 or category 3.

What we've already tried:

@ids = SELECT `link`.`source_id` AS `link_id`
    FROM `link`
    WHERE (`link`.`destination_module` = 'category' AND `link`.`destination_id` IN (1,3) AND `link`.`source_module` = 'news')
    UNION
    SELECT `link`.`destination_id` AS `link_id`
    FROM `link`
    WHERE (`link`.`source_module` = 'category' AND `link`.`source_id` IN (1,3) AND `link`.`destination_module` = 'news';

SELECT * FROM `news` 
WHERE `id` IN (@ids)
AND COUNT(@ids) = 2;

With this solution we get back news ids 1,2,3 while we're checking for the category ids 1,3, so we get a AND 3 = 2, so we get no items back and have an invalid comparison as we're not comparing the amount of news items which are linked to both categories, but the amount of news items which are linked to either one of the categories.

Another try:

@ids = SELECT `link`.`source_id` AS `link_id`
    FROM `link`
    WHERE (`link`.`destination_module` = 'category' AND `link`.`destination_id` IN (1,3) AND `link`.`source_module` = 'news')
    UNION
    SELECT `link`.`destination_id` AS `link_id`
    FROM `link`
    WHERE (`link`.`source_module` = 'category' AND `link`.`source_id` IN (1,3) AND `link`.`destination_module` = 'news')
HAVING COUNT(DISTINCT `link`.`link_id`) = 2;

Error: Unknown column 'link.link_id' in 'having clause'

SELECT * FROM `news` 
WHERE `id` IN (@ids);

Has anyone an idea how to make it possible to convert the old query to the new table and having the same result as in the old situation?

EDIT:

Our goal

Our goal is like in the examples below to have code which retrieves the related modules from the link table for a single entity.

$News = $EntityManager->getRepository("News")->fetch(1);
var_dump($News->getLinks("Category"));

with output:

array(2) { [0]=> object(Category)#1 (2) { ["id"]=> int(1) ["title"]=> string(4) "Test" } [1]=> object(Category)#2 (2) { ["id"]=> int(3) ["title"]=> string(6) "Test 3" } }

and

$Category = $EntityManager->getRepository("Category")->fetch(1);
var_dump($Category->getLinks("News"));

with output:

array(2) { [0]=> object(News)#1 (2) { ["id"]=> int(1) ["title"]=> string(4) "News" } [1]=> object(News)#2 (2) { ["id"]=> int(2) ["title"]=> string(6) "News 2" } }

but also:

$Form = $EntityManager->getRepository("Form")->fetch(1);
var_dump($Form->getLinks("Field"));

with output:

array(2) { [0]=> object(Field)#1 (2) { ["id"]=> int(22) ["title"]=> string(8) "Field 22" } [1]=> object(Field)#2 (2) { ["id"]=> int(23) ["title"]=> string(8) "Field 23" } }

I really don't get what you try to achieve

SELECT * FROM `news` 
JOIN `link` 
    ON `link`.`source_id` = `news`.`id` 
    AND `link`.`destination_id` IN (1,3) 
    AND `link`.`destination_module` = 'category' 
GROUP BY `news`.`id` 
HAVING COUNT(DISTINCT `link`.`destination_id`) = 2;

Your query is the almost the same.

your queries are not completely logical as link . source_module = 'category' doesn't exist. But you can go from there an make you way to the wanted result.

For example add

link . source_module = 'news'

sample

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM