2017-10-23 9 views
1

HTMLコンテンツを含むテーブルがあります。このコンテンツには、1つまたは複数のURLを含めることができます。また、関連するリライトを持つURLを含むマッピングテーブルがあります。 すべてのURLを書き換えて置き換えるためには、各HTMLコンテンツでできることが必要です。テキストフィールド内の複数の文字列置換

ユースケース(Postgresの9.5)のよう

row1: 1, 'A BA POM CHU' 
row2: 2, 'B AB' 
row3: 3, 'C CHU NOTA CA' 

rewriteテーブルに新しい行を追加する:

row3: 3, 'NOTA', 'ISB' 

TABLE some_content (content_id int4, content text) 
row1: 1, 'A BA BLAH PIKA', 
row2: 2, 'B AB', 
row3: 3, 'C PIKA NOTA CA' 

TABLE rewrite (rule_id int4, old_string text, new_string text) 
row1: 1, 'PIKA', 'CHU', 
row2: 2, 'BLAH', 'POM' 

クエリは、次のセットをouputをすべきです

は、結果セットを(int4、text)に変換します。

row1: 1, 'A BA POM CHU' 
row2: 2, 'B AB' 
row3: 3, 'C CHU ISB CA' 

何かヒント?

+0

Postgresのバージョン、テーブル定義? –

+0

@ErwinBrandstetterが追加されました。ありがとうございました – greg

+0

テーブル 'rewrite'にID列があるはずです。原則として、また、置換が適用される順序が重要なためです。さらに、単語全体(区切り記号*正確に*を定義する)または任意の一致を置き換えるだけでよいかどうかを明確にします。 –

答えて

1

各置換は、最後の置換の結果によって異なります。あなたはある種のループが必要です。そして、置き換えの中で決定論的な順序が必要です。 rule_idを昇順に仮定します。そして、あなたがのいずれかをと置き換えることを前提としていれば、単語全体だけではなく(適応しやすい)。

plpgsql関数をループすることができます。おそらくもっと速いでしょう。参照:

あるいは、純粋なSQLのために、この再帰CTEを試してみてください。

WITH RECURSIVE cte AS (
    SELECT s.content_id, r.rule_id 
     , replace(s.content, r.old_string, r.new_string) AS content 
    FROM some_content s 
    CROSS JOIN (
     SELECT rule_id, old_string, new_string 
     FROM rewrite 
     ORDER BY rule_id -- order of rows is relevant! 
     LIMIT 1 
    ) r 

    UNION ALL 
    SELECT c.content_id, r.rule_id 
     , replace(c.content, r.old_string, r.new_string) AS content 
    FROM cte c 
     , LATERAL (
     SELECT rule_id, old_string, new_string 
     FROM rewrite 
     WHERE rule_id > c.rule_id 
     ORDER BY rule_id -- order of rows is relevant! 
     LIMIT 1 
    ) r 
    ) 
SELECT DISTINCT ON (content_id) content 
FROM cte 
ORDER BY content_id, rule_id DESC; 

LATERALあなたはCTEを参照する直接サブクエリになるだろう"invalid reference to FROM-clause entry for table "c"回避するために参加。関連:

あるいは、ギャップlike you commentedせずにシリアル番号を生成するためにrow_number()を使用して:

WITH RECURSIVE r AS (
    SELECT old_string, new_string 
     , row_number() OVER (ORDER BY rule_id) AS rn -- your ORDER BY expression? 
    FROM rewrite 
    ) 
, cte AS (
    SELECT s.content_id, r.rn 
     , replace(s.content, r.old_string, r.new_string) AS content 
    FROM some_content s 
    JOIN r ON r.rn = 1 

    UNION ALL 
    SELECT s.content_id, r.rn 
     , replace(s.content, r.old_string, r.new_string) AS content 
    FROM cte s 
    JOIN r ON r.rn = s.rn + 1 
    ) 
SELECT DISTINCT ON (content_id) content 
FROM cte 
ORDER BY content_id, rn DESC; 

dbfiddle here

それは多くの場合、無地のCTEができることを見落としていますまだ追加するWITH RECURSIVE後編:

についてDISTINCT ON

+0

まだまだバグだ... –

+0

心配はありませんが、私はヒントを求めました。私は再帰的なCTEにも行きましたが、CTEの中に複数の式があることがわかっています(要求された識別子を追加するために、書き換えルールにrow_number()を追加するもの)。 PLPgSQL関数でループを使うというあなたの提案も良いヒントです。ありがとうございました。 – greg

+1

これは動作します。テーブル "c"のためのFROM句エントリへの無効な参照を回避するための '' LATERAL'ジョイン '' –

関連する問題