2015-10-17 96 views
4

簡版: 我想查詢另一個查詢的結果,以便選擇一個更有限的結果集但是,添加where子句重寫第一個查詢,而不是對結果的工作,所以我沒有得到我想要的答案如何在rails中查詢查詢結果(使用rails查詢'DISTINCT ON'的結果&postgres

的細節:。 我有兩個型號,檢查和ticks.Checks has_many ticks

第一個查詢使用DISTINCT ON並收集所有'檢查'和所有r興高采烈的滴答,但只返回最近的滴答。我在模型中作爲範圍工作。

在我的控制器,

def checklist 
    #Filter the results by scope or return all checks with latest tick 
    case params[:filter] 
    when "duebylastresult" 
     @checks = Check.mostrecenttickonly.duebylastresult 
    when "duebydate" 
     @checks = Check.mostrecenttickonly.duebydate 
    else 
     @checks = Check.mostrecenttickonly 
    end 
    end 

在該模型中,第一個範圍(工作):

scope :mostrecenttickonly, -> { 
includes(:ticks) 
.order("checks.id, ticks.created_at DESC") 
.select("DISTINCT ON (checks.id) *").references(:ticks) 
} 

生成以下SQL:

Parameters: {"filter"=>""} 
    SQL (1.0ms) SELECT DISTINCT ON (checks.id) *, 
"checks"."id" AS t0_r0, 
"checks"."area" AS t0_r1, "checks"."frequency" AS t0_r2, 
"checks"."showinadvance" AS t0_r3, "checks"."category" AS t0_r4, 
"checks"."title" AS t0_r5, "checks"."description" AS t0_r6, 
"checks"."created_at" AS t0_r7, "checks"."updated_at" AS t0_r8, 
"ticks"."id" AS t1_r0, "ticks"."result" AS t1_r1, 
"ticks"."comments" AS t1_r2, "ticks"."created_at" AS t1_r3, 
"ticks"."updated_at" AS t1_r4, "ticks"."check_id" AS t1_r5 
FROM "checks" LEFT OUTER JOIN "ticks" 
ON "ticks"."check_id" = "checks"."id" 
ORDER BY checks.id, ticks.created_at DESC 

已經得到了結果,我只想顯示具有等於或大於3的值的刻度,所以範圍:

scope :duebylastresult, -> { where("ticks.result >= 3") } 

生成SQL

Parameters: {"filter"=>"duebylastresult"} 
    SQL (1.0ms) SELECT DISTINCT ON (checks.id) *, 
"checks"."id" AS t0_r0, 
"checks"."area" AS t0_r1, "checks"."frequency" AS t0_r2, 
"checks"."showinadvance" AS t0_r3, "checks"."category" AS t0_r4, 
"checks"."title" AS t0_r5, "checks"."description" AS t0_r6, 
"checks"."created_at" AS t0_r7, "checks"."updated_at" AS t0_r8, 
"ticks"."id" AS t1_r0, "ticks"."result" AS t1_r1, 
"ticks"."comments" AS t1_r2, "ticks"."created_at" AS t1_r3, 
"ticks"."updated_at" AS t1_r4, "ticks"."check_id" AS t1_r5 
FROM "checks" LEFT OUTER JOIN "ticks" 
ON "ticks"."check_id" = "checks"."id" 
WHERE (ticks.result >= 3) 
ORDER BY checks.id, ticks.created_at DESC 

盡我所知道的,WHERE語句在行動前的DISTINCT ON子句,所以我現在有「其結果爲> = 3最新滴答」,而我正在尋找'最近的滴答,那麼只有當結果是> = 3'時。

希望有道理&在此先感謝!

編輯 - 是我所得到什麼,我需要的例子:

The Data: 
Table Checks: 
ID: 98 Title: Eire 
ID: 99 Title: Land 

Table Ticks: 
ID: 1 CheckID: 98 Result:1 Date: Jan12 
ID: 2 CheckID: 98 Result:5 Date: Feb12 
ID: 3 CheckID: 98 Result:1 Date: Mar12 
ID: 4 CheckID: 99 Result:4 Date: Apr12 

First query returns the most recent result, like; 
Check.ID: 98 Tick.ID: 3 Tick.Result: 1 Tick.Date: Mar12 
Check.ID: 99 Tick.ID: 4 Tick.Result: 4 Tick.Date: Apr12 

Second query currently returns the most recent result where the result is =>3, like; 
Check.ID: 98 Tick.ID: 2 Tick.Result: 5 Tick.Date: Feb12 
Check.ID: 99 Tick.ID: 4 Tick.Result: 5 Tick.Date: Apr12 

When I really want: 
Check.ID: 99 Tick.ID: 4 Tick.Result: 5 Tick.Date: Apr12 

(ID 98 doesn't show as the last Tick.Result is 1). 
+0

你能舉例說明現有查詢的結果將如何變化不是比所需的查詢? –

+0

謝謝@RobWise,添加了示例。 –

回答

1

你能嘗試以下,看看它是否開始你在正確的方向:

scope :just_a_test, -> { 
    includes(:ticks) 
    .order("checks.id") 
    .where("ticks.created_at = (SELECT MAX(ticks.created_at) FROM ticks WHERE ticks.check_id = checks.id)") 
    .where("ticks.result >= 3") 
    .group("checks.id") 
    } 
+0

那麼,我試過你編輯前的版本, 範圍:mostrecenttickonly, - > { 包括(:ticks) .order(「checks.id」) 。where(「ticks.created_at =(SELECT MAX(ticks。 created_at)FROM ticks WHERE ticks.check_id = checks.id)「) } 它給出了一個錯誤,因此添加了」.references(:ticks)「....它看起來像所有的工作! 我會在早上和你的新建議一起進一步測試,但很可能我會將此標記爲答案 - 許多人非常感謝! –

+0

目前的版本是否能給你想要的最終結果?如果是這樣,我認爲你可以刪除第二個where子句並將其用於'''mostrecenttickonly''' – laertiades

+0

謝謝@laertiades,回答接受。我還編輯了你的答案,以添加與.references一起工作的代碼(目前正在等待同行評審)。再次感謝! –

0

我米不知道我真的明白:mostrecenttickonly範圍的點,因爲你只是加載檢查。

話雖這麼說,如果你想獲得只有那些檢查其最近的蜱有大於三的結果,我認爲要做到這一點的最佳方式將是一個window function

check.rb

... 
    scope :duebylastresult, -> { 
    find_by_sql(
     'SELECT * 
     FROM (SELECT checks.*, 
        ticks.id AS tick_ids, 
        ticks.date AS tick_date, 
        ticks.result AS tick_result, 
        dense_rank() OVER (
         PARTITION BY checks.id 
         ORDER BY ticks.date DESC 
        ) AS tick_rank 
      FROM checks 
      LEFT OUTER JOIN ticks ON checks.id = ticks.check_id) AS ranked_ticks 
     WHERE tick_rank = 1 AND tick_result >= 3;' 
    ) 
    } 
... 

基本上,我們只是在檢查連接一切,蜱表,然後添加名爲tick_rank是在結果根據其date相對於相同其他行集居各行的另一個屬性值。

的方式SQL工作原理是,謂詞(在WHERE子句中的條件)的SELECT領域的評估之前評估的,這意味着我們不能只寫tick_rank = 1本聲明。

所以我們必須去包裝結果(我們別名爲ranked_ticks)的額外步驟,然後選擇所有內容並將我們想要的謂詞應用於此外部選擇語句。該tick_rank必須是1,這意味着它最近tick,其結果必須是> = 3


編輯:我用的是那篇文章我聯繫的複習,因爲我常常忘記SQL語法,但看着它之後,我認爲這將在一定程度上更好的性能(基本上只是迫不及待地加入checks直到劃分完成後,這樣,我相信它會做較少的完全掃描):

scope :duebylastresult, -> { 
    find_by_sql(
     'SELECT * 
     FROM checks 
     LEFT OUTER JOIN 
      (SELECT id AS tick_id, 
        check_id AS check_id, 
        date AS tick_date, 
        result AS tick_result, 
        dense_rank() OVER (
         PARTITION BY ticks.check_id 
         ORDER BY ticks.date DESC 
        ) AS tick_rank 
      FROM ticks) AS ranked_ticks ON checks.id = ranked_ticks.check_id 
     WHERE tick_rank = 1 AND tick_result >= 3;' 
    ) 
    } 
+0

感謝@RobWise的附加解釋,他們幫助我提高了我對sql的理解。具有以下特徵的原因:最具挑戰性的範圍是,這是顯示所有內容的視圖,然後用戶可以選擇不同的視圖。通過分兩步來理解每個視圖對我來說更容易。 –