5

これが私の状況です。たくさんのURLとそれらに関連付けられたクロール日を含むテーブルがあります。プログラムがURLを処理するときに、クロール日を含む新しい行を挿入したいと思います。URLがすでに存在する場合は、クロール日を現在の日時に更新したいと思います。MS SQLまたはOracleでは、おそらくこれにMERGEコマンドを使用します。mySQLでは、おそらくON DUPLICATEKEYUPDATE構文を使用します。

プログラムで複数のクエリを実行できますが、スレッドセーフである場合とそうでない場合があります。さまざまな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 /の方法でそれを行うべきです」というコメントはありますか?それが重要な場合、私はPG9.0を使用しています。

更新:誰かがそれを望んでいたので、クエリプラン:)

"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)"
4

5 に答える 5

5

さて、私はなんとかテストケースを作成することができました。その結果、新しい挿入であっても、更新部分は常に実行されます。COPYはルールシステムをバイパスしているようです。[わかりやすくするために、これを別の返信に入れました]

DROP TABLE pages CASCADE;
CREATE TABLE pages
    ( url VARCHAR NOT NULL  PRIMARY KEY
    , html VARCHAR
    , last TIMESTAMP
    );

INSERT INTO pages(url,html,last) VALUES ('www.example.com://page1' , 'meuk1' , '2001-09-18 23:30:00'::timestamp );

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 html=new.html , last = NOW() WHERE url = NEW.url
    );

INSERT INTO pages(url,html,last) VALUES ('www.example.com://page2' , 'meuk2' , '2002-09-18 23:30:00':: timestamp );
INSERT INTO pages(url,html,last) VALUES ('www.example.com://page3' , 'meuk3' , '2003-09-18 23:30:00':: timestamp );

INSERT INTO pages(url,html,last) SELECT pp.url || '/added'::text, pp.html || '.html'::text , pp.last + interval '20 years' FROM pages pp;

COPY pages(url,html,last) FROM STDIN;
www.example.com://pageX     stdin   2000-09-18 23:30:00
\.

SELECT * FROM pages;

結果:

              url              |    html    |            last            
-------------------------------+------------+----------------------------
 www.example.com://page1       | meuk1      | 2001-09-18 23:30:00
 www.example.com://page2       | meuk2      | 2011-09-18 23:48:30.775373
 www.example.com://page3       | meuk3      | 2011-09-18 23:48:30.783758
 www.example.com://page1/added | meuk1.html | 2011-09-18 23:48:30.792097
 www.example.com://page2/added | meuk2.html | 2011-09-18 23:48:30.792097
 www.example.com://page3/added | meuk3.html | 2011-09-18 23:48:30.792097
 www.example.com://pageX       | stdin      | 2000-09-18 23:30:00
 (7 rows)

更新:それができることを証明するためだけに:

INSERT INTO pages(url,html,last) VALUES ('www.example.com://page1' , 'meuk1' , '2001-09-18 23:30:00'::timestamp );
CREATE VIEW vpages AS (SELECT * from pages);

CREATE RULE Pages_Upsert AS ON INSERT TO vpages
  DO INSTEAD (
     UPDATE pages p0
     SET html=NEW.html , last = NOW() WHERE p0.url = NEW.url
    ;
     INSERT INTO pages (url,html,last)
    SELECT NEW.url, NEW.html, NEW.last
        WHERE NOT EXISTS ( SELECT * FROM pages p1 WHERE p1.url = NEW.url)
    );

CREATE RULE Pages_Indate AS ON UPDATE TO vpages
  DO INSTEAD (
     INSERT INTO pages (url,html,last)
    SELECT NEW.url, NEW.html, NEW.last
        WHERE NOT EXISTS ( SELECT * FROM pages p1 WHERE p1.url = OLD.url)
        ;
     UPDATE pages p0
     SET html=NEW.html , last = NEW.last WHERE p0.url = NEW.url
        ;
    );

INSERT INTO vpages(url,html,last) VALUES ('www.example.com://page2' , 'meuk2' , '2002-09-18 23:30:00':: timestamp );
INSERT INTO vpages(url,html,last) VALUES ('www.example.com://page3' , 'meuk3' , '2003-09-18 23:30:00':: timestamp );

INSERT INTO vpages(url,html,last) SELECT pp.url || '/added'::text, pp.html || '.html'::text , pp.last + interval '20 years' FROM vpages pp;
UPDATE vpages SET last = last + interval '-10 years' WHERE url = 'www.example.com://page1' ;

-- Copy does NOT work on views
-- COPY vpages(url,html,last) FROM STDIN;
-- www.example.com://pageX    stdin    2000-09-18 23:30:00
-- \.

SELECT * FROM vpages;

結果:

INSERT 0 1
INSERT 0 1
INSERT 0 3
UPDATE 1
              url              |    html    |        last         
-------------------------------+------------+---------------------
 www.example.com://page2       | meuk2      | 2002-09-18 23:30:00
 www.example.com://page3       | meuk3      | 2003-09-18 23:30:00
 www.example.com://page1/added | meuk1.html | 2021-09-18 23:30:00
 www.example.com://page2/added | meuk2.html | 2022-09-18 23:30:00
 www.example.com://page3/added | meuk3.html | 2023-09-18 23:30:00
 www.example.com://page1       | meuk1      | 1991-09-18 23:30:00
(6 rows)

このビューは、書き換えシステムが再帰的にならないようにするために必要です。DELETEルールの作成は、読者の練習問題として残されています。

于 2011-09-18T21:56:51.863 に答える
2

それを知っているか、そのような人に非常に近いはずの人からのいくつかの良い点;-)

PostgreSQLルールは何に適していますか?

ショートストーリー:

  • ルールはとでうまく機能しSERIALますBIGSERIALか?
  • ルールはとのRETURNING句でうまく機能しますか?INSERTUPDATE
  • ルールは次のようなものでうまく機能しrandom()ますか?

これらすべてのことは、ルールシステムが行駆動型ではなく、想像もできない方法でステートメントを変換するという事実に要約されます。

あなた自身とあなたのチームが好意を持って、そのようなことのために役割を使うのをやめなさい。

編集:あなたの問題はPostgreSQLコミュニティでよく議論されています。検索キーワードは次のとおりです。 MERGEUPSERT

于 2011-09-18T19:43:42.323 に答える
1

これが主観的になりすぎるかどうかはわかりませんが、あなたの解決策について私が考えるのは、セマンティクスがすべてです。私が挿入を行うとき、私は挿入を期待しますが、挿入を行うかもしれないがそうではないかもしれないいくつかの派手なロジックではありません。確かにそれが関数の目的です。

まず、プログラムのURLを確認してから、挿入するか更新するかを選択します。それが遅すぎることが判明した場合は、関数を使用します。のようinsert_or_update_urlに名前を付けると、自動的にいくつかのドキュメントを無料で入手できます。書き換えルールでは暗黙知が必要ですが、私は一般的にそれを避けようとしています。

プラス面:誰かがデータをコピーしたが、ルールと関数を忘れた場合、ソリューションは黙って壊れるかもしれませんが(ただし、他の制約に依存する可能性があります)、欠落している関数は悲鳴を上げます。誤解しないでください。あなたのソリューションは非常に創造的でスマートだと思います。私の好みには少しあいまいすぎます。

于 2011-09-18T19:36:21.190 に答える
0

Postgresのドキュメントには、単純な関数を使用してアップサート/マージを実装する例があります。

ルールは絶対に使用しないでください。ルールは悪です。

于 2011-09-18T22:04:57.380 に答える
-1

ルール資格で古いものと新しいもの以外のテーブルを参照することはできません。代わりに、ルール本体でこれを行う必要があります。これはすべて、ルールが、実行すべき変換と実行すべきでない変換について書き換えシステムに通知する方法にすぎないためです。ルールはトリガーではなく、すべての行に対して実行されますが、クエリプランナーに細かいマッサージを提供し、プランを書き直すように適切に要求します。ドキュメントから:

ルール資格とは何ですか?これは、ルールのアクションを実行するタイミングと実行しないタイミングを指示する制限です。この資格は、基本的にオブジェクトとして与えられた関係を表す(ただし、特別な意味を持つ)疑似関係NEWおよび/またはOLDのみを参照できます。

于 2011-09-18T19:34:53.313 に答える