2016-01-16 92 views
0

讓我通過說是,我意識到即使是初學者DBA也許應該知道這個問題的答案,但我從未接受過任何正式培訓並且找不到回答相當多的谷歌搜索後,所以請容易對我:)MySQL從多個結構上完全相同的表中選擇

我有一個數據庫包含88個相同(在結構,而不是數據)表總共20465行。我正在尋找一種方式來聚合這些,所以我可以:

SELECT * FROM [aggregate] WHERE id = 'some unique value'; 

我想出了(工作,但很慢)的解決方案是通過select *從每個表創建一個視圖,然後union在一起,但它在進行搜索時顯而易見,這不是正確的方法。例如,選擇~200個記錄需要一分鐘。

這似乎並不是連接的用例,因爲這些表彼此沒有關係,它們只是包含相同類型的數據。

我覺得索引是我在找的東西,但我不確定是否應該索引視圖(我的谷歌搜索似乎表明這不可能?),或者如果我可能不瞭解索引正常。

任何提示在正確的方向將不勝感激! (即使它只是一些文檔的鏈接)。

+4

我的第一個問題是,「爲什麼你有那麼多相同的桌子?」 –

+2

你的看法是否真的使用'union'?既然你應該允許使用'union all'的表格之間的重複,這也應該是一個巨大的速度提升。 – Mureinik

+0

創建視圖定義作爲問題的解決方案通常會引入比原始問題更大的問題。這不是要跳過「觀點不好」的風潮,而是創建和使用視圖*而不去理解*如何在MySQL中處理這些問題會導致*顯着的性能問題。 (我在答案中更詳細地解決了這個問題。) – spencer7593

回答

0

評論者是正確的。使用UNION ALL而不是UNIONUNION ALL不會嘗試刪除重複數據行,其中UNION會執行重複數據刪除操作。重複數據刪除是很多工作,即使沒有任何重複。

您需要使用一系列UNION ALL操作將所有這些表視爲一個表。這就是你如何做到的。

如果是我我會一次運行此查詢:

CREATE new_table AS 
      SELECT 1 source, * FROM table1 
    UNION ALL SELECT 2 source, * FROM table2 
    UNION ALL SELECT 3 source, * FROM table3 
     etc etc ad nauseam 
    UNION ALL SELECT 88 source, * FROM table88 

然後使用new_table所有未來的工作。那之後我會放棄88張桌子。

0

它是而不是可能爲視圖定義聲明一個索引。 (谷歌是對的)。

但是,您可以在88個表中的每一個上添加一個索引。創建哪些索引確實取決於數據分佈,基數以及最重要的是針對這些表運行的查詢。索引不是一個銀彈。對於某些查詢模式,沒有索引可以幫助。所以,在我們開始創建索引...


觀點在MySQL中是如何工作的通知發現性能問題

學習引用視圖定義在My​​SQL中被處理的查詢如何是關鍵瞭解爲什麼觀點可能會導致性能問題,從而引發未初始化的不知情。

這是一個太簡單的答案,以跳到「意見是壞」的流行。它並沒有真正回答爲什麼與觀點的表現被認爲是「壞」。

以下是一些漫步...並可以使用良好的編輯。


在老版本的MySQL中,視圖定義總是物化的。在MySQL白話中,它被稱爲派生表。當你理解操作的順序時,他們使用它的名字是有意義的。無論是內聯視圖(在查詢中用作行源的SELECT語句)還是對存儲視圖的引用(作爲對象存儲在數據庫中的SELECT語句),都會觀察到相同的行爲。

性能問題與處理外部查詢中的謂詞一起使用。在較早版本的MySQL中,外部查詢中的謂詞是從未將推入視圖定義中。如果視圖是內聯或存儲的,則無關緊要。

作爲一個簡化的演示,考慮這個查詢:

SELECT v.mycol 
    FROM (SELECT t.mycol 
      FROM bigtable t 
     ) v 
WHERE v.mycol = 'foo' 

操作在MySQL的順序是先運行括號之間的查詢,並把它作爲物化派生表。性能是在做的順序

CREATE TEMPORARY TABLE v (mycol mydatatype); 
INSERT INTO v (mycol) SELECT t.mycol FROM bigtable t; 

對於滿足一些特定要求的小型表,MySQL將使用MEMORY引擎。如果一個表不符合要求,或者超過了一定的大小,那麼MySQL將它作爲MyISAM表格將其作爲一個磁盤關閉到磁盤。

該操作完成後,可以運行外部查詢。當這個運行時,v可以像一個普通的表進行訪問:

SELECT v.mycol 
    FROM v 
WHERE v.mycol = 'foo' 

隨着該查詢,MySQL有評估mycolv(派生表),以確定是否行匹配或不。就性能而言,這會帶來傷害。

(具有最新版本的MySQL 5.7,優化器將(在某些情況下)實際上創建派生表的索引。在舊版本中,MySQL會從未創建一個派生表的索引。哎喲。

如果我們的查看查詢執行的是SELECT * FROM bigtable(選擇每一列,那麼視圖有效地複製整個表。如果bigtable中的行很大,並且該表包含大量的行,則這可能是一個昂貴的操作。

如果我們將該查詢定義作爲VIEW存儲在數據庫中,那麼相同的一組操作將發生我們查詢視圖的時間。


我的意思是「推」謂詞「進入」查看查詢。

對比上面什麼用這樣的查詢情況:

SELECT v.mycol 
    FROM (
      SELECT t.mycol 
      FROM bigtable t 
      WHERE t.mycol = 'foo' 
     ) v 
    WHERE v.mycol = 'foo' 

請注意,我們有一個WHERE條款對內部查詢中,括號之間。 MySQL運行該內部查詢,它僅從bigtable中檢索滿足WHERE子句中該條件的行。如果這是相當有選擇性的,那麼行數可能是一個非常小的集合。通過這個內部查詢,MySQL優化器可以利用具有前導列mycol的索引來有效地滿足查詢。如果mycol是bigtable的PRIMARY KEY或UNIQUE KEY,那麼查詢最多隻返回一行。這是一個更小的派生表來實現。

而在此示例中,外部查詢的謂詞是多餘的。外部查詢中的WHERE子句可以被刪除,我們仍然保證得到相同的結果。


在您的特定情況下,MySQL是有開88桌,獲得元數據鎖,並獲得表鎖(如果它的MyISAM)等

至於其他的答案(正確地)指出的那樣,您的查詢中的UNION運算符正在導致「唯一排序」操作,以識別並從整個集合中刪除重複行。這可能是昂貴的。

TL; DR

BOTTOM LINE

創建視圖的定義是更可能創建出更大,更成問題的性能問題,不是什麼問題正在創建視圖定義解決。

就性能而言,使用包含每個表的查詢的謂詞的查詢會更好。假設你需要每列(你真的需要返回的每一列,或者你真的只需要其中的一個子集)

這個查詢將會運行得更快:

SELECT t01.* FROM mytable01 t01 WHERE t01.mycol = 'foo' 
    UNION ALL 
    SELECT t02.* FROM mytable02 t02 WHERE t02.mycol = 'foo' 
    UNION ALL 
    SELECT t03.* FROM mytable03 t03 WHERE t03.mycol = 'foo' 
    ... 
    UNION ALL 
    SELECT t88.* FROM mytable88 t88 WHERE t88.mycol = 'foo' 

這是特別是如果在88個表中的每一個表中,mycol都有適當的索引。

當我有一個結合來自多個查詢的結果(但不包括88個表!)時,我通常會包含一個鑑別器列,這將允許我確定哪個查詢返回了該行。

SELECT 't01' AS q, t01.* FROM mytable01 t01 WHERE t01.mycol = 'foo' 
    UNION ALL 
    SELECT 't02' AS q, t02.* FROM mytable02 t02 WHERE t02.mycol = 'foo' 
    UNION ALL 
    SELECT 't03' AS q, t03.* FROM mytable03 t03 WHERE t03.mycol = 'foo' 
    ... 
    UNION ALL 
    SELECT 't88' AS q, t88.* FROM mytable88 t88 WHERE t88.mycol = 'foo' 

從在結果(q)我可以確定哪個查詢返回的行第一列。

問題是什麼?我想你問了一些提示。我希望我已經給你一些要考慮的事情。