我有3张桌子:
ITEMS ITEM_FILES_MAP FILES
id id id
name item_id filename
in_trash file_id
FILES通过ITEM_FILES_MAP表与ITEMS建立了一对多的关系。
我需要一个select查询,它通过以下标准返回文件列表:
示例:
ITEMS
id name in_trash
1 Item A 0
2 Item B 0
3 Item C 1
4 Item D 1
FILES
id filename
1 File A
2 File B
3 File C
4 File D
5 File E
ITEM_FILES_MAP
id item_id file_id
1 1 2
2 1 3
3 2 1
4 3 2
5 3 4
6 4 3
7 4 4
期望的结果: 返回文件D(id 4)。
文件B,C和D(FILES表中的id 2,3,4)将被返回,但由于文件B和C与in_trash = 0的项目相关,因此不会列出它们。
如果您想测试解决方案,这是一个示例转储:
CREATE TABLE `files` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`filename` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `files` (`id`, `filename`)
VALUES
(1,'File A'),
(2,'File B'),
(3,'File C'),
(4,'File D'),
(5,'File E');
CREATE TABLE `item_files_map` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`item_id` int(11) DEFAULT NULL,
`file_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `item_files_map` (`id`, `item_id`, `file_id`)
VALUES
(1,1,2),
(2,1,3),
(3,2,1),
(4,3,2),
(5,3,4),
(6,4,3),
(7,4,4);
CREATE TABLE `items` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`in_trash` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `items` (`id`, `name`, `in_trash`)
VALUES
(1,'Item A',0),
(2,'Item B',0),
(3,'Item C',1),
(4,'Item D',1);
答案 0 :(得分:0)
我没有在mysql中测试,但你可以这样做:
SELECT filename FROM
(SELECT filename, sum(in_trash) AS s, count(*) AS c
FROM items, files, item_files_map
WHERE items.id = item_files_map.item_id AND files.id = item_files_map.file_id
GROUP BY filename) sub
WHERE s = c
子查询为每个文件名计算引用它的项目数和垃圾箱中的项目数。对于您的示例,它返回:
"D" 2 2
"B" 1 2
"C" 1 2
"A" 0 1
如果这些计数相同,则仅在垃圾项目参考中。
编辑:按照axiac的建议,这是查询:
SELECT filename, files.id, sum(in_trash) AS s, count(*) AS c
FROM items, files, item_files_map
WHERE items.id = item_files_map.item_id AND files.id = item_files_map.file_id
GROUP BY files.id
HAVING s = c
答案 1 :(得分:0)
首先,确保您在表UNIQUE INDEX
上的字段item_id
和file_id
(按此顺序)中有item_files_map
。无论您运行什么查询,如果它包含此表,索引将使事物飞行而不是爬行。 但是,在某些查询中,具有相反顺序的字段的索引会有所帮助,但对于此任务,我们需要按所示顺序使用它们。
ALTER TABLE item_files_map
ADD UNIQUE INDEX item_file_id(`item_id`, `file_id`);
另外,请确保INDEX
items
in_trash
上有ALTER TABLE items
ADD INDEX (`in_trash`);
。
1
对于大型表格,如果0
和in_trash=1
值之间的比率介于0.05和20之间,则MySQL可能会忽略它(如果没有使用的值小于5%的行。)
可能具有in_trash=0
的项目比具有items
的项目要少得多(反之亦然),这将说服MySQL使用该表PK
的一个实例的索引因为索引从检查中删除了很多行。
更多,因为查询只使用此表中的字段in_trash
和# Query #1
SELECT DISTINCT f.id, f.filename
FROM items iit1
INNER JOIN item_files_map ifm1 ON iit1.id = ifm1.item_id
INNER JOIN files f ON f.id = ifm1.file_id
WHERE iit1.in_trash = 1
AND ifm1.file_id NOT IN (
SELECT ff.id
FROM files ff
INNER JOIN item_files_map ifm0 ON ff.id = ifm0.file_id
INNER JOIN items iit0 ON iit0.id = ifm0.item_id
WHERE iit0.in_trash = 0
);
,MySQL将使用索引获取所需的信息,而不会读取表数据。由于索引小于表数据,因此从存储中读取较少的字节可以提高执行速度。
执行所需操作的查询是:
item_files_map
如果您完全确定表file_id
不包含孤立files
值(即id
列中找不到的值,则此查询不如可以获得的那样好并且可以改进。 1}}。files
)。这不应该在设计良好的应用程序上发生,数据库可以使用FOREIGN KEY constraints(仅限InnoDB)帮助您避免此类情况。
假设满足这个条件,我们可以从内部查询中删除表# Query #2
SELECT DISTINCT f.id, f.filename
FROM items iit1
INNER JOIN item_files_map ifm1 ON iit1.id = ifm1.item_id
INNER JOIN files f ON f.id = ifm1.file_id
WHERE iit1.in_trash = 1
AND ifm1.file_id NOT IN (
SELECT ifm0.file_id
FROM item_files_map ifm0
INNER JOIN items iit0 ON iit0.id = ifm0.item_id
WHERE iit0.in_trash = 0
);
,使其更简单,更快捷:
file
此查询将生成正确的结果。
可以通过仅选择id
。# Query #3
SELECT DISTINCT ifm1.file_id
FROM items iit1
INNER JOIN item_files_map ifm1 ON iit1.id = ifm1.item_id
WHERE iit1.in_trash = 1
AND ifm1.file_id NOT IN (
SELECT ifm0.file_id
FROM item_files_map ifm0
INNER JOIN items iit0 ON iit0.id = ifm0.item_id
WHERE iit0.in_trash = 0
);
来完成另一项优化,并暂时删除文件名,将运行另一个查询来获取它:
JOIN
您可以将最后 INNER JOIN items iit0 FORCE INDEX(PRIMARY) ON iit0.id = ifm0.item_id
更改为:
PK
强制MySQL使用files
进行该连接,但我不知道它是否会运行得更快。也许当桌子变得更大时。
此查询未选择文件名(因为它根本不访问files
表)。它可以很容易地获取(与表PK
中的其他字段一起使用,也可以使用从其他连接表中选择的字段),使用类似风的查询,因为它使用表# Query #3-extra
SELECT *
FROM files
WHERE id IN (1, 2, 3)
来获取它需要的行:
1, 2, 3
将Query #2
替换为上一个查询返回的文件ID列表。
对于大表,这两个查询的运行速度可能比Query #2
如上一节所述,Query #3
和file_id
假设item_files_map
表中没有孤立Query #3
条目。如果存在此类孤立条目file_id
可以返回无效的Query #3-extra
值,但它们将被{{1}}过滤掉,并且它返回的最终结果集将仅包含有效结果。