【问题标题】:Recursive SQL statement (Postgresql) - simplified version递归 SQL 语句 (Postgresql) - 简化版
【发布时间】:2012-07-19 15:10:30
【问题描述】:

这是此处发布的更复杂问题的简化问题:

Recursive SQL statement (PostgreSQL 9.1.4)

简化问题

假设您将上三角矩阵存储在 3 列(RowIndex、ColumnIndex、MatrixValue)中:

   ColumnIndex       
    1   2   3   4   5
1   2   2   3   3   4
2   4   4   5   6   X
3   3   2   2   X   X
4   2   1   X   X   X
5   1   X   X   X   X

X 值将使用以下算法计算:

M[i,j] = (M[i-1,j]+M[i,j-1])/2
(i= rows, j = columns, M=matrix)

Example:
M[3,4] = (M[2,4]+M[3,3])/2
M[3,5] = (m[2,5]+M[3,4])/2

所需的完整结果是:

   ColumnIndex       
    1   2   3    4      5
1   2   2   3    3      4
2   4   4   5    6      5
3   3   2   2    4      4.5
4   2   1   1.5  2.75   3.625
5   1   1   1.25 2.00   2.8125

样本数据:

create table matrix_data (
    RowIndex integer,
    ColumnIndex integer,
    MatrixValue numeric);

    insert into matrix_data values (1,1,2);
    insert into matrix_data values (1,2,2);
    insert into matrix_data values (1,3,3);
    insert into matrix_data values (1,4,3);
    insert into matrix_data values (1,5,4);
    insert into matrix_data values (2,1,4);
    insert into matrix_data values (2,2,4);
    insert into matrix_data values (2,3,5);
    insert into matrix_data values (2,4,6);
    insert into matrix_data values (3,1,3);
    insert into matrix_data values (3,2,2);
    insert into matrix_data values (3,3,2);
    insert into matrix_data values (4,1,2);
    insert into matrix_data values (4,2,1);
    insert into matrix_data values (5,1,1);

这个可以吗?

【问题讨论】:

  • 是的。 (这只是一个评论,所以我可以稍后找到这个问题)
  • 看起来您的预期输出中有错误:M[4,4] 为 2.75 (4.5 + 1)/2 = 5.5 = 2.75。只是想在发布之前确认我的解决方案是正确的。
  • @podiluska:您可以出于同样的目的使用“收藏”功能,而不会让全世界知道。

标签: sql postgresql common-table-expression recursive-query


【解决方案1】:

测试设置:

CREATE TEMP TABLE matrix (
    rowindex integer,
    columnindex integer,
    matrixvalue numeric);

INSERT INTO matrix VALUES
 (1,1,2),(1,2,2),(1,3,3),(1,4,3),(1,5,4)
,(2,1,4),(2,2,4),(2,3,5),(2,4,6)
,(3,1,3),(3,2,2),(3,3,2)
,(4,1,2),(4,2,1)
,(5,1,1);

使用 DO 在循环中运行 INSERT:

DO $$
BEGIN

FOR i IN 2 .. 5 LOOP
   FOR j IN 7-i .. 5 LOOP
      INSERT INTO matrix
      VALUES (i,j, (
         SELECT sum(matrixvalue)/2
         FROM   matrix
         WHERE  (rowindex, columnindex) IN ((i-1, j),(i, j-1))
         ));
   END LOOP;
END LOOP;

END;
$$

查看结果:

SELECT * FROM matrix order BY 1,2;

【讨论】:

    【解决方案2】:

    这可以在单个 SQL 选择语句中完成,但这只是因为不需要递归。我将概述解决方案。如果你真的想要 SQL 代码,请告诉我。

    首先,请注意,对总和有贡献的唯一项目是沿对角线。现在,如果我们遵循 (1, 5) 中值“4”的贡献,它对 (2,5) 的贡献为 4/2,对 (3,5) 的贡献为 4/4,对 (4,5) 的贡献为 4/8 )。每次贡献都会减半,因为 (a+b)/2 是 (a/2 + b/2)。

    当我们扩展它时,我们开始看到类似于帕斯卡三角形的模式。事实上,对于下三角矩阵中的任何给定点(在您有值的位置下方),您都可以找到对值有贡献的对角线元素。向上延伸一条垂直线以击中对角线,并延长一条水平线以击中对角线。这些是对角线的贡献者。

    他们贡献了多少?好吧,为此我们可以使用帕斯卡三角形。对于我们有值的下面的第一个对角线,贡献是 (1,1)/2。对于第二条对角线,(1,2,1)/4。对于第三个, (1,3,3,1)/8 。 . .等等。

    幸运的是,我们可以使用公式(组合数学中的“选择”函数)计算每个值的贡献。 2的幂很简单。而且,确定给定单元格与对角线的距离并不难。

    所有这些都可以组合成一个 Postgres SQL 语句。但是,@Erwin 的解决方案也有效。如果他的解决方案不能满足您的需求,我只想努力调试该语句。

    【讨论】:

      【解决方案3】:

      ...这里是带有多个嵌入式 CTE (tm) 的递归 CTE:

      DROP SCHEMA tmp CASCADE;
      CREATE SCHEMA tmp ;
      SET search_path=tmp;
      
      CREATE TABLE matrix_data (
          yyy integer,
          xxx integer,
          val numeric);
      
          insert into matrix_data (yyy,xxx,val) values
            (1,1,2) , (1,2,2) , (1,3,3) , (1,4,3) , (1,5,4)
          , (2,1,4) , (2,2,4) , (2,3,5) , (2,4,6)
          , (3,1,3) , (3,2,2) , (3,3,2)
          , (4,1,2) , (4,2,1)
          , (5,1,1)
              ;
      
      WITH RECURSIVE rr AS (
              WITH xx AS (
                      SELECT MIN(xxx) AS x0
                      , MAX(xxx) AS x1
                      FROM matrix_data
                      )
              , mimax AS (
                      SELECT generate_series(xx.x0,xx.x1) AS xxx
                      FROM xx
                      )
              , yy AS (
                      SELECT MIN(yyy) AS y0
                      , MAX(yyy) AS y1
                      FROM matrix_data
                      )
              , mimay AS (
                      SELECT generate_series(yy.y0,yy.y1) AS yyy
                      FROM yy
                      )
              , cart AS (
                      SELECT * FROM mimax mm
                      JOIN mimay my ON (1=1)
                      )
              , empty AS (
                      SELECT * FROM cart ca
                      WHERE NOT EXISTS (
                              SELECT *
                              FROM matrix_data nx
                              WHERE nx.xxx = ca.xxx
                              AND nx.yyy = ca.yyy
                              )
                      )
              , hot AS (
                      SELECT * FROM empty emp
                      WHERE EXISTS (
                              SELECT *
                              FROM matrix_data ex
                              WHERE ex.xxx = emp.xxx -1
                              AND ex.yyy = emp.yyy
                              )
                      AND EXISTS (
                              SELECT *
                              FROM matrix_data ex
                              WHERE ex.xxx = emp.xxx
                              AND ex.yyy = emp.yyy -1
                              )
                          )
              -- UPDATE from here:
              SELECT h.xxx,h.yyy, md.val / 2 AS val
              FROM hot h
              JOIN matrix_data md ON
                      (md.yyy = h.yyy AND md.xxx = h.xxx-1)
                      OR (md.yyy = h.yyy-1 AND md.xxx = h.xxx)
              UNION ALL
              SELECT e.xxx,e.yyy, r.val / 2 AS val
              FROM empty e
              JOIN rr r ON ( e.xxx = r.xxx+1 AND e.yyy = r.yyy)
                      OR ( e.xxx = r.xxx AND e.yyy = r.yyy+1 )
              )
      INSERT INTO matrix_data(yyy,xxx,val)
      SELECT DISTINCT yyy,xxx
              ,SUM(val)
      FROM rr
      GROUP BY yyy,xxx
              ;
      
      SELECT * FROM matrix_data
              ;
      

      新结果:

      NOTICE:  drop cascades to table tmp.matrix_data
      DROP SCHEMA
      CREATE SCHEMA
      SET
      CREATE TABLE
      INSERT 0 15
      INSERT 0 10
       yyy | xxx |          val           
      -----+-----+------------------------
         1 |   1 |                      2
         1 |   2 |                      2
         1 |   3 |                      3
         1 |   4 |                      3
         1 |   5 |                      4
         2 |   1 |                      4
         2 |   2 |                      4
         2 |   3 |                      5
         2 |   4 |                      6
         3 |   1 |                      3
         3 |   2 |                      2
         3 |   3 |                      2
         4 |   1 |                      2
         4 |   2 |                      1
         5 |   1 |                      1
         2 |   5 |     5.0000000000000000
         5 |   5 | 2.81250000000000000000
         4 |   3 | 1.50000000000000000000
         3 |   5 | 4.50000000000000000000
         5 |   2 | 1.00000000000000000000
         3 |   4 | 4.00000000000000000000
         5 |   3 | 1.25000000000000000000
         4 |   5 | 3.62500000000000000000
         4 |   4 | 2.75000000000000000000
         5 |   4 | 2.00000000000000000000
      (25 rows)
      

      【讨论】:

      • 哇,如果递归 CTEism 处于其终止状态! :) 如果您比较我的解决方案中的 ~ 15 行代码,这似乎相当可怕。 BTW,结果不对。
      • 我只是讨厌命令式、过程式代码,我必须做点什么!但这仍然不对...不能引用递归CTE两次+不能使用聚合。我被困住了。不错的尝试,不过 ;-] BTW:另一种方法是首先构建一个帕斯卡三角形,然后从中更新。顺序很重要。
      • 知道了。关键是将总和拉入外部查询。我现在可以获得 CTE 奖吗?
      • 我猜你应该得到一些奖牌。 :) +1 坚韧!
      • 您可能会从递归 CTE 中拉出所有 CTE,直到 cart(可能还有 empty)。另外,也许考虑在他们的系列生成器中为xxyy 做嵌套的SELECTs。 postGre 没有CROSS JOIN 子句(对于cart)吗?你也有可能达到这一点。就个人而言,考虑到问题的性质,我可能更喜欢递归查询。
      【解决方案4】:
       while (select max(ColumnIndex+RowIndex) from matrix_data)<10
       begin
            insert matrix_data
            select c1.RowIndex, c1.ColumnIndex+1, (c1.MatrixValue+c2.MatrixValue)/2 
            from matrix_data c1
                 inner join
                 matrix_data c2 
                 on c1.ColumnIndex+1=c2.ColumnIndex and c1.RowIndex-1 = c2.RowIndex   
            where c1.RowIndex+c1.ColumnIndex=(select max(RowIndex+ColumnIndex) from matrix_data) 
            and c1.ColumnIndex<5
       end
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-07
        • 2013-07-06
        • 2012-03-01
        • 2010-09-23
        • 1970-01-01
        • 2018-03-12
        • 1970-01-01
        相关资源
        最近更新 更多