2015-05-04 113 views
2

我有這樣的一個表:PostgreSQL的行合併使用相同的密鑰(hstore或JSON)

+--------+--------------------+ 
| ID | Attribute  | 
+--------+--------------------+ 
| 1 |"color" => "red" |  
+--------+--------------------+ 
| 1 |"color" => "green" | 
+--------+--------------------+ 
| 1 |"shape" => "square" | 
+--------+--------------------+ 
| 2 |"color" => "blue" | 
+--------+--------------------+ 
| 2 |"color" => "black" | 
+--------+--------------------+ 
| 2 |"flavor" => "sweat" | 
+--------+--------------------+ 
| 2 |"flavor" => "salty" | 
+--------+--------------------+ 

我要運行一些Postgres的查詢得到的結果表是這樣的:

+--------+------------------------------------------------------+ 
| ID |     Attribute       | 
+--------+------------------------------------------------------+ 
| 1 |"color" => "red, green", "shape" => "square"   |  
+--------+------------------------------------------------------+ 
| 2 |"color" => "blue, black", "flavor" => "sweat, salty" | 
+--------+------------------------------------------------------+ 

屬性列可以是hstore或json格式。我在hstore中爲它編寫了一個例子,但如果我們無法在hstore中實現這一點,但在json中,我會將該列更改爲json。

我知道hstore不支持多個值的一個鍵,當我嘗試一些合併方法時,它只爲每個鍵保留一個值。但是對於json,我沒有發現任何支持多值合併的東西。我認爲這可以通過將同一個鍵的值合併爲一個字符串/文本並將其添加回鍵/值對來完成。但我堅持實施它。

注意:如果在某些功能中實現此功能,理想情況下功能中不應出現任何顏色,形狀等鍵,因爲鍵可以動態擴展。

有沒有人有這方面的想法?任何建議或頭腦風暴都可能有所幫助。謝謝!

回答

0

只是一個注意事項之前:在您desidered輸出我會用一些適當的json而不是那種看起來像。所以,根據我正確的輸出將是:

+--------+----------------------------------------------------------------------+ 
| ID |        Attribute        | 
+--------+----------------------------------------------------------------------+ 
| 1 | '{"color":["red","green"], "flavor":[], "shape":["square"]}'   |  
+--------+----------------------------------------------------------------------+ 
| 2 | '{"color":["blue","black"], "flavor":["sweat","salty"], "shape":[]}' | 
+--------+----------------------------------------------------------------------+ 

PL/pgSQL的功能,解析JSON屬性和執行動態查詢會做的工作,這樣的事情:

CREATE OR REPLACE FUNCTION merge_rows(PAR_table regclass) RETURNS TABLE (
    id   integer, 
    attributes json 
) AS $$ 
DECLARE 
    ARR_attributes text[]; 
    VAR_attribute text; 
    ARR_query_parts text[]; 
BEGIN 
    -- Get JSON attributes names 
    EXECUTE format('SELECT array_agg(name ORDER BY name) AS name FROM (SELECT DISTINCT json_object_keys(attribute) AS name FROM %s) AS s', PAR_table) INTO ARR_attributes; 

    -- Write json_build_object() query part 
    FOREACH VAR_attribute IN ARRAY ARR_attributes LOOP 
     ARR_query_parts := array_append(ARR_query_parts, format('%L, array_remove(array_agg(l.%s), null)', VAR_attribute, VAR_attribute)); 
    END LOOP; 

    -- Return dynamic query 
    RETURN QUERY EXECUTE format(' 
     SELECT t.id, json_build_object(%s) AS attributes 
      FROM %s AS t, 
      LATERAL json_to_record(t.attribute) AS l(%s) 
      GROUP BY t.id;', 
     array_to_string(ARR_query_parts, ', '), PAR_table, array_to_string(ARR_attributes, ' text, ') || ' text'); 
END; 
$$ LANGUAGE plpgsql; 

我經過測試,它似乎工作,它返回一個JSON。這裏是我的測試代碼:

CREATE TABLE mytable (
    id   integer NOT NULL, 
    attribute json NOT NULL 

); 
INSERT INTO mytable (id, attribute) VALUES 
(1, '{"color":"red"}'), 
(1, '{"color":"green"}'), 
(1, '{"shape":"square"}'), 
(2, '{"color":"blue"}'), 
(2, '{"color" :"black"}'), 
(2, '{"flavor":"sweat"}'), 
(2, '{"flavor":"salty"}'); 

SELECT * FROM merge_rows('mytable'); 

當然你也可以通過idattribute列名作爲參數,以及也許細化功能了一下,這只是給你一個想法。

編輯:如果你在9.4,請考慮使用jsonb數據類型,它會好得多,給你改進的餘地。您只需要將json_*函數更改爲它們的jsonb_*等效函數。

+0

非常感謝您的快速回復。我無法將我的postgres升級到9。4來測試它昨天(json_to_record()需要9.4)。有很多員工需要在postgres中學習。我查找了PAR_table,它看起來像表名的文本表示,並且類似地,PAR_where_clause是where子句的文本。但是我還沒有找到解釋它如何工作的定義或者它的定義,和regclass一樣。您能否介紹一下這些內容,或者提供一些我可以閱讀的來源或文章? – icebox

+0

另外,正如我在文章中提到的,屬性列是以hstore格式編寫的,它看起來像json。 – icebox

+0

爲了防止任何SQL注入或不良行爲PAR_table被作爲類型[regclass](http://www.postgresql.org/docs/current/static/datatype-oid.html)傳遞給函數,並通過[ format()](http://www.postgresql.org/docs/current/static/functions-string.html#FUNCTIONS-STRING-FORMAT)函數。關於hstore,如果你使用9.4,我建議你使用[jsonb](http://www.postgresql.org/docs/current/static/datatype-json.html)數據類型,它有很多好處。你在你的問題中說過改變數據類型是一個選項。 9.4,我認爲這是你能做的最好的。 – Eggplant

0

如果你只是想這個用於顯示目的,這可能是不夠的:

select id, string_agg(key||' => '||vals, ', ') 
from (
    select t.id, x.key, string_agg(value, ',') vals 
    from t 
    join lateral each(t.attributes) x on true 
    group by id, key  
) t 
group by id; 

如果你不是在9.4,您不能使用橫向聯接:

select id, string_agg(key||' => '||vals, ', ') 
from (
    select id, key, string_agg(val, ',') as vals 
    from (
    select t.id, skeys(t.attributes) as key, svals(t.attributes) as val 
    from t 
) t1 
    group by id, key 
) t2 
group by id; 

這將返回:

id | string_agg         
---+------------------------------------------- 
1 | color => red,green, shape => square  
2 | color => blue,black, flavor => sweat,salty 

SQLFiddle:http://sqlfiddle.com/#!15/98caa/2