2

我有一张桌子:

   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 ) 列向上。但是,如果有baccab的星座, 它将匹配abc 10。如果有像bcc这样的行,那么它不会是像cbb这样的行。每场比赛都是独一无二的。

我认为最好的方法是使用 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++

编辑

  • 首先,示例表中有一个错误。有一行abccba。不可能。abc = cba!并且每一行必须是唯一的。
  • 示例:三个文本输入 abc | 公元前| C
  • 每个元素与每个元素: abc 、 acc 、 bbc 、 bcc 、 cbc 、 ccc
  • abc = 10; acc (与cac相同) = 10; 英国广播公司= 10; bcc(与cbc相同)= 30; CBC = 30; ccc(不匹配)= 0;结果 = 90
4

2 回答 2

2

认为这可能是你想要的:

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

简单版

使用contains@>和被<@操作符包含要简单得多:

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')vs.会失败('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 演示。

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

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

行值更复杂。我将三列放在一个数组中,并用unnest(). 由于您没有提供主键,因此我使用ctidas ad-hoc surrogate 主键 - 我需要它GROUP BY来将现在排序的内容填充(c1, c2, c3)到数组中。

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

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

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

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

指数/表现

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

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

于 2012-10-15T20:50:07.623 回答
0

您不需要为此编写函数。

首先,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() 称为“聚合”函数,聚合函数的其他示例是例如 MIN() 或 MAX()。请注意您实际尝试做的事情是通过每行连接的效果按一些结果 VARCHAR 列对行进行分组。

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

于 2012-10-15T20:37:37.420 回答