2009-08-10 47 views
1

我有一個表的SQL(Postgres的),我們稱之爲「項」,看起來像這樣(簡化):怎麼辦根據計數的更新 -

id [pk] 
user_id [fk] 
created [date] 
processed [boolean, default false] 

,我想創建一個UPDATE將除了每個用戶的最近3個(最新創建的列)之外的所有條目上將處理標誌設置爲真的查詢。因此,對於以下條目:

1,456,2009-06-01,false 
2,456,2009-05-01,false 
3,456,2009-04-01,false 
4,456,2009-03-01,false 

只有條目4將其處理標誌更改爲true。

任何人都知道我該怎麼做?

回答

3

我不知道postgres,但這是標準的SQL,可能適合你。

update entries set 
    processed = true 
where (
    select count(*) 
    from entries as E 
    where E.user_id = entries.user_id 
    and E.created > entries.created 
) >= 3 

換句話說,只要在以後的日期有相同的user_id有三個或更多的條目,就將處理的列更新爲true。我假設[創建]列對於給定的user_id是唯一的。如果沒有,你需要一個額外的標準來確定你的意思是「最新」。

在SQL Server中,你可以做到這一點,這是更容易跟蹤一點,可能會被更有效地執行:

with T(id, user_id, created, processed, rk) as (
    select 
    id, user_id, created, processed, 
    row_number() over (
     partition by user_id 
     order by created desc, id 
    ) 
    from entries 
) 
    update T set 
    processed = true 
    where rk > 3; 

更新的CTE是一個非標準的功能,而不是所有的數據庫系統的支持ROW_NUMBER。

+0

是的你的SQL查詢完美工作。我確實嘗試過這樣做,但它對我沒有用。我不確定,因爲我沒有保留那個查詢,但我想我正在嘗試在子查詢中選擇count(*),user_id出於某種原因,但我不知道爲什麼我會這樣做。 – 2009-08-10 13:39:56

+0

謝謝。順便說一下,在閱讀depesz的解決方案後,我改變了> => =,這與我的解決方案類似,但正確。 :)請確保你不保留我原來的錯誤。 – 2009-08-10 13:50:50

4

首先,讓我們開始查詢,將列出所有要更新的行:

select e.id 
from entries as e 
where (
    select count(*) 
    from entries as e2 
    where e2.user_id = e.user_id 
     and e2.created > e.created 
) > 2 

這列出的記錄所有的ID,具有2所以上這樣的記載,user_id是相同的,但創建以後比在行中創建要返回。

即它會列出所有記錄,但每個用戶最後3個。

現在,我們可以:

update entries as e 
set processed = true 
where (
    select count(*) 
    from entries as e2 
    where e2.user_id = e.user_id 
     and e2.created > e.created 
) > 2; 

有一點想法 - 它可能會很慢。在這種情況下,使用自定義聚合或者(如果你使用8.4)窗口函數可能會更好。