![](/img/trans.png)
[英]mysql left join with multiple tables,having only primary key as foreign key of first table
[英]Left join multiple tables if foreign key is not null - mysql optimization
有一些類似的問題,但沒有一個與我的情況相符。
我有具有這種結構的通知表
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
選擇通知。 根據通知類型,適當的table * _id具有一些值,其他foreign_keys為null(但有2個或什至3個table *_id
的通知類型可以不同於null)。 最初的想法是,如果外鍵通過使用CASE,WHEN的外鍵的值與空值不同,那么將只連接那些表的查詢,但是正如我從此問題的答案中學到的,
在這種情況下不能使用。
表table1-table5將相對較大,具有幾百萬或幾千萬個記錄。 因此,如果外鍵為空,我將不希望加入額外的2-4個表。 另外,我認為將查詢分為兩個主要部分不是更好,例如-首先獲取通知,然后在循環中查找關聯表的值。
因此,重點是僅連接那些table*_id
不為null的table*_id
如果可以在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小提琴http://sqlfiddle.com/#!2/3bf8f/1/0
謝謝
我想你什么都不擔心。 MySQL將按原樣處理您的查詢,而無需您花費更多精力。
您聲明:
如果外鍵為空,我不希望加入額外的2-4表。
好消息:MySQL不會。
它將看到notifications
表中的鍵為空,看到您要加入的對應表中沒有記錄,然后繼續前進。 我什至不確定您想像的是您正在嘗試去優化,但是您的查詢已經按原樣進行了優化。
如果您已經在運行此查詢並且遇到性能問題,則可能在其他地方發出該問題。 在這種情況下,請提供更多信息。 特別是, // other fields
行可能實際上對您的影響超出您的想象,具體取決於其他字段的位置。
為什么不對左連接查詢使用VIEW?
以下是有關View性能的更多信息: 視圖是否比簡單查詢快?
假設查詢工作正常,則可以從中創建視圖:
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作為外鍵而不是要查詢其表的列會更有意義:
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個分區表怎么辦?
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.