簡體   English   中英

如何在sql中使連接表更新高效(從我的ruby代碼中提高效率!!)

[英]how to make join table update efficient in sql (it's efficient from my ruby code!!)

這是我無法理解的奇怪之處,我在PostGreSql9.2中工作......

我有這個數據庫:

movies (id, title, votes)
infos (id, movie_id, info_type, value)

我想用infos.value更新movies.votes,加入movies.id = infos.movi​​e_if並且只在info_type = 100(這是投票的類型......)的地方加入

我嘗試了兩個不同的查詢:

update movies
set votes = cast(i.value as integer)
from movies m inner join infos i on m.id = i.movie_id
where i.info_type = 100

其中(使用說明)預測大約1100萬秒的運行時間(太多了!)

第二次嘗試:

update movies m
set votes = cast(
(
  select value
  from infos i
  where i.info_type = 100 and i.movie_id = m.id
  limit 1
) AS integer);

這一個應該是“只”20萬秒..仍然太多了

我真的不知道查詢計划是如何工作的,所以我嘗試使用ruby腳本(使用active_record)來執行此操作...這是:

Info.find_in_batches(:conditions => "info_type = 100") do |group|
    group.each{ |info|
        movie = Movie.find(info.movie_id)
        movie.votes = info.value.to_i
        movie.save
    }
end

對於那些沒有閱讀ruby的人來說,這個查詢只是循環遍歷滿足info_type = 100條件的所有信息,然后每個信息搜索相應的電影並更新它。

它非常快! 只需幾分鍾,所有紅寶石/ orm開銷!

現在,為什么? 知道電影大約是600k的記錄,但只有200k(三分之一)有一個帶有投票數的信息記錄。 這仍然無法解釋發生了什么。

EXPLAIN

正如@ruakh已經解釋的那樣,你可能誤解了EXPLAIN告訴你的內容。 如果您想要以秒為單位的實際時間,請使用EXPLAIN ANALYZE

但請注意,這實際上是執行語句。 我在這里引用手冊:

重要說明:請記住,在使用ANALYZE選項時,實際上會執行該語句。 盡管EXPLAIN將丟棄SELECT將返回的任何輸出,但該語句的其他副作用將照常發生。 如果要在INSERTUPDATEDELETECREATE TABLE ASEXECUTE語句上使用EXPLAIN ANALYZE而不讓命令影響數據,請使用以下方法:

 BEGIN; EXPLAIN ANALYZE ...; ROLLBACK; 

盡管如此,對第一個查詢的估計仍然很高並且表明存在嚴重問題。

怎么了?

至於你的第三種方法 :對於大表,數據庫服務器一次更新整個(大)表比將每條行的指令發送到服務器的速度總是快一個數量級 - 如果是新的話更是如此值來自數據庫。 更多相關答案 如果您的測試顯示不正確,那么您的(測試)設置可能出現問題。 事實上,它是......

您的第一個查詢 完全出錯了 可怕的性能估計表明它是多么可怕的錯誤。 當您將表格movies加入FROM子句中的表格infos時,您會忘記將結果行綁定到UPDATE表格中的行的WHERE條件。 這導致了CROSS JOIN ,即movies 每一行(600k)都會以每次投票的values (200k)進行更新,從而產生120,000,000次更新 好吃。 一切都錯了。 從不執行此操作。 甚至在可以回滾的事務中也是如此。

你的第二個查詢也 出錯了 它運行一個相關的子查詢 ,即它為每一行運行一個單獨的查詢。 這是600k子查詢而不是1,因此表現糟糕。

這是正確的600k子查詢。 不是200k。 你指示Postgres更新部電影,無論如何。 沒有匹配的infos.value (沒有info_type = 100 )的那些,在votes收到NULL值,覆蓋之前的任何內容。

另外,我想知道LIMIT 1在那里做什么?

  • 或者(infos.movie_id, infos.info_type)UNIQUE ,那么你不需要LIMIT
  • 或者它不是UNIQUE 如果您打算保留結構,則為infos添加UNIQUE index

正確的查詢

UPDATE movies m
SET    votes = i.value::int
FROM   infos i
WHERE  m.id = i.movie_id
AND    i.info_type = 100
AND    m.votes IS DISTINCT FROM i.value::int;

這非常類似於您的第一個查詢,只是簡化並正確執行,以及增強功能。

  • 無需再次加入movies 你只需要infosFROM子句中。

  • 實際上將要更新的行綁定到攜帶新值的行,從而避免(非預期的) CROSS JOIN

     WHERE m.id = i.movie_id 
  • 避免空洞更新,它們帶來了無成本的成本。 這就是最后一行的用途。

應該是幾秒或更短的時間,而不是幾百萬秒。
順便說一下, 索引對這個查詢沒有幫助,因為你使用了所有(或三分之一)所涉及的表,所以表掃描對於所描述的數據分發來說更快。

[...](使用說明)預測大約1100萬秒的運行時間(太多了!)

[...]這個“只有”20萬秒......仍然太多了

我認為你誤解了EXPLAIN的輸出。 正如其文檔中所解釋的那樣,“估計的語句執行成本”(即“計划者猜測運行語句需要多長時間”)不是以為單位測量的,而是“以成本單位表示任意,但通常意味着磁盤頁面提取“。

因此,PostgreSQL猜測第二個語句的運行速度比第一個語句快500倍,但是沒有人會在你認為的任何時候接近任何一個語句。 :-)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM