有一些类似的问题,但没有一个与我的情况相符。
SQL Optimization - Join different tables based on column value
How to JOIN on different tables based on column value
MySQL query to JOIN tables based on column values
MySQL: Use CASE/ELSE value as join parameter
MySQL query where JOIN depends on CASE
我有这种结构的通知表
CREATE TABLE `notifications` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`notificaiton_type_id` int(11) DEFAULT NULL,
`table1_id` int(11) DEFAULT NULL,
`table2_id` int(11) DEFAULT NULL,
`table3_id` int(11) DEFAULT NULL,
`table4_id` int(11) DEFAULT NULL,
`table5_id` int(11) DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
`created` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `userIdIndex` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
和5个表,从table1到table5,具有这些结构(其他是相同的:我将其设置为测试,不确定它是否重要,但除了发布的字段之外的那些表(1到5)还有其他字段作为好吧,只是他们不参与查询,所以为了简单起见我只是跳过了它们)
CREATE TABLE `table1` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(300) COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
table * _id是表的外键:table1 - table5,具有一对多的关系。
我应该根据user_id
选择通知。根据通知类型,适当的表* _id有一些值,其他foreign_keys为null(顺便说一下,通知类型为2甚至3 table *_id
可以与null不同)。最初的想法是有一个只能连接那些表的查询,如果外键通过使用CASE,WHEN,它的某些值与null不同,但正如我从这个问题的答案中学到的那样,
MySQL query where JOIN depends on CASE
在这种情况下不能使用它。
表table1-table5将会相对较大,有数百万或数十万条记录。因此,如果外键为null,我不想加入额外的2-4个表。此外,我认为将查询分成两个主要部分并不是更好,例如 - 首先获取通知然后在循环中查找关联表的值。
所以,重点是只加入那些table*_id
不是null的表,如果可以在mysql中完成的话。
主要问题是实现这一目标的最有效方法 - 获取相关表格数据的通知信息。
连接到所有表的常规查询是通常的左连接,像这样的
EXPLAIN SELECT
n.`id`,
n.`user_id`,
n.`table1_id`,
n.`table2_id`,
n.`table3_id`,
n.`table4_id`,
n.`table5_id`
// other fields
FROM
notifications AS n
LEFT JOIN table1 AS t1
ON t1.`id` = n.`table1_id`
LEFT JOIN table2 AS t2
ON t2.`id` = n.`table2_id`
LEFT JOIN table3 AS t3
ON t3.`id` = n.`table3_id`
LEFT JOIN table4 AS t4
ON t4.`id` = n.`table4_id`
LEFT JOIN table5 AS t5
ON t5.`id` = n.`table5_id`
WHERE user_id = 5
这里是sql fiddle with data http://sqlfiddle.com/#!2/3bf8f/1/0
由于
答案 0 :(得分:1)
我认为你无所畏惧。 MySQL会照原样处理您的查询,而不需要您的任何努力。
你说:
如果外键为空,我不想加入额外的2-4个表。
好消息:MySQL不会。
在notifications
表中会看到该键为空,看到您要加入的相应表中没有记录,然后继续前进。我甚至不确定你想象的可能会尝试优化,但你的查询已经过优化了。
如果您已经在运行此查询并遇到性能问题,那么您可能会在其他地方发布问题。在这种情况下请提供更多信息。特别是,您的// other fields
行实际上可能会比您想象的更多地影响事物,具体取决于其他字段的位置。
答案 1 :(得分:0)
为什么不将VIEW用于此左连接查询?
以下是有关View性能的更多信息:Is a view faster than a simple query?
假设您的查询工作正常,您可以从中创建视图:
CREATE VIEW view_myView AS
SELECT
n.`id`,
n.`user_id`,
n.`table1_id`,
n.`table2_id`,
n.`table3_id`,
n.`table4_id`,
n.`table5_id`
FROM
notifications AS n
LEFT JOIN table1 AS t1
ON t1.`id` = n.`table1_id`
LEFT JOIN table2 AS t2
ON t2.`id` = n.`table2_id`
LEFT JOIN table3 AS t3
ON t3.`id` = n.`table3_id`
LEFT JOIN table4 AS t4
ON t4.`id` = n.`table4_id`
LEFT JOIN table5 AS t5
ON t5.`id` = n.`table5_id`
WHERE user_id = 5
然后您只需通过以下方式访问此视图中的数据:
SELECT * FROM view_myView;
它应该比每次调用查询更快。
如你所见,写作也要短得多。
答案 2 :(得分:0)
使用单个ID作为外键然后使用要查询的表的列是不是更有意义:
CREATE TABLE `notifications` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`notification_type_id` int(11) DEFAULT NULL,
`table_id` int(11) DEFAULT NULL,
`table_name` VARCHAR(10) DEFAULT NULL
...
然后,您可以选择要查询的实际数据表。
SELECT `table_id`,`table_name` FROM `notifications`;
SELECT * FROM @table_name WHERE `id`=@table_id;
在这种情况下,不需要昂贵的LEFT JOIN,并且两个查询(或复合查询作为存储过程)将否定对外键的大索引的需要,从而简化构造。它还具有可扩展性的优势,例如,如果您需要第6个,第7个或第100个分区表,该怎么办?