从加入中排除项目 table

Exclude items from join table

CREATE TABLE `files`
(
    `file_id`  bigint(20) unsigned             NOT NULL AUTO_INCREMENT,
    `filename` text COLLATE utf8mb4_unicode_ci NOT NULL,
    PRIMARY KEY (`file_id`)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_unicode_ci;

INSERT INTO `files` (`file_id`, `filename`)
VALUES (100, 'file_100.ext'),
       (101, 'file_101.ext');

CREATE TABLE `files_tags`
(
    `file_id` bigint(20) unsigned NOT NULL,
    `tag_id`  int(10) unsigned    NOT NULL,
    PRIMARY KEY (`file_id`, `tag_id`),
    CONSTRAINT `FK__files` FOREIGN KEY (`file_id`) REFERENCES `files` (`file_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_unicode_ci;

INSERT INTO `files_tags` (`file_id`, `tag_id`)
VALUES (100, 1),
       (100, 2),
       (100, 3),
       (101, 1),
       (101, 2);

SQL fiddle: http://www.sqlfiddle.com/#!9/b776ab/3

我有一个连接 table (files_tags),它列出了另一个 table(文件)的标签。我有以下查询,它适用于 select 所有标有标签 1 和 2 的文件:

SELECT f.filename
FROM files_tags ft0
         INNER JOIN files f ON ft0.file_id = f.file_id
         INNER JOIN files_tags ft1 ON f.file_id = ft1.file_id AND ft1.tag_id = 2
WHERE ft0.tag_id = 1;

我正在寻找 selects 文件标记为 1 和 2 但不是 3 的查询。我尝试使用此附加连接进行上述查询:

         INNER JOIN files_tags ft2 ON f.file_id = ft2.file_id AND ft1.tag_id = 3

但这最终只会返回 5 行(与 files_tags table 相同),这不是我想要的。 我对此的预期输出是单行 file_101.ext。获取标记为 1 和 2 但不是 3 的文件的正确查询是什么?

您可以使用exists/ not exists来检查相关记录是否存在

select  f.filename
from files f
where exists (
  select 1
  from files_tags
  where tag_id = 1
  and file_id = f.file_id
) and exists (
  select 1
  from files_tags
  where tag_id = 2
  and file_id = f.file_id
) and not exists (
  select 1
  from files_tags
  where tag_id = 3
  and file_id = f.file_id
)

demo

不确定这是否足够优雅,但应该退出性能。

SELECT f.filename
FROM files_tags ft0
     INNER JOIN files f ON ft0.file_id = f.file_id
     INNER JOIN files_tags ft1 ON f.file_id = ft1.file_id AND ft1.tag_id = 2
     LEFT JOIN files_tags ft2 on f.file_id = ft2.file_id and ft2.tag_id = 3
 WHERE ft0.tag_id = 1
 and ft2.file_id is null;