Mysql

可以優化此查詢還是已經優化?

  • July 18, 2015

我有三個表:default_users、default_profiles、default_friend。這是每個 SQL:

--
-- Table structure for table `default_users`
--
CREATE TABLE IF NOT EXISTS `default_users` (
 `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
 `email` varchar(60) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
 `password` varchar(100) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
 `salt` varchar(6) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
 `group_id` int(11) DEFAULT NULL,
 `ip_address` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL,
 `active` int(1) DEFAULT NULL,
 `activation_code` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL,
 `created_on` int(11) NOT NULL,
 `last_login` int(11) NOT NULL,
 `username` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
 `forgotten_password_code` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL,
 `remember_code` varchar(40) COLLATE utf8_unicode_ci DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `email` (`email`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci  AUTO_INCREMENT=16 ;

--
-- Table structure for table `default_profiles`
--

CREATE TABLE IF NOT EXISTS `default_profiles` (
 `id` int(9) NOT NULL AUTO_INCREMENT,
 `created` datetime DEFAULT NULL,
 `updated` datetime DEFAULT NULL,
 `created_by` int(11) DEFAULT NULL,
 `ordering_count` int(11) DEFAULT NULL,
 `user_id` int(11) unsigned NOT NULL,
 `display_name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
 `first_name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
 `last_name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
 `company` varchar(100) COLLATE utf8_unicode_ci DEFAULT NULL,
 `phone` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
 `address_line1` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
 `postcode` varchar(20) COLLATE utf8_unicode_ci DEFAULT NULL,
 `website` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
 `updated_on` int(11) unsigned DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `user_id` (`user_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=16 ;

--
-- Table structure for table `default_friend`
--

CREATE TABLE IF NOT EXISTS `default_friend` (
 `friend_id` mediumint(8) NOT NULL,
 `user_id` mediumint(8) unsigned NOT NULL,
 `is_subscriber` tinyint(1) NOT NULL DEFAULT '1',
 `privacy` tinyint(3) DEFAULT '0',
 `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
 `friend_list_id` smallint(5) DEFAULT NULL,
 `approved` tinyint(1) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

我正在為我的網站建構一個搜尋引擎,因此我需要找到所有使用者名或電子郵件類似於某些字元串的使用者,但還需要獲取這些使用者中的每個結果,這些使用者是我的朋友,這意味著結果 default_users.id 所在的位置friend.user_id 或friend.friend_id 因為我可以邀請其他人,但其他人也可以邀請我。我建立這個查詢:

SELECT 
     u.*
   , p.*
   , (
       SELECT f.approved
       FROM default_friend f
       WHERE (
               f.user_id = u.id
               AND f.friend_id = 1
           ) OR (
               f.friend_id = u.id
               AND f.user_id = 1
           ) 
       LIMIT 1
   ) AS approved
FROM 
     default_users u
   , default_profiles p
WHERE 
   (
       u.email LIKE '%some_string%'
       OR u.username LIKE '%some_string%'
   ) 
   AND u.id != 1
   AND p.user_id = u.id
GROUP BY u.id

對此優化有任何幫助嗎?我認為有點慢,每個表中將有大約 15 000 條記錄。

EXPLAIN計劃:

+----+--------------------+-------+--------+---------------+---------+---------+------------------------+------+---------------------------------+
| id | select_type        | table | type   | possible_keys | key     | key_len | ref                    | rows | Extra                           |
+----+--------------------+-------+--------+---------------+---------+---------+------------------------+------+---------------------------------+
|  1 | PRIMARY            | p     | ALL    | user_id       | NULL    | NULL    | NULL                   |   18 | Using temporary; Using filesort |
|  1 | PRIMARY            | u     | eq_ref | PRIMARY       | PRIMARY | 2       | comvivem_db1.p.user_id |    1 | Using where                     |
|  2 | DEPENDENT SUBQUERY | f     | ALL    | NULL          | NULL    | NULL    | NULL                   |    4 | Using where                     |
+----+--------------------+-------+--------+---------------+---------+---------+------------------------+------+---------------------------------+

我使用JOINLEFT JOIN修改了您的查詢:

SELECT 
   u.*,
   p.*,
   f.approved 
FROM test.default_user AS u
JOIN test.default_profile AS p ON (u.id>1 AND p.user_id = u.id)
LEFT JOIN test.default_friend AS f ON ((f.user_id = u.id AND f.friend_id = 1) OR (f.friend_id = u.id AND f.user_id = 1)) 
WHERE u.email LIKE '%some_string%' OR u.username LIKE '%some_string%'
GROUP BY u.id;

我不確定在 mysql 中,但我看到一堆導致 SQL Server 性能問題的東西。首先,相關的子查詢通常是性能殺手,因為通過在集合中插入的行進行痛苦的執行行。

接下來,不要在 where 子句中使用萬用字元作為第一個字元,因為它會阻止索引的使用。您的表格設計不正確,您需要這樣做(例如當您儲存逗號分隔列表時),或者您需要開始使用某種類型的全文搜尋。坦率地說,使用者沒有理由不能至少輸入電子郵件或使用者名的第一個字元,因此萬用字元可能根本不需要。查看您的要求,並確保您在此過程中沒有鍍金和損害系統。

接下來,任何生產程式碼都不應該使用 select * 尤其是當有連接時。通過這樣做,您已經重複了兩次連接欄位,這會浪費伺服器資源和網路資源。如果您實際上沒有使用所有其他列,那也是浪費。此外,我知道在 SQl 伺服器中查找列名時性能會受到影響,這對於 mysql 可能是正確的。無論如何,它是一個 SQl 反模式,就像使用隱式連接是一個 SQl 反模式一樣。維護也很危險,因為添加到表中的內容不應自動添加到查詢中。您最終可能會在錯誤的位置顯示內容(如果有人重新排列了列)或返回您不使用的數據,甚至顯示您不希望使用者看到的某些欄位。這是一個非常糟糕的做法。

引用自:https://dba.stackexchange.com/questions/22367