多对多联接,没有重复的行

时间:2018-10-27 17:24:30

标签: mysql sql join

我有几个表具有多对多关系。 例如表格视频模式

CREATE TABLE `videos` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `description` varchar(10000) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=47606 DEFAULT CHARSET=utf8;

和两个表演员film_actors,例如作家和film_writers

演员和电影演员模式

CREATE TABLE `actors` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `actors_UN` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=152216 DEFAULT CHARSET=utf8;


CREATE TABLE `film_actors` (
  `actor_id` int(11) NOT NULL,
  `film_id` int(11) NOT NULL,
  PRIMARY KEY (`film_id`,`actor_id`),
  KEY `FKrs472oyyff3hfwq10pyo94k1d` (`actor_id`),
  CONSTRAINT `FK12uvap3je50qd8cq3s0jf7h7r` FOREIGN KEY (`film_id`) REFERENCES `videos` (`id`) ON DELETE CASCADE,
  CONSTRAINT `FKrs472oyyff3hfwq10pyo94k1d` FOREIGN KEY (`actor_id`) REFERENCES `actors` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

作家

CREATE TABLE `writers` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `writers_UN` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=39875 DEFAULT CHARSET=utf8;

CREATE TABLE `film_writers` (
  `film_id` int(11) NOT NULL,
  `writer_id` int(11) NOT NULL,
  PRIMARY KEY (`writer_id`,`film_id`),
  KEY `FKh0kfwnarp6utb4f80ycj1lap` (`film_id`),
  CONSTRAINT `FK4hwpb5l48m0xps6jqn1wyjb63` FOREIGN KEY (`writer_id`) REFERENCES `writers` (`id`) ON DELETE CASCADE,
  CONSTRAINT `FKh0kfwnarp6utb4f80ycj1lap` FOREIGN KEY (`film_id`) REFERENCES `videos` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

我尝试与演员和作家一起选择电影信息,但结果包含许多重复的行。我使用此sql查询

SELECT
    vid.id,
    vid.title,
    vid.original_title_name,
    vid.`year`,
    ac.id as actor_id,
    ac.name as actor_name,
    wr.id as writer_id,
    wr.name as writer_name
from
    (
    select
        *
    From
        videos v
    where
        v.id = 1722
    ) vid
JOIN film_actors fa ON
    vid.id = fa.film_id
JOIN actors ac ON
    fa.actor_id = ac.id
JOIN film_writers fw ON
    vid.id = fw.film_id
JOIN writers wr ON
    fw.writer_id = wr.id

输出是很多重复行。

id   |title           |original_title_name |year |actor_id |actor_name         |writer_id |writer_name    |
-----|----------------|--------------------|-----|---------|-------------------|----------|---------------|
1722 |Назад в будущее |Back to the Future  |1985 |1796     |Майкл Дж. Фокс     |319       |Боб Гейл       |
1722 |Назад в будущее |Back to the Future  |1985 |2648     |Джордж ДиЧенцо     |319       |Боб Гейл       |
1722 |Назад в будущее |Back to the Future  |1985 |4807     |Криспин Гловер     |319       |Боб Гейл       |
1722 |Назад в будущее |Back to the Future  |1985 |7601     |Кристофер Ллойд    |319       |Боб Гейл       |
1722 |Назад в будущее |Back to the Future  |1985 |8195     |Лиа Томпсон        |319       |Боб Гейл       |
1722 |Назад в будущее |Back to the Future  |1985 |8707     |Марк МакКлюр       |319       |Боб Гейл       |
1722 |Назад в будущее |Back to the Future  |1985 |9242     |Фрэнсис Ли МакКейн |319       |Боб Гейл       |
1722 |Назад в будущее |Back to the Future  |1985 |9602     |Уэнди Джо Спербер  |319       |Боб Гейл       |
1722 |Назад в будущее |Back to the Future  |1985 |10545    |Клаудия Уэллс      |319       |Боб Гейл       |
1722 |Назад в будущее |Back to the Future  |1985 |10546    |Томас Ф. Уилсон    |319       |Боб Гейл       |
1722 |Назад в будущее |Back to the Future  |1985 |1796     |Майкл Дж. Фокс     |320       |Роберт Земекис |
1722 |Назад в будущее |Back to the Future  |1985 |2648     |Джордж ДиЧенцо     |320       |Роберт Земекис |
1722 |Назад в будущее |Back to the Future  |1985 |4807     |Криспин Гловер     |320       |Роберт Земекис |
1722 |Назад в будущее |Back to the Future  |1985 |7601     |Кристофер Ллойд    |320       |Роберт Земекис |
1722 |Назад в будущее |Back to the Future  |1985 |8195     |Лиа Томпсон        |320       |Роберт Земекис |
1722 |Назад в будущее |Back to the Future  |1985 |8707     |Марк МакКлюр       |320       |Роберт Земекис |
1722 |Назад в будущее |Back to the Future  |1985 |9242     |Фрэнсис Ли МакКейн |320       |Роберт Земекис |

是否可以通过某种方式将结果转换为类似的内容?

id   |title           |original_title_name |year |actor_id |actor_name         |writer_id |writer_name    |
-----|----------------|--------------------|-----|---------|-------------------|----------|---------------|
1722 |Назад в будущее |Back to the Future  |1985 |1796     |Майкл Дж. Фокс     |319       |Боб Гейл       |
1722 |Назад в будущее |Back to the Future  |1985 |2648     |Джордж ДиЧенцо     |320       |Роберт Земекис |
1722 |Назад в будущее |Back to the Future  |1985 |4807     |Криспин Гловер     |          |               |
1722 |Назад в будущее |Back to the Future  |1985 |7601     |Кристофер Ллойд    |          |               |
1722 |Назад в будущее |Back to the Future  |1985 |8195     |Лиа Томпсон        |          |               |
1722 |Назад в будущее |Back to the Future  |1985 |8707     |Марк МакКлюр       |          |               |
1722 |Назад в будущее |Back to the Future  |1985 |9242     |Фрэнсис Ли МакКейн |          |               |
1722 |Назад в будущее |Back to the Future  |1985 |9602     |Уэнди Джо Спербер  |          |               |
1722 |Назад в будущее |Back to the Future  |1985 |10545    |Клаудия Уэллс      |          |               |
1722 |Назад в будущее |Back to the Future  |1985 |10546    |Томас Ф. Уилсон    |          |               |

2 个答案:

答案 0 :(得分:1)

之所以会出现“重复”行,是因为一部电影的演员很多,而一部电影的作家很多。电影演员和电影作家之间根本没有关系

用于教育目的的简单查询:

SELECT * 
FROM
  films 
  INNER JOIN actors on films.id = actors.filmid
  INNER JOIN writers on films.id = writers.filmid

为简化我的观点,它隐藏了表的复杂性,这些复杂性将多对多关系分解为多对一关系

现在。如果一部电影有10位演员和11位作家,则此查询将产生110行

每个演员都将与每个作家相关联

为什么?因为电影演员和电影作家之间绝对没有关系。这两件事根本没有关系。我们没有在JOIN条件下写任何东西来断言某个actor属性等于某个writer属性,因为我们不能-根本没有任何东西可以将它们两个关联在一起

结果数据库唯一可能做的就是生成一个行集,该行集为每个演员/作家组合都有一行。演员数据重复11次,作家数据重复10次

Actor1/Writer1
Actor1/Writer2
...
Actor2/Writer1
Actor2/Writer2
...
Actor10/Writer11

没有办法解决这个“问题”,这是由于试图将两个无关的东西放到一个查询中而引起的。唯一的“解决方案”是不要将它们放在同一查询中。如果您正在编写像IMDB这样的网站,并且您有一部电影的网页,其中有两个标签,一个用于演员,一个用于作家,请运行两个单独的查询(电影加入演员)和(电影加入作家)来填充数据每个分页-您无法在一个查询中完成*。

*当我说“不能”时,我的意思是“真的不应该”。您发布的“期望结果”网格将演员1796和作家319 毫无缘由地联系在一起-一行中的所有内容都应该是相关的,并且这两个实体除了某些任意对象外,不共享任何关系当ID升序排列时,决定两者均排名第一。尽管有多种方法可以用来关联它们并从查询中消除笛卡尔乘积,但这是一种可怕的代码味道,表明您实际上正在完全解决其他问题(我们看不到)。错误的方式(抱歉)

答案 1 :(得分:-1)

您可以在SELECT语句后使用关键字DISTINCT来启用对非重复行的选择。在这种情况下,将DISTINCT关键字放在第一个SELECT关键字之后应该可以解决您的问题。希望您的问题得到回答