【发布时间】:2011-11-19 19:13:22
【问题描述】:
这是我的情况。我有一张包含一堆 URL 的表格并爬网 与他们相关的日期。当我的程序处理一个 URL 时,我想要 插入带有抓取日期的新行。如果 URL 已经存在,我 要将抓取日期更新为当前日期时间。使用 MS SQL 或 Oracle 我可能会为此使用 MERGE 命令。使用 mySQL 我会 可能使用 ON DUPLICATE KEY UPDATE 语法。
我可以在我的程序中执行多个查询,这可能是也可能不是 线程安全。我可以编写一个具有各种 IF...ELSE 的 SQL 函数 逻辑。但是,为了尝试 Postgres 功能,我已经 以前从未使用过,我正在考虑创建一个 INSERT 规则 - 像这样:
CREATE RULE Pages_Upsert AS ON INSERT TO Pages
WHERE EXISTS (SELECT 1 from Pages P where NEW.Url = P.Url)
DO INSTEAD
UPDATE Pages SET LastCrawled = NOW(), Html = NEW.Html WHERE Url = NEW.Url;
这似乎真的很好用。它可能会失去一些分数 “代码可读性”的观点,因为有人在看我的代码 第一次必须神奇地知道这个规则,但我 猜测可以通过良好的代码注释来解决 文档。
这个想法是否还有其他缺点,或者可能是“你的想法 糟透了,你应该这样做 /this/ 方式”评论?我在 PG 9.0 如果 这很重要。
UPDATE:查询计划,因为有人想要它:)
"Insert (cost=2.79..2.81 rows=1 width=0)"
" InitPlan 1 (returns $0)"
" -> Seq Scan on pages p (cost=0.00..2.79 rows=1 width=0)"
" Filter: ('http://www.foo.com'::text = lower((url)::text))"
" -> Result (cost=0.00..0.01 rows=1 width=0)"
" One-Time Filter: ($0 IS NOT TRUE)"
""
"Update (cost=2.79..5.46 rows=1 width=111)"
" InitPlan 1 (returns $0)"
" -> Seq Scan on pages p (cost=0.00..2.79 rows=1 width=0)"
" Filter: ('http://www.foo.com'::text = lower((url)::text))"
" -> Result (cost=0.00..2.67 rows=1 width=111)"
" One-Time Filter: $0"
" -> Seq Scan on pages (cost=0.00..2.66 rows=1 width=111)"
" Filter: ((url)::text = 'http://www.foo.com'::text)"
【问题讨论】:
-
请将生成的查询计划(针对目标表上的 INSERT 操作)添加到帖子中。
-
顺便说一句,页面上的 lower(url) 上实际上有一个 INDEX - 我认为它正在进行顺序扫描,因为目前表中只有 4 行,并且规划者认为这样做更快查看 4 行而不是命中索引。只是猜测。
-
谢谢。我得到的差不多。更新部分总是发生。如果您执行实际的 INSERT INTO pages(...) VALUES (...),则 lastcrawled 的值将被 now() 覆盖,即使在插入之前该键不存在。
标签: database postgresql rules upsert