2014-02-24 42 views
2

我有很重的查詢與FOR循環重寫,並希望做到這一點更簡單,使用更多的SQL而不是plpgsql結構。查詢看起來像:從plpgsql中的FOR循環切換到基於集合的SQL命令

FOR big_xml IN SELECT unnest(xpath('//TAG1', my_xml)) LOOP 
    str_xml = unnest(xpath('/TAG2/TYPE/text()', big_xml)); 

     FOR single_xml IN SELECT unnest(xpath('/TAG2/single', big_xml)) LOOP 

     CASE str_xml::INT 
      WHEN 1 
      THEN 
      INSERT INTO tab1(id, xml) VALUES (1, single_xml); 
      WHEN 2 
      THEN 
      INSERT INTO tab2(id, xml) VALUES (1, single_xml); 
      WHEN 3 
      [...] 
      WHEN 11 
      [...] 
      ELSE 
       RAISE EXCEPTION 'something' 
      END CASE; 
     END LOOP; 
    END LOOP; 

RETURN xmlelement(NAME "out", xmlforest(1 AS out)); 

我開始重寫它有更好的表現:

INSERT INTO tab1(id, xml) 
    SELECT 1, unnest(xpath('/TAG2/single', (SELECT unnest(xpath('//TAG1', my_xml)))); 

但我不知道如何處理這些CASE ... INSERT語句。 任何想法? 或者,也許我的做法是完全錯誤的?

編輯14年2月25日:在PostgreSQL 9.3.1

+0

一如往常,樣本值和您的Postgres版本將有所幫助。 –

+1

'unnest()'在你的原始語句中沒有任何意義:'str_xml = unnest(xpath('/ TAG2/TYPE/text()',big_xml))''。如果返回多行,則代碼會中斷;如果這不可能發生,'unnest()'沒有意義... –

回答

2

要插入不同的表,這取決於你的數據,你需要一些程序代碼。但是你可以湊合着用一個單迴路和嵌套unnest()

DEFINE 
    int_xml int; 
    single_xml xml; 
BEGIN 
    FOR r IN      -- or you can use two variables 
     SELECT xpath('/TAG2/single', big_xml))[1]::text::int AS int_xml 
      , unnest(xpath('/TAG2/TYPE/text()', big_xml)) AS single_xml 
     FROM (SELECT unnest(xpath('//TAG1', my_xml)) AS big_xml) sub 
    LOOP 
     CASE int_xml 
     WHEN 1 THEN 
     INSERT INTO tab1(id, xml) VALUES (1, single_xml); 
     WHEN 2 THEN 
     INSERT INTO tab2(id, xml) VALUES (1, single_xml); 
     WHEN 3 THEN 
      ... 

     ELSE 
     RAISE EXCEPTION 'something' 
     END CASE; 
    END LOOP; 
END 

或採取臨時表或CTE和追加多INSERT語句。對於很多行應該更快。

WITH cte AS (
    SELECT xpath('/TAG2/single', big_xml))[1]::text::int AS int_xml 
     , unnest(xpath('/TAG2/TYPE/text()', big_xml)) AS single_xml 
    FROM (SELECT unnest(xpath('//TAG1', my_xml)) AS big_xml) sub 
), i1 AS (
    INSERT INTO tab1(id, xml) 
    SELECT 1, single_xml 
    FROM cte 
    WHERE int_xml = 1 
), i2 AS (
    INSERT INTO tab2(id, xml) 
    SELECT 1, single_xml 
    FROM cte 
    WHERE int_xml = 2 
), 
    ... 
) 
SELECT int_xml INTO my_var 
FROM cte 
WHERE int_xml <> ALL ({'1','2','3','11'}::int[]) 
LIMIT 1; 

IF FOUND THEN 
    RAISE EXCEPTION 'One of the int_xml did not fit: %!', my_var; 
END IF; 

最後一位補償了原件中的異常。

如果您的big_xml真的很大,請確保您爲CTE提供足夠的臨時緩衝區。如果常規設置太低,請僅增加此會話的temp_buffers設置。在這個相關的答案的詳細信息:
How can I insert common data into a temp table from disparate schemas?

+0

感謝您的回答,它一如既往地出色,但我對INSERT語句的描述不夠詳細。當值等於1時,插入到tab1,當2到tab2等。所以我應該使用動態SQL呢?或者有一種方法可以用標準SQL編寫它? – Borys

+0

所以我需要在xml中的T​​YPE和表名之間進行一些映射。也許通過unlogged表和加入? – Borys

+1

@Borys;我重寫了答案以適應不同的目標表。考慮更新。 –