如果外键不为空,左连接多个表 - mysql 优化
Left join multiple tables if foreign key is not null - mysql optimization
有一些类似的问题,但没有一个符合我的情况。
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
我有通知 table 这种结构
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个tables,从table1到table5,具有这些结构(其他相同:我设置这个是为了测试,不确定是否重要,但是那些 tables (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 是 tables 的外键:table1 - table5 具有一对多关系。
我应该 select 基于 user_id
的通知。根据通知类型,适当的 table*_id 有一些值,其他 foreign_keys 为空(顺便说一下,有 2 个甚至 3 个 table *_id
可以不同于 null 的通知类型)。最初的想法是通过使用 CASE,WHEN,如果外键具有不同于 null 的某些值,则有一个只连接那些 tables 的查询,但是正如我从这个问题的答案中了解到的那样,
MySQL query where JOIN depends on CASE
在这种情况下不能使用。
表table1-table5会比较大,有几百万或几千万条记录。因此,如果外键为空,我不希望加入额外的 2-4 tables。另外,我不认为将查询分为两个主要部分会更好,例如 - 首先获取通知,然后在循环中查找相关的 tables' 值。
所以,重点是只加入 table 那些 table*_id
不是空的,如果它可以在 mysql 中完成的话。
主要问题是实现此目的的最有效方法是什么 - 获取通知信息及其相关 tables 数据。
连接到所有 table 的一般查询是通常的左连接,像这样
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 和数据
http://sqlfiddle.com/#!2/3bf8f/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;
而且它应该比每次都调用查询更快。
如你所见,写起来也短多了。
使用单个 ID 作为外键,然后使用 table 要查询的列是否更有意义:
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 which table查询您需要的实际数据。
SELECT `table_id`,`table_name` FROM `notifications`;
SELECT * FROM @table_name WHERE `id`=@table_id;
在这种情况下不需要昂贵的 LEFT JOIN,并且两个查询(或作为存储过程的复合查询)将不需要外键上的大索引,从而简化构造。它还具有可扩展性的优势,例如,如果您需要第 6 个、第 7 个或第 100 个分区怎么办 table?
我看你是多虑了。 MySQL 将按原样处理您的查询,无需您做任何更多努力。
您说:
I would not prefer to join extra 2-4 tables if foreign keys are null.
好消息:MySQL不会。
会看到notifications
table中key为null,看到你加入的对应table中没有记录,然后继续.我什至不确定你想象的它可能会尝试做你正在尝试优化的事情,但你的查询已经按原样进行了优化。
如果您已经 运行 此查询并且遇到性能问题,则您的问题可能出现在其他地方。请在这种情况下提供更多信息。特别是,您的 // other fields
行实际上可能比您想象的影响更大,具体取决于其他字段所在的位置。
有一些类似的问题,但没有一个符合我的情况。
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
我有通知 table 这种结构
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个tables,从table1到table5,具有这些结构(其他相同:我设置这个是为了测试,不确定是否重要,但是那些 tables (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 是 tables 的外键:table1 - table5 具有一对多关系。
我应该 select 基于 user_id
的通知。根据通知类型,适当的 table*_id 有一些值,其他 foreign_keys 为空(顺便说一下,有 2 个甚至 3 个 table *_id
可以不同于 null 的通知类型)。最初的想法是通过使用 CASE,WHEN,如果外键具有不同于 null 的某些值,则有一个只连接那些 tables 的查询,但是正如我从这个问题的答案中了解到的那样,
MySQL query where JOIN depends on CASE
在这种情况下不能使用。
表table1-table5会比较大,有几百万或几千万条记录。因此,如果外键为空,我不希望加入额外的 2-4 tables。另外,我不认为将查询分为两个主要部分会更好,例如 - 首先获取通知,然后在循环中查找相关的 tables' 值。
所以,重点是只加入 table 那些 table*_id
不是空的,如果它可以在 mysql 中完成的话。
主要问题是实现此目的的最有效方法是什么 - 获取通知信息及其相关 tables 数据。
连接到所有 table 的一般查询是通常的左连接,像这样
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 和数据 http://sqlfiddle.com/#!2/3bf8f/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;
而且它应该比每次都调用查询更快。
如你所见,写起来也短多了。
使用单个 ID 作为外键,然后使用 table 要查询的列是否更有意义:
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 which table查询您需要的实际数据。
SELECT `table_id`,`table_name` FROM `notifications`;
SELECT * FROM @table_name WHERE `id`=@table_id;
在这种情况下不需要昂贵的 LEFT JOIN,并且两个查询(或作为存储过程的复合查询)将不需要外键上的大索引,从而简化构造。它还具有可扩展性的优势,例如,如果您需要第 6 个、第 7 个或第 100 个分区怎么办 table?
我看你是多虑了。 MySQL 将按原样处理您的查询,无需您做任何更多努力。
您说:
I would not prefer to join extra 2-4 tables if foreign keys are null.
好消息:MySQL不会。
会看到notifications
table中key为null,看到你加入的对应table中没有记录,然后继续.我什至不确定你想象的它可能会尝试做你正在尝试优化的事情,但你的查询已经按原样进行了优化。
如果您已经 运行 此查询并且遇到性能问题,则您的问题可能出现在其他地方。请在这种情况下提供更多信息。特别是,您的 // other fields
行实际上可能比您想象的影响更大,具体取决于其他字段所在的位置。