【问题标题】:Merge hstore data from multiple rows合并来自多行的 hstore 数据
【发布时间】:2014-09-15 11:20:13
【问题描述】:

想象一下我有一张这样定义的表格:

CREATE TABLE test (
  values HSTORE NOT NULL
);

假设我插入了几条记录,结果如下:

values
-----------------------------
"a"=>"string1","b"=>"string2"
"b"=>"string2","c"=>"string3"

有什么方法可以进行聚合查询,为我提供一个新的 hstore,其中包含所有行的合并键(和值)。

伪查询:

SELECT hstore_sum(values) AS value_sum FROM test;

想要的结果:

value_sum
--------------------------------------------
"a"=>"string1","b"=>"string2","c"=>"string3"

我知道每个键的不同值可能存在冲突,但在我的情况下,选择值的顺序/优先级并不重要(它甚至不必是确定性的,因为它们对于相同的键)。

这可能是开箱即用的还是你必须使用一些特定的自制 SQL 函数或其他方法来做到这一点?

【问题讨论】:

    标签: postgresql aggregate hstore


    【解决方案1】:

    你可以做很多事情,f.ex:

    我的第一个想法是使用each() 函数,并分别聚合键和值,例如:

    SELECT  hstore(array_agg(key), array_agg(value))
    FROM    test,
    LATERAL each(hs);
    

    但这表现最差。

    您也可以使用hstore_to_array() 函数来构建一个键值更改数组,例如 (@JakubKania):

    SELECT  hstore(array_agg(altering_pairs))
    FROM    test,
    LATERAL unnest(hstore_to_array(hs)) altering_pairs;
    

    但这还不完美。

    您可以依赖 hstore 值的表示形式,并构建一个字符串,其中将包含您的所有对:

    SELECT  hstore(string_agg(nullif(hs::text, ''), ','))
    FROM    test;
    

    这是相当快的。但是,如果您愿意,可以使用custom aggregate function(可以使用内置的hstore 连接):

    CREATE AGGREGATE hstore_sum (hstore) (
        SFUNC = hs_concat(hstore, hstore),
        STYPE = hstore
    );
    
    -- i used the internal function (hs_concat) for the concat (||) operator,
    -- if you do not want to rely on this function,
    -- you could easily write an equivalent in a custom SQL function
    
    SELECT hstore_sum(hs)
    FROM   test;
    

    SQLFiddle

    【讨论】:

    • 它如何与“内部”函数一起工作?另外,我需要在每个连接上创建 AGGREGATE 函数还是只需要一次? (就像 CREATE EXTENSION hstore 是一次性的)。
    • @ChristianP。 internal (在这种情况下)意味着它没有被记录。 hstore 文档列出了串联运算符||,但没有列出其底层函数(hs_concat)。这通常意味着hstore 的创建者希望他们的用户使用运算符,而不是函数(例如,因为该函数可能是一个实现细节)。 -- 是的,您只需要声明一次聚合函数 - 就像普通的用户定义函数一样 - 并且每次都需要使用它。
    • 好的,有道理。我唯一担心的是内部函数会被重命名、更改等,而我假设它们由于版本之间的奇偶性原因而保持函数相同。它现在按预期工作:)
    • @ChristianP。如果您担心,您可以为此目的创建一个自定义函数 f.ex。 CREATE FUNCTION hstore_concat(hstore, hstore) RETURNS hstore LANGUAGE SQL AS 'SELECT $1 || $2';
    • 好主意。感谢(非常)完整的答案:)
    【解决方案2】:

    没有用于此的内置函数,但 hstore 提供了一些函数可以将其转换为其他内容,例如数组。所以我们将其转换为数组,合并数组并从最终数组创建 hstore:

    SELECT hstore(array_agg(x)) FROM 
    (SELECT unnest(hstore_to_array(hs)) AS x
      FROM test)
    as q;
    

    http://sqlfiddle.com/#!15/cb11a/1

    附:其他一些组合(例如使用 JSON)可能更有效。

    【讨论】:

      【解决方案3】:

      这是我写的,现在可以在生产中使用。我避免类型之间的过度转换,例如hstorearray。我也不会直接使用hs_concat 作为sfunc,因为如果它聚合的任何哈希值是NULL,它会计算NULL

      CREATE OR REPLACE FUNCTION public.agg_hstore_sum_sfunc(state hstore, val hstore)
      RETURNS hstore AS $$
      BEGIN
          IF val IS NOT NULL THEN
              IF state IS NULL THEN
                  state := val;
              ELSE
                  state := state || val;
              END IF;
          END IF;
          RETURN state;
      END;
      $$ LANGUAGE 'plpgsql';
      
      CREATE AGGREGATE public.sum(hstore) (
          SFUNC = public.agg_hstore_sum_sfunc,
          STYPE = hstore
      );
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-27
        • 1970-01-01
        • 2016-04-29
        • 1970-01-01
        • 2017-01-31
        相关资源
        最近更新 更多