2011-04-14 209 views
19

我有6張桌子:需要幫助優化MySQL查詢

CREATE TABLE IF NOT EXISTS `sbpr_groups` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `name` varchar(255) DEFAULT NULL, 
    `active` tinyint(1) DEFAULT '0', 
    `dnd` tinyint(1) DEFAULT '0', 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=32 ; 

CREATE TABLE IF NOT EXISTS `sbpr_newsletter` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `created_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `from` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `mail` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `subject` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `body` text COLLATE utf8_unicode_ci, 
    `attach1` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `attach2` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    `attach3` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=14; 

CREATE TABLE IF NOT EXISTS `sbpr_news_groups` (
    `newsletter_id` int(11) NOT NULL, 
    `groups` int(11) NOT NULL, 
    KEY `fk_sbpr_news_groups_sbpr_newsletter1` (`newsletter_id`), 
    KEY `fk_sbpr_news_groups_sbpr_groups1` (`groups`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

CREATE TABLE IF NOT EXISTS `sbpr_news_recs` (
    `newsletter_id` int(11) NOT NULL, 
    `recipients` int(11) NOT NULL, 
    KEY `fk_sbpr_news_recs_sbpr_newsletter1` (`newsletter_id`), 
    KEY `fk_sbpr_news_recs_sbpr_recipients1` (`recipients`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

CREATE TABLE IF NOT EXISTS `sbpr_recipients` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `mail` varchar(160) DEFAULT NULL, 
    `date_reg` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 
    `active` tinyint(1) DEFAULT '0', 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=3008 ; 

CREATE TABLE IF NOT EXISTS `sbpr_rec_groups` (
    `rec_id` int(11) NOT NULL, 
    `group` int(11) NOT NULL, 
    KEY `fk_sbpr_rec_groups_sbpr_recipients` (`rec_id`), 
    KEY `fk_sbpr_rec_groups_sbpr_groups1` (`group`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; 

有了這個foregin鍵:

ALTER TABLE `sbpr_news_groups` 
    ADD CONSTRAINT `fk_sbpr_news_groups_sbpr_groups1` 
    FOREIGN KEY (`groups`) REFERENCES `sbpr_groups` (`id`) 
    ON DELETE CASCADE ON UPDATE NO ACTION, 
    ADD CONSTRAINT `fk_sbpr_news_groups_sbpr_newsletter1` 
    FOREIGN KEY (`newsletter_id`) REFERENCES `sbpr_newsletter` (`id`) 
    ON DELETE CASCADE ON UPDATE NO ACTION; 

ALTER TABLE `sbpr_news_recs` 
    ADD CONSTRAINT `fk_sbpr_news_recs_sbpr_newsletter1` 
    FOREIGN KEY (`newsletter_id`) REFERENCES `sbpr_newsletter` (`id`) 
    ON DELETE CASCADE ON UPDATE NO ACTION, 
    ADD CONSTRAINT `fk_sbpr_news_recs_sbpr_recipients1` 
    FOREIGN KEY (`recipients`) REFERENCES `sbpr_recipients` (`id`) 
    ON DELETE CASCADE ON UPDATE NO ACTION; 

ALTER TABLE `sbpr_rec_groups` 
    ADD CONSTRAINT `fk_sbpr_rec_groups_sbpr_groups1` 
    FOREIGN KEY (`group`) REFERENCES `sbpr_groups` (`id`) 
    ON DELETE CASCADE ON UPDATE NO ACTION, 
    ADD CONSTRAINT `fk_sbpr_rec_groups_sbpr_recipients` 
    FOREIGN KEY (`rec_id`) REFERENCES `sbpr_recipients` (`id`) 
    ON DELETE CASCADE ON UPDATE NO ACTION; 

視覺表的結構: enter image description here

我想選擇所有的r OWS從sbpr_newsletter表,並從sbpr_recipients其id規定在sbpr_news_recs或規定在sbpr_rec_groups depence上FKS添加到每個這些行的行數的。

Ex。我想要選擇所有收件人的通訊電子郵件都在sbpr_news_recs或存在於組中,至少在sbpr_rec_groups加上活躍收件人的數量。

我已經workinq SQL:

SELECT d.id, d.subject , d.created_date, 
    (SELECT count(*) FROM sbpr_recipients r 
     LEFT JOIN sbpr_news_recs nr ON nr.recipients = r.id 
     LEFT JOIN sbpr_rec_groups g ON g.rec_id = r.id 
     LEFT JOIN sbpr_news_groups ng ON ng.groups = g.group 
     WHERE nr.newsletter_id = d.id OR ng.newsletter_id = d.id) AS repicients, 

    (SELECT count(*) FROM sbpr_recipients r 
     LEFT JOIN sbpr_news_recs nr ON nr.recipients = r.id 
     LEFT JOIN sbpr_rec_groups g ON g.rec_id = r.id 
     LEFT JOIN sbpr_news_groups ng ON ng.groups = g.group 
     WHERE (nr.newsletter_id = d.id OR ng.newsletter_id = d.id) 
     AND r.active = 1) AS active_repicients 
FROM sbpr_newsletter d 
ORDER BY d.id ASC, d.id 

解釋這個SQL語句: ​​

問題: 我如何優化SQL?

+9

+1爲所有細節。希望更多的問題是這樣的。 – Wes 2011-04-14 07:40:18

+0

當你將'd.id ASC,d.id'的順序更改爲'd.id ASC'順序時,你的解釋是什麼樣的? – eisberg 2011-04-14 07:59:01

+0

@eisberg http://imm.io/4YVk – 2011-04-14 08:16:24

回答

12

只是方法來優化,兩個SELECT查詢轉移到JOIN子句 -

SELECT d.id 
    , d.subject 
    , d.created_date 
    , count(if(nr_newsletter_id is not null or ng_newsletter_id is not null, 1, null)) repicients 
    , count(if((nr_newsletter_id is not null or ng_newsletter_id is not null) and t.active = 1, 1, null)) active_repicients 
FROM 
    sbpr_newsletter d 
LEFT JOIN (
    SELECT nr.newsletter_id nr_newsletter_id 
     , ng.newsletter_id ng_newsletter_id 
     , r.active 
    FROM 
    sbpr_recipients r 
    LEFT JOIN sbpr_news_recs nr 
    ON nr.recipients = r.id 
    LEFT JOIN sbpr_rec_groups g 
    ON g.rec_id = r.id 
    LEFT JOIN sbpr_news_groups ng 
    ON ng.groups = g.group 
) t 
ON nr_newsletter_id = d.id OR ng_newsletter_id = d.id 
GROUP BY 
    d.id; 

我rewrited查詢了一下,它不是測試,而是嘗試。

+0

非常好!它比我的變體執行速度快兩倍。 – 2011-04-14 08:25:24

+1

對於SELECT左側JOIN中的+1非常好。 – Johan 2011-04-14 08:33:29

+0

我剛剛注意到您的查詢結果,而我的結果不同:http://imm.io/4YWf – 2011-04-14 08:39:17

0

你可以創建一個視圖,查詢代替 - 折衷是存儲,但應該大大從服務器刪除加載...

0

收件人的子查詢/ active_recipients運行兩次,每次返回3311條記錄,所以它作爲一種觀點值得定義。

否則,請在連接中使用的外鍵上定義索引。

+0

來自@Devart的查詢與索引同時執行,沒有它們,我是否還需要添加索引? – 2011-04-14 08:32:50