2011-11-06 147 views
4

我的問題是由以下查詢表示:使用單個SQL相關子查詢得到兩列

SELECT 
    b.row_id, b.x, b.y, b.something, 
    (SELECT a.x FROM my_table a WHERE a.row_id = (b.row_id - 1), a.something != 42) AS source_x, 
    (SELECT a.y FROM my_table a WHERE a.row_id = (b.row_id - 1), a.something != 42) AS source_y 
FROM 
    my_table b 

我使用的是相同的子查詢語句兩次,獲得兩source_xsource_y。 這就是爲什麼我想知道是否有可能只使用一個子查詢什麼關係呢?

因爲一旦我在我的真實數據(百萬行)運行此查詢它似乎永遠不會完成,並需要幾個小時甚至幾天(我的連接結束前掛斷)。

我使用PostgreSQL 8.4

回答

2

@DavidEG發佈該查詢的最好的語法。

然而,你的問題是絕對不僅僅是與查詢技術。的JOIN而不是兩個A子查詢可以通過兩個因素充其量加快東西。最可能少一點。這並不能解釋「小時」。即使有數百萬行,一個體面的PostgreSQL應該在幾秒鐘內完成一個簡單的查詢,而不是幾個小時。

脫穎而出
  • 第一件事是語法錯誤在您的查詢:

    ... WHERE a.row_id = (b.row_id - 1), a.something != 42 
    

ANDOR這裏所需要,而不是逗號。

  • 檢查接下來的事情是指標。如果row_id不是主鍵,則可能沒有索引。對於這個特定的查詢的最佳性能上(row_id, something)創建multi-column index這樣的:

    CREATE INDEX my_table_row_id_something_idx ON my_table (row_id, something) 
    
  • 如果過濾排除相同的值something != 42每次你也可以使用一個partial index,而不是額外的加速:

    CREATE INDEX my_table_row_id_something_idx ON my_table (row_id) 
    WHERE something != 42 
    

這樣只會令如果一個實質性的區別是一種常見的值something不僅僅是一個整數更大的柱。 (由於數據對齊,具有兩個整數的索引通常在光盤上佔據與索引相同的大小,只有一個索引。更多關於data alignment here。)

  • 當性能是一個問題,它始終是一個好主意,check your settings。標準設置PostgreSQL在許多發行版中都非常小,並且不能處理「數百萬行」。

  • 根據您的PostgreSQL的實際版本,升級到當前版本9.1可能會幫助很多

  • 最終,hardware也是一個因素。調整和優化只能讓你到目前爲止。

+0

我嘗試了部分索引,然後@DavidEG查詢並且很快創建了新表。非常感謝! –

+0

@ JulieFen-Chong:很酷。 :)擬合指數對於數百萬行是必不可少的。 –

7

我認爲你可以使用這種方法:

SELECT b.row_id 
    , b.x 
    , b.y 
    , b.something 
    , a.x 
    , a.y 
    FROM my_table b 
    left join my_table a on a.row_id = (b.row_id - 1) 
         and a.something != 42 
0

老式的語法:

SELECT 
    b.row_id, b.x, b.y, b.something 
    , a.x AS source_x 
    , a.y AS source 
FROM my_table b 
    ,my_table a 
WHERE a.row_id = b.row_id - 1 
    AND a.something != 42 
    ; 

加入語法:

SELECT 
    b.row_id, b.x, b.y, b.something 
    , a.x AS source_x 
    , a.y AS source 
FROM my_table b 
JOIN my_table a 
    ON (a.row_id = b.row_id - 1) 
WHERE a.something != 42 
    ; 
+0

您需要'LEFT JOIN'來請求結果。戴維格克釘了它。 –

+0

是的,如果沒有找到,子查詢應該返回NULL。但是太難看了,我想...... – wildplasser

0
SELECT b.row_id, b.x, b.y, b.something, a.x, a.y 
    FROM my_table b 
    LEFT JOIN (
    SELECT row_id + 1, x, y 
     FROM my_table 
     WHERE something != 42 
) AS a ON a.row_id = b.row_id; 
+0

這樣做會起作用,但可能非常慢,因爲在加入之前,必須用'something!= 42'(可能是大部分「數百萬行」)增加每一行,阻礙了對聯合使用標準索引作爲副作用。 –

+0

@ErwinBrandstetter我明白你的意思,我應該在連接條件中保留'a.row_id = b.row_id - 1'。我把注意力集中在將'something!= 42'移動到子查詢中。 – Neil