2014-09-22 88 views
2

簡化,我得到了以下情況。我有兩張桌子。一次遷移通過checks.migration_id進行多次檢查。列checks.old描述了一種檢查。現在我想爲每次遷移獲得支票,其中old爲true(query1)和false(query2)的時間最長。取得最新的兒童記錄沒有給定

約有30.000次遷移,每次約有1000次檢查,其中old = true,1000次檢查old = false。表格檢查會變得非常極端。沒有給出支票的順序,可能會完全混淆。

我想一次獲得最多150次遷移的最新檢查。

SQL小提琴:http://sqlfiddle.com/#!15/282ce/15

我使用PostgreSQL 9.3和Rails 3.2(不應該的問題)

請告訴我最有效的方式來獲得最新的子記錄,其中舊=真的嗎?

表遷移:

| ID | 
|----| 
| 1 | 
| 2 | 

表檢查:

| ID | MIGRATION_ID | OLD | OK |        TIME | 
|----|--------------|-----|----|----------------------------------| 
| 1 |   1 | 1 | 1 | September, 22 2014 12:00:01+0000 | 
| 2 |   1 | 0 | 1 | September, 22 2014 12:00:02+0000 | 
| 3 |   2 | 1 | 1 | September, 22 2014 12:00:01+0000 | 
| 4 |   2 | 0 | 1 | September, 22 2014 12:00:02+0000 | 
| 5 |   1 | 1 | 1 | September, 22 2014 12:00:03+0000 | 
| 6 |   1 | 0 | 1 | September, 22 2014 12:00:04+0000 | 
| 7 |   2 | 1 | 1 | September, 22 2014 12:00:03+0000 | 
| 8 |   2 | 0 | 1 | September, 22 2014 12:00:04+0000 | 

查詢1應該返回以下結果:

| Migration.id | Check_ID | OLD | OK |        TIME | 
|--------------|----------|-----|----|----------------------------------| 
|  1  |  5 | 1 | 1 | September, 22 2014 12:00:03+0000 | 
|  2  |  7 | 1 | 1 | September, 22 2014 12:00:03+0000 | 

查詢1應該返回以下結果:

| Migration.id | Check_ID | OLD | OK |        TIME | 
|--------------|----------|-----|----|----------------------------------| 
|  1  |  6 | 0 | 1 | September, 22 2014 12:00:04+0000 | 
|  2  |  8 | 0 | 1 | September, 22 2014 12:00:04+0000 | 

我試圖用子查詢中的最大值來解決它,但後來我丟失了關於checks.ok和check.time的信息。

SELECT eq.id, (SELECT max(checks.id) FROM checks WHERE checks.migration_id = eq.id and checks.old = 't') AS latest FROM migrations eq; 
SELECT eq.id, (SELECT max(checks.id) FROM checks WHERE checks.migration_id = eq.id and checks.old = 'f') AS latest FROM migrations eq; 

(我知道我得到的max(id)代替max(time)

在Rails我想每個遷移取這就造成了1 + N問題的最新記錄。我無法包含所有支票,因爲有很多方法。

+0

非常好的工作,第一個問題!小提琴特別受歡迎。 – 2014-09-22 17:01:20

回答

1

簡單溶液與Postgres的特定DISTINCT ON

查詢1( 「每個遷移與最大time其中old爲真支票」):

SELECT DISTINCT ON (migration_id) 
     migration_id, id AS check_id, old, ok, time 
FROM checks 
WHERE old 
ORDER BY migration_id, time DESC; 

倒轉WHERE條件爲查詢2

... 
WHERE NOT old 
... 

詳情:

但是,如果你想更好的閱讀性能與大表,使用JOIN LATERAL(Postgres的9.2+,標準SQL),建立在像多列索引:

CREATE INDEX checks_special_idx ON checks(old, migration_id, time DESC); 

查詢1

SELECT m.id AS migration_id 
    , c.id AS check_id, c.old, c.ok, c.time 
FROM migrations m 
-- FROM (SELECT id FROM migrations LIMIT 150) m 
JOIN LATERAL (
    SELECT id, old, ok, time 
    FROM checks 
    WHERE migration_id = m.id 
    AND old 
    ORDER BY time DESC 
    LIMIT 1 
    ) c ON TRUE; 

交換機再次old條件查詢2.
對於未指定「最大的150點遷移」,使用註釋的替代線。

詳情:

SQL Fiddle.

旁白:不要用 「時間」 作爲標識。這是一個reserved word in standard SQL和Postgres中的基本類型名稱。

+1

嗨Erwin 我花了一些時間來實現解決方案2並測試它,但現在它工作得很完美,而且速度很快。由於我已經有了索引,在我的情況下,將時間索引從普通格式改爲DESC的性能影響是不可測的。 (沒有寫那個) 我忘了說我也想遷移沒有檢查,我通過切換到左側橫向連接。 我的總查詢時間從4秒減少到幾毫秒。非常感謝你! – Fabian 2014-09-25 06:25:33

+0

@Fabian:很好。是的,向後掃描索引幾乎與向前一樣快。您是否也嘗試用「DISTINCT ON」查詢進行性能比較? – 2014-09-25 06:28:58