具有关系状态的SQL多对多

时间:2018-05-26 13:04:22

标签: php mysql sql

我遇到了这个问题:

实体有一个名字。实体可以属于多个父母。实体可以有多个孩子。儿童可以有多个孩子等。实体名称是唯一的。

提供一个解决方案,可以检索来自任何特定实体的所有父母,兄弟姐妹和孩子 - 但不是祖父母或孙子女,因此当前深度为+/- 1级。结果集中不返回给定的实体。

按名称对结果集进行排序,并且应清除关系状态(parent,sibling,child)。结果集应该支持分页。

可用的工具是PHP 7和MySQL 5.7

是的,这是大学的任务,你不是来做我的作业,但我非常困难,并且会喜欢指向正确的方向。

我决定制作2张表,样本数据如下:

CREATE TABLE `entities` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(200) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
);

CREATE TABLE `entity_relations` (
  `id_entity` int(11) unsigned NOT NULL,
  `id_parent` int(11) NOT NULL,
  UNIQUE KEY `id_parent` (`id_parent`,`id_entity`)
) ;

INSERT INTO `entities` (`id`, `name`)
VALUES
    (1,'Parent 1'),
    (2,'Parent 2'),
    (3,'Parent 3'),
    (4,'Parent 4'),
    (5,'Sibling 1'),
    (6,'Sibling 2'),
    (7,'Sibling 3'),
    (8,'Sibling 4'),
    (9,'Sibling 5'),
    (10,'Child 1'),
    (11,'Child 2'),
    (12,'Grandchild 1');

INSERT INTO `entity_relations` (`id_entity`, `id_parent`)
VALUES
    (1,0),
    (2,0),
    (3,0),
    (4,0),
    (5,1),
    (8,1),
    (5,2),
    (7,2),
    (6,3),
    (7,3),
    (9,3),
    (6,4),
    (10,5),
    (11,5),
    (12,10);

现在问题是获取所需结果集所需的查询。如果我搜索“兄弟1”作为我的实体名称,我应该得到以下数据:

Name | Relation
-----------------
child 1 | child
child 2 | child
parent 1 | parent
parent 2 | parent
sibling 3 | sibling
sibling 4 | sibling

由于要求声明它应该支持分页,我认为所有这些都应该在SQL中发生。我当然可以从PHP做出3个不同的查询,一个用于父项,一个用于兄弟,一个用于子项,在数组中合并并按名称排序,然后从该数组中取出一个子集,但这似乎不是是最有效的方式。所以我决定试一试SQL。

到目前为止,我提出了这个问题:

select e.*
from entities e 
join entity_relations er on er.id_entity = e.id
join entity_relations err on err.id_parent = e.id
where er.id_parent in ( -- children
    select e.id
    from entities e
    where e.name = "Sibling 1"
) 
or err.id_entity in ( -- parents
    select e.id
    from entities e
    where e.name = "Sibling 1"
)
order by e.name asc;

但那已经不起作用了,它只给了我父母而不是孩子。兄弟姐妹甚至还不在查询中。

有人能指出我正确的方向吗?我的桌子结构对此有用还是应该做一些完全不同的事情?

因为没有限制我可以使用它的MySQL工具意味着存储过程也在桌面上,这些会有用吗?

我真的想学习这一点,所以理解为什么要做出某些选择是我最想要的。

2 个答案:

答案 0 :(得分:1)

我会给你一个没有名字的方法:;

select e.*
from entities e
where e.id in (select er.id_parent
               from entity_relationships er
               where er.id_entity = ?
              )  or
      e.id in (select er.id_entity 
               from entity_relationships er
               where er.id_parent = ?
              );

?是实体 id 的占位符。

我会让你修改查询来处理名字。

Here是一个SQL小提琴。

答案 1 :(得分:-1)

好的,我现在得到了它,它给了我正确的结果。但我不禁想知道这是否是“正确”的做法。所有反馈都非常赞赏

编辑:现在删除了许多子选择并使用连接:

SELECT e.name AS name, 'parent' AS relation
FROM entities e
RIGHT JOIN entity_relations er ON er.id_parent = e.id
RIGHT JOIN entities origin ON origin.id = er.id_entity
WHERE origin.name = "Sibling 1"
UNION ALL
SELECT e.name AS name, 'child' AS relation
FROM entities e
RIGHT JOIN entity_relations er ON er.id_entity = e.id
RIGHT JOIN entities origin ON origin.id = er.id_parent
WHERE origin.name = "Sibling 1"
UNION ALL
SELECT e.name AS name, 'child' AS relation
FROM entities e
RIGHT JOIN entity_relations er on er.id_entity = e.id
WHERE er.id_parent IN (
    SELECT id_parent 
    FROM entity_relations er
    LEFT JOIN entities e ON er.id_entity = e.id
    WHERE e.name = "Sibling 1"
) 
AND e.name != "Sibling 1";
ORDER BY name ASC;

有关这是否是一种有效的方法的任何想法?如果没有,有什么可以改进的?

为了好奇;如果表格中存在沉浸式的行数和关系,这还能表现得好吗?数百万?

我当然明白,在这种情况下,可能会有比(My)SQL更好的选项,但如果没有?