2014-11-05 96 views
1

我正在製作一個抓取項目,以抓取物品,並通過不同的時間表查看它們的計數。計劃是用戶定義的時間段(日期),當腳本打算運行時。如何使用子查詢,WHERE IN和varchar比較字段來優化查詢?

表結構如下:

CREATE TABLE IF NOT EXISTS `stats` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `schedule_id` smallint(11) NOT NULL, 
    `type` smallint(11) NOT NULL, 
    `name` varchar(250) COLLATE utf8_unicode_ci NOT NULL, 
    `views` int(11) NOT NULL, 
    `updated_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 
    PRIMARY KEY (`id`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ; 

所有數據被存儲在表中的統計信息,並在以後將進行分析,以看在視圖的類型明智增長。

的數據會是這樣:

樣本集

enter image description here

刮擦是在週期完成,每個日程,預計有大約20K entries.The時間表可能是以每日或每週爲單位制作,因此在5-6個月內數據將增長到2-3百萬。

在這些數據上,我需要執行查詢來彙總出現在選定時間表範圍內的相同名稱。

例如:

我需要骨料相同的項目(名稱),它遇到多個計劃。如果選擇時間表1和2,則僅選擇兩個時間表下的項目。因此,這裏將是項目A項目B

這裏應該計算視圖的類型總和。

因此,對於時間表1:(修訂版)

SELECT COUNT(t.`type`) AS count, SUM(t.views) AS view_count 
FROM `stats` t 
INNER JOIN 
( 
    SELECT name,COUNT(name) as c FROM `stats` WHERE `schedule_id` IN (1,2) GROUP BY name HAVING c=2 
) t2 ON t2.`name` = t.`name` 
WHERE `schedule_id`=2 GROUP BY type 

enter image description here

這是我的預期的結果。

但我已閱讀使用子查詢,WHERE IN,varchar比較字段將無助於具有優化的查詢。如何可以優化此更好的性能。

爲同類型聚集的規則將如下所示:

1.Under時間表ID,有可能是與schedule_id,名稱和類型不同類型value.Combination相同的名字將不會被複制。

2.類型明智的聚合器 - 將每個類型下的值相加。

我正在做項目在Python -MySQL的刮目的和PHP的上市results.I想知道如何正確組織這個表以及查詢更好的性能。 請諮詢。

+0

對於初學者,我會製作一個字典表,其中包含id和相應的名稱,如'itemA',並且只存儲主表中的id。這肯定會加快查詢速度,並且如果您想要檢索名稱,您可以隨時通過ID加入字典表。 – 2014-11-05 08:44:41

+0

@ConsiderMe像ItemA這樣的名字只是一個虛擬值。在實際情況下,它不會將這個名稱帶入字典表中。如果varchar創建問題,是否可以將varchar名稱字符串轉換爲整數。 – Surabhil 2014-11-05 08:59:46

+0

與性能無關,但您的子查詢正在嘗試查找schedule_id爲1和2的記錄。但是,它也會匹配schedule_id 1兩次的任何記錄。如果這在邏輯上不可能發生,那麼你可以輕鬆地重寫它而不用子查詢。 – Kickstart 2014-11-05 10:21:46

回答

1

VARCHAR列

如說,在評論,這將是一個很好的做法,存放在字典表VARCHAR處理。爲什麼?它們需要比int4更多的空間,因此具有越來越大的表將只佔用更多空間,而每個名稱可以在另一個表中存儲一次。

查詢性能

WHERE IN實際上意味着策劃師就比較schedule_idANY'{1,2}'轉換爲integer[]類型,你可以在下面看到了。

子查詢

有時你無法避免子查詢,如果您需要彙總數據。考慮到這一點,請記住並非所有查詢都包含1 SELECT聲明。在現實中,他們確實很少(除非你有一個只有一個應用它與數據庫連接的小部分,例如像簡單的遊戲,你只需要存儲包含信息的用戶和分)

QUERY

在給定的樣本數據

您的查詢計劃:

select count(type), sum(views) from tmp_test8 a join (select name,count(1) from tmp_test8 where schedule_id in (1,2) group by 1 having count(1) = 2) b 
on a.name = b.name where schedule_id = 1; 

            QUERY PLAN         
------------------------------------------------------------------------------ 
Aggregate (cost=23.59..23.60 rows=1 width=8) 
    -> Nested Loop (cost=11.77..23.59 rows=1 width=8) 
     Join Filter: ((a.name)::text = (tmp_test8.name)::text) 
     -> Seq Scan on tmp_test8 a (cost=0.00..11.75 rows=1 width=524) 
       Filter: (schedule_id = 1) 
     -> HashAggregate (cost=11.77..11.79 rows=2 width=516) 
       Filter: (count(1) = 2) 
       -> Seq Scan on tmp_test8 (cost=0.00..11.75 rows=2 width=516) 
        Filter: (schedule_id = ANY ('{1,2}'::integer[])) 

雖然,你的查詢可以改寫無連接,因此它只能掃描表一次。這是我的建議:

select count, sum(view_count) 
from( 
    select name, count(1) count, sum(case when schedule_id = 1 then views end) view_count 
    from tmp_test8 
    where schedule_id in (1,2) 
    group by 1 
    having count(1) = 2 
    ) foo 
group by 1 
           QUERY PLAN        
------------------------------------------------------------------------ 
HashAggregate (cost=11.83..11.85 rows=2 width=16) 
    -> HashAggregate (cost=11.78..11.80 rows=2 width=524) 
     Filter: (count(1) = 2) 
     -> Seq Scan on tmp_test8 (cost=0.00..11.75 rows=2 width=524) 
       Filter: (schedule_id = ANY ('{1,2}'::integer[])) 

這兩個查詢都會產生相同的結果。

+0

謝謝你。但在你的第二個查詢中有一個子查詢,裏面有個案檢查。我相信它會用更大的數據集慢慢取得。 – Surabhil 2014-11-05 14:35:20

+0

@Surabhil對不起,但每個提供更大的數據集的查詢將取得更慢,更慢。這很明顯。你可以運行一個測試用例,但是不管你的數據樣本將包含多少行,查詢計劃將總是*(除非你像索引一樣改變查詢之外的東西)保持不變。 – 2014-11-05 14:47:57

+0

是的,我可以理解大數據的事情,但我認爲我們應該關注我們所有的基本優化。 – Surabhil 2014-11-05 15:04:59

1

繼續我的評論。

如果一個名稱可以有重複的日程安排ID,那麼您當前的查詢將不起作用。最簡單的修復方法是將其從COUNT(名稱)更改爲COUNT(DISTINCT schedule_id)

如果一個名稱,然後schedule_id是唯一的,那麼你可以爲每個計劃ID加入對統計一次,避免子查詢: -

SELECT COUNT(t.`type`) AS count, SUM(t.views) AS view_count 
FROM `stats` t 
INNER JOIN stats t1 ON t.name = t1.name AND t1.schedule_id = 1 
INNER JOIN stats t2 ON t.name = t2.name AND t2.schedule_id = 2 
WHERE t.schedule_id = 1 

這是使你的數據的一些假設。

雖然有時需要子查詢,但MySQL不會使用子查詢結果的索引將其與主表連接起來。

+0

我會假設名稱不會有重複的條目與'schedule_id'相同。你可以通過模式看到,'views'確實被更新了,'updated_time'每當它完成時都會被修改。 – 2014-11-05 10:34:52

+0

@ConsiderMe - 這將是我的希望,但不是確定的。如果名稱不能有相同schedule_id的重複條目,那麼它可以很容易地避免子查詢(此時包含名稱和schedule_id的索引可能會極大地幫助 - 或者更好,因爲您建議將名稱分開並使用其ID加入) – Kickstart 2014-11-05 10:40:17

+0

@Kickstart我已經更新了這個問題。 將COUNT(名稱)更改爲COUNT(DISTINCT schedule_id)// 在** COUNT **下使用** DISTINCT **是一種很好的做法?在我的情況下,可能會有多個姓名字段在一個時間表下使用不同的類型。 – Surabhil 2014-11-05 14:24:56