簡體   English   中英

如果外鍵不為空,則左連接多個表-MySQL優化

[英]Left join multiple tables if foreign key is not null - mysql optimization

有一些類似的問題,但沒有一個與我的情況相符。

SQL優化-根據列值連接不同的表

如何基於列值聯接不同的表

MySQL根據列值查詢聯接表

MySQL:使用CASE / ELSE值作為連接參數

MySQL查詢,其中JOIN取決於情況

https://dba.stackexchange.com/questions/53301/mysql-getting-result-using-3-tables-and-case-statements

我有具有這種結構的通知表

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的外鍵的值與空值不同,那么將只連接那些表的查詢,但是正如我從此問題的答案中學到的,

MySQL查詢,其中JOIN取決於情況

在這種情況下不能使用。

表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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM