2009-11-03 39 views
2

這更像是一個通用的SQL問題,但如果有人知道Firebird/Interbase特定優化,我會使用Firebird 2.5。首先,下面是一個簡單的例子架構來說明我試圖解決這一問題:在對外鍵進行排序時加速SQL查詢

CREATE TABLE users 
(
    id INTEGER PRIMARY KEY, 
    name VARCHAR(16) 
); 

CREATE TABLE data_set 
(
    id INTEGER PRIMARY KEY, 
    name VARCHAR(64) 
); 

CREATE UNIQUE INDEX data_set_name_idx ON data_set(name); 

CREATE TABLE data 
(
    user_id INTEGER, 
    data_set_id INTEGER, 
    data BLOB, 
    PRIMARY KEY(user_id, data_set_id) 
); 

CREATE INDEX data_user_id_idx ON data(user_id); 
CREATE INDEX data_data_set_id_idx ON data(data_set_id); 

我試圖運行查詢如下:

SELECT users.name, data_set.name, data FROM users, data_set, data 
WHERE user_id=XXX AND user_id=users.id AND data_set_id=data_set.id 
ORDER BY data_set.name; 

隨着「XXX」正在填寫user_id我想要的。所以我正在做的是從數據表中選擇屬於特定用戶的所有行,並且我正在根據data_set名稱對結果進行排序。

這工作,因爲它是,但問題是數據表有超過十億行中它和data_set表不小任。單個用戶ID的結果集可能有數十億行。會發生什麼情況是,爲了使ORDER BY工作數據庫必須創建大量的臨時數據,這些數據非常慢,並且使用大量磁盤空間。如果沒有ORDER BY,它很快,但顯然不會按照我的需要排序。

一個解決辦法是採取data_set.name值,只是把它們放在一個VARCHAR列在數據。然後可以編制索引並快速排序。這種方法的問題是它會有很多重複的數據,並使數據庫變得非常龐大。

另一種解決方案將類似於索引視圖或索引計算列。據我所知,Firebird都不支持這些功能。

還有其他想法嗎?

+0

我沒有確定的答案,但我認爲你可以更多地使用索引來玩,並用查詢來對它們進行基準測試。 – 2009-11-03 12:14:01

+0

爲什麼要首先爲單個用戶提供「數億行」?不僅需要處理查詢而且需要將數據傳輸到客戶端。 – liggett78 2009-11-03 14:35:05

+0

@ liggett78:哦,我同意它將需要時間來傳輸它,因爲它有很多數據。這就是爲什麼我想盡可能地加快速度。它不需要像現在這樣慢,因爲它浪費了大量時間爲這種排序創建臨時數據。 – 2009-11-03 16:06:21

回答

0

爲什麼不索引data_set.name?

此外,我會跳過事實表(數據)的主鍵定義,併爲外鍵放置兩個單獨的索引,以加快連接。 (當然,如果要插入大量記錄,索引可能會影響插入)

如果您需要確保事實表的唯一約束,您可以從將數據傳輸到該表的作業中完成(我沒有關於該表的詳細信息:)。

+0

對不起,我忘了包括那個。是的,data_set.name被編入索引。我將更新示例模式。索引它並不能解決問題。 – 2009-11-03 12:24:29

+0

然後您應該檢查執行計劃。這可能是你必須去數據庫特定的優化...... – 2009-11-03 12:39:58

+0

就執行計劃而言,數據庫基本上是將整個未排序的結果集放到一個臨時表中,以便索引ORDER BY列。本質上是同樣的事情,如果我添加一列到* data *包含* data_set.name *,除了結果集。由於結果集的大小,這非常緩慢。 我想知道是否有某種方式來創建外鍵引用* data *中的索引,除了使用它引用的值(data_set.name)而不是整數值。 – 2009-11-03 13:25:30

1

這是相當投機,但我不知道這可能是改組爲:

  1. 用戶和數據集,包括用戶的謂語之間笛卡爾積。
  2. 整理數據集名
  3. 加入到數據

...會更有效,特別是如果你只是在查詢的抗凍行感興趣。

在Oracle中,我認爲這不會是因爲嵌套循環連接的效率遠遠低於散列連接,但我對恐龍並不熟悉。

0

嘗試在data_set(id,name)上定義一個索引並對其進行實驗 - 可能與此處的其他建議結合使用。如果您的需求指定它,並且Firebird支持唯一約束,您可以將現有的UNIQUE索引更改爲UNIQUE CONSTRAINT。