Postgresql函数string_to_array

时间:2012-10-15 20:02:47

标签: arrays function postgresql

我有一张桌子:

   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个字符串/文本的函数,例如(' abc bd c '),比较每个元素相互之间,找出这个组合是否存在一行,并将第四个( c4 )列的数量加起来。但如果存在 b a c c a b 的星座,它将匹配 a b c 10 。如果有像 b c c 这样的行,那么它就不会像 c b b 那样。每场比赛都是独一无二的。

我认为最好的方法是使用string_to_array(文本,文本)。

我把一些伪代码放在一起,但不知道如何在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 c c 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

2 个答案:

答案 0 :(得分:2)

认为这可能是你想要的:

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

<击>

<击>

简易版

使用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子句中使用CTE中的VALUE表达式实现了这一点,我立即订购并将其收集到一个数组中。为方便起见,我使用CTE,因此我们只需在一个地方输入值。

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

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

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

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

..如果连接,则产生相同的字符串。

指数/表现

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

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

答案 1 :(得分:0)

您无需为此编写函数。

首先,postgresql(sql)没有“字符串”,它是“text”或“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或“||”这样的功能运算符,然后按数字列汇总/分组。一切你需要的 是连接列,并为结果列提供一个名称。 确切地说,您甚至不需要在结果表上显示连接列, 例如,你可以只输出sums和sumarized的行数。

理论上你可以为此编写SQL函数或PL / SQL函数,但我确定它没有必要,你的情况似乎很简单,能够在没有函数的情况下实现你想要的结果。内置的sumarizing函数SUM()被称为“聚合”函数,聚合函数的其他示例例如是MIN()或MAX()。 请注意您实际尝试执行的操作,即通过每行连接的效果对某些结果VARCHAR列进行分组。

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