簡體   English   中英

如何在postgresql中將兩行轉換為鍵值json對象?

[英]How to convert two rows into key-value json object in postgresql?

我有一個如下所示的數據模型,它被簡化為僅向您展示這個問題(底部的SQL Fiddle Link):

在此處輸入圖片說明

一個人在數據庫中表示為元表行,具有名稱和多個屬性,這些屬性作為鍵值對存儲在數據表中(鍵和值在單獨的列中)。

預期結果

現在我想檢索所有用戶的所有屬性。 屬性應在單獨的列中作為 json 對象返回。 例如:

name, data
Florian, { "age":23, "color":"blue" }
Markus, { "age":24, "color":"green" }

我的方法

現在我的問題是,我找不到在 postgres 中創建鍵值對的方法。 我嘗試了以下操作:

SELECT
  name,
  array_to_json(array_agg(row(d.key, d.value))) AS data
FROM meta AS m
JOIN (
  SELECT d.fk_id, d.key, d.value AS value FROM data AS d
  ) AS d
ON d.fk_id = m.id
GROUP BY m.name;

但它返回這個作為數據列:

[{"f1":"age","f2":24},{"f1":"color","f2":"blue"}]

其他解決方案

我知道有一個函數crosstab ,它使我能夠將數據表轉換為鍵作為列和值作為行表。 但這不是動態的。 而且我不知道一個人在數據表中有多少屬性。 所以這不是一個選擇。

我還可以使用兩個行值創建一個類似字符串的 json 並聚合它們。 但也許有更好的解決方案。

不,不可能更改數據模型,因為真正的數據模型已經被多方使用。

SQLFiddle

檢查並測試我為此問題創建的小提琴: http : //sqlfiddle.com/#!15/bd579/14

使用聚合函數json_object_agg(key, value)

select 
    name, 
    json_object_agg(key, value) as data
from data
join meta on fk_id = id
group by 1;

SQL小提琴。

該函數是在Postgres 9.4中引入的。

我找到了一種返回帶有動態列的交叉表數據的方法。 也許重寫這會更好地滿足您的需求:

CREATE OR REPLACE FUNCTION report.usp_pivot_query_amount_generate(
    i_group_id INT[],
    i_start_date TIMESTAMPTZ,
    i_end_date TIMESTAMPTZ,
    i_interval INT
    ) RETURNS TABLE (
    tab TEXT
    ) AS $ab$
DECLARE
    _key_id TEXT;
    _text_op TEXT = '';
    _ret TEXT;
BEGIN
    -- SELECT DISTNICT for query results
    FOR _key_id IN
    SELECT DISTINCT at_name
      FROM report.company_data_date cd 
      JOIN report.company_data_amount cda ON cd.id = cda.company_data_date_id 
      JOIN report.amount_types at ON cda.amount_type_id  = at.id 
     WHERE date_start BETWEEN i_start_date AND i_end_date
       AND group_id = ANY (i_group_id)
       AND interval_type_id = i_interval
    LOOP
    -- build function_call with datatype of column
        IF char_length(_text_op) > 1 THEN
            _text_op := _text_op || ', ' || _key_id || ' NUMERIC(20,2)';
        ELSE
            _text_op := _text_op || _key_id || ' NUMERIC(20,2)';
        END IF;
    END LOOP;
    -- build query with parameter filters
    _ret = '
        SELECT * FROM crosstab(''SELECT date_start, at.at_name,  cda.amount ct 
          FROM report.company_data_date cd 
          JOIN report.company_data_amount cda ON cd.id = cda.company_data_date_id 
          JOIN report.amount_types at ON cda.amount_type_id  = at.id 
         WHERE date_start between $$' || i_start_date::TEXT || '$$ AND $$' || i_end_date::TEXT || '$$ 
           AND interval_type_id = ' || i_interval::TEXT || ' 
           AND group_id = ANY (ARRAY[' || array_to_string(i_group_id, ',') || '])
         ORDER BY date_start'') 
            AS ct (date_start timestamptz, ' || _text_op || ')';
    RETURN QUERY
    SELECT _ret;
END;
$ab$ LANGUAGE 'plpgsql';

調用函數獲取字符串,然后執行字符串。 我想我嘗試在函數中執行它,但效果不佳。

當我需要更新一些 JSON 並刪除數據庫中的一些元素時,我遇到了同樣的問題。 下面的這個查詢對我來說效果很好,因為它保留了字符串引號但沒有將它們添加到數字中。

select
  '{' || substr(x.arr, 3, length(x.arr) - 4) || '}'
from
(
  select
     replace(replace(cast(array_agg(xx) as varchar), '\"', '"'), '","', ', ') as arr
  from
  (
  select
    elem.key,
    elem.value,
    '"' || elem.key || '":' || elem.value as xx
  from
    quote q
      cross join
    json_each(q.detail::json -> 'bQuoteDetail'-> 'quoteHC'->0)  as elem  
  where
    elem.key != 'allRiskItems'  
  ) f 
) x

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM