【问题标题】:Postgres SQL function string_to_arrayPostgresql 函数 string_to_array
【发布时间】:2012-10-05 20:51:45
【问题描述】:

我有一张桌子:

   c1|c2|c3|c4
-----+--+--+----    
    a  b c  10
    a  a b  20
    c  a c  10
    b  b c  10
    c  b c  30

我想编写一个函数,其中输入是 3 个字符串/文本,例如('abcbdc'),比较每个元素彼此,查找是否存在此组合的行,将第 4 (c4) 列的数量相加。但是,如果有 b a cc a b 的星座,它将匹配 a b c 10。如果有像 b c c 这样的行,那么它不会是像 c b b 这样的行。每场比赛都是独一无二的。

我认为最好使用 string_to_array(text, text)。

我整理了一些伪代码,但不知道如何用 SQL 编写它。可能逻辑也错了。

function (x,y,z)
 res = 0
 x_array = string_to_array(x, ' ')
 y_array = string_to_array(y, ' ')
 z_array = string_to_array(z, ' ')

 foreach(x_item in x_array)
  foreach(y_item in y_array)
   foreach(z_item in z_array)
    if  (c1 = (x_item || y_item || z_item ) && c2 = (x_item || y_item || z_item ) && c3 = (x_item || y_item || z_item ))
     res++

编辑

  • 首先,示例表中有一个错误。有一行a b cc b a。不可能。 a b c = c b a !并且每一行都必须是唯一的。
  • 示例:三个文本输入a b c | b c | c
  • 每个元素与每个元素:a b c、a c c、b b c、b c c、c b c、c c c
  • a b c = 10; a c c(与c a c相同)= 10; b b c = 10; b c c(与c b c相同)= 30; c b c = 30; c c c (不匹配) = 0;结果 = 90

【问题讨论】:

  • 你能澄清你的文字吗?这对我来说似乎很矛盾。特别要明确你的结果应该是什么样子。添加示例。
  • 新用户提示:如果您发现某些答案有帮助/有用,您可能需要接受/点赞。

标签: arrays function postgresql


【解决方案1】:

您不需要为此编写函数。

首先,postgresql ( sql ) 没有“字符串”,它是“文本”或“varchar”。

其次,你需要一个这样的 SQL 查询:

SELECT ( DISTINCT ( c1 || c2 || c3 )) AS txtcol, SUM (c4) AS rowsum; 

SELECT ( DISTINCT ( c1 || c2 || c3 )) AS txtcol, SUM(c4) AS numsum GROUP BY txtcol;

暂时不记得确切的语法,你需要解决它, 无论如何,关键是您需要将 varchar 列与一些内置的 像 CONCAT 或“||”这样的函数运算符,然后按数字列求和/分组。一切你需要的 是连接列,并为生成的所有列命名。 确切地说,您甚至不需要在结果表上显示连接列, 例如,您可以只输出总和和汇总的行数。

理论上,您可以为此编写 SQL 函数或 PL/SQL 函数,但我确信这不是必需的,在我看来,您的情况很简单,可以在没有函数的情况下实现您想要的结果。内置汇总函数 SUM() 称为“聚合”函数,聚合函数的其他示例例如最小值()或最大值()。 请注意您实际尝试做的事情是通过每行连接的效果按一些结果 VARCHAR 列对行进行分组。

编辑:SQL 或过程 SQL 中的“数组”是一些内部处理的数组,不要将它们与关系(数据库中的表,也不与作为 SELECT 结果的表混淆)。我认为您也不需要 SQL 数组,这项任务实际上并不像看起来那么难。

【讨论】:

    【解决方案2】:

    认为这可能就是你想要的:

    从给定的三个标记集与列 (c1, c2, c3) 匹配的所有行中返回列 c4 的总和。

    罢工>

    简单版

    contains @> and is contained <@ by operators 更简单:

    SELECT sum(c4) AS sum_of_matching_c4
    FROM   tbl
    WHERE  ARRAY[c1,c2,c3] <@ ARRAY['b', 'a', 'c'] -- strings in arbitrary order
    AND    ARRAY[c1,c2,c3] @> ARRAY['b', 'a', 'c'];
    

    抱歉,('b', 'c', 'c')('c', 'b', 'b') 相比将失败。

    缓慢而确定

    WITH i(arr) AS (
       SELECT ARRAY(VALUES ('b'), ('c'), ('c') ORDER BY 1)  -- input once
       )                                                    -- in arbitrary order
    SELECT sum(c4) AS sum_of_matching_c4
    FROM  (
      SELECT c4, array_agg(x ORDER BY x) AS arr
      FROM  (
          SELECT ctid, c4, unnest(ARRAY[c1,c2,c3]) AS x
          FROM   tbl t, i
          WHERE  ARRAY[c1,c2,c3] <@ arr -- optional pre-selection
          AND    ARRAY[c1,c2,c3] @> arr -- for better performance?
          ) a
       GROUP BY ctid, c4
       ) b
    JOIN i USING (arr)
    

    -> sqlfiddle demo.

    主要的困难是在行对列的值进行排序。

    对于您的输入(3 个字符串),我在 WHERE 子句中使用 VALUE 表达式在 CTE 中实现这一点,我立即订购它并将其收集到一个数组中。为方便起见,我使用 CTE,因此我们只需在一个位置输入值。

    行值更复杂。我将三列放在一个数组中,并用unnest() 将其分成几行。由于您没有提供主键,我使用 ctid 作为临时代理主键 - 我需要 GROUP BY 将现在排序的 (c1, c2, c3) 填充到数组中。

    最后,我总结了所有 c4 现在排序的数组完全匹配的行。

    注意:我明确使用string_agg(),因为那样会产生不同的结果。考虑:

    'abc' 'cde' 'fgh'
    'ab' 'ccdef' 'gh'
    

    .. 如果连接起来,则结果是相同的字符串。

    索引/性能

    您可以考虑保存预先订购的数据以加快查询速度。即时进行是昂贵的。 IE。您可以预先生成已排序的数组并将其保存为冗余列,然后您可以使用索引支持该列。对于冗余数据存储的成本,应该快几个数量级。
    如果您正在处理长字符串,类似于我在related answer on dba.SE 中概述的解决方案可能是最好的做法。

    或者(首选!)保证(c1, c2, c3) 始终按升序存储。您可以使用触发器BEFORE INSERT OR UPDATE 来保持行内的值有序。没有冗余存储,您可以简单地在三列上创建一个multi-column index 并一一进行比较(而不是像我的示例中那样比较数组)。

    【讨论】:

    • 您可能会考虑保存预先订购的数据以加快查询速度。您能解释一下吗?在这个例子中你会保存什么。?不幸的是,该表已经有 100 万个唯一行。
    • @user1748124:我在回答中添加了一些关于预购数据和索引的内容。
    猜你喜欢
    • 1970-01-01
    • 2015-04-14
    • 2012-08-03
    • 2017-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-16
    相关资源
    最近更新 更多