递归SQL语句(Postgresql)-简化版本
这是一个简化的问题,更复杂的问题发布在这里: 简化问题 假设上三角矩阵存储在3列中,RowIndex、ColumnIndex、MatrixValue:递归SQL语句(Postgresql)-简化版本,sql,postgresql,common-table-expression,recursive-query,Sql,Postgresql,Common Table Expression,Recursive Query,这是一个简化的问题,更复杂的问题发布在这里: 简化问题 假设上三角矩阵存储在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]
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);
可以这样做吗?测试设置:
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在循环中运行插入:
见结果:
SELECT * FROM matrix order BY 1,2;
测试设置:
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在循环中运行插入:
见结果:
SELECT * FROM matrix order BY 1,2;
这可以在一个SQL select语句中完成,但这只是因为不需要递归。我将概述解决方案。如果您真的需要SQL代码,请告诉我 首先,请注意,构成总和的唯一项目是沿对角线的。现在,如果我们按照1,5中的值4的贡献,它贡献4/2到2,5,4/4到3,5,4/8到4,5。因为a+b/2是a/2+b/2,所以每次的贡献都会减半 当我们扩展它时,我们开始看到一个类似于Pascal三角形的模式。事实上,对于下三角矩阵中的任何给定点,如果你有值,你可以找到对值有贡献的对角线元素。向上延伸一条垂直线以击中对角线,并延伸一条水平线以击中对角线。这些是对角线行中的贡献者 他们贡献了多少?那么,我们可以去帕斯卡三角形。对于下面有值的第一条对角线,贡献为1,1/2。对于第二条对角线,1,2,1/4。第三,1,3,3,1/8。等等 幸运的是,我们可以使用组合数学中的选择函数公式计算每个值的贡献。2的力量很简单。而且,确定给定单元格与对角线的距离并不太难
所有这些都可以组合成一条Postgres SQL语句。然而,@Erwin的解决方案也有效。如果他的解决方案不能满足您的需要,我只想把精力放在调试语句上。这可以在单个SQL select语句中完成,但这只是因为递归不是必需的。我将概述解决方案。如果您真的需要SQL代码,请告诉我 首先,请注意,构成总和的唯一项目是沿对角线的。现在,如果我们按照1,5中的值4的贡献,它贡献4/2到2,5,4/4到3,5,4/8到4,5。因为a+b/2是a/2+b/2,所以每次的贡献都会减半 当我们扩展它时,我们开始看到一个类似于Pascal三角形的模式。事实上,对于下三角矩阵中的任何给定点,如果你有值,你可以找到对值有贡献的对角线元素。向上延伸一条垂直线以击中对角线,并延伸一条水平线以击中对角线。这些是对角线行中的贡献者 他们贡献了多少?那么,我们可以去帕斯卡三角形。对于下面有值的第一条对角线,贡献为1,1/2。对于第二条对角线,1,2,1/4。第三,1,3,3,1/8。等等 幸运的是,我们可以使用组合数学中的选择函数公式计算每个值的贡献。2的力量很简单。而且,确定给定单元格与对角线的距离并不太难
所有这些都可以组合成一条Postgres SQL语句。然而,@Erwin的解决方案也有效。如果他的解决方案不能满足您的需要,我只想把精力放在调试语句上。。。。下面是带有多个嵌入式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)
。。。下面是带有多个嵌入式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)
对这只是一个注释,所以我可以在以后发现这个问题。看起来您的预期输出中有一个错误:M[4,4]be 2.75 4.5+1/2=5.5=2.75。只是想在发布之前确认我的解决方案是正确的。@podiluska:你可以使用favorite功能实现同样的目的,而不必让世界知道。是的。这只是一个注释,所以我可以在以后发现这个问题。看起来您的预期输出中有一个错误:M[4,4]be 2.75 4.5+1/2=5.5=2.75。只是想在发布之前确认我的解决方案是正确的。@podiluska:你可以使用favorite功能实现同样的目的,而不必让世界知道。哇,如果递归CTEism处于其终端状态,这是:如果您比较一下我的解决方案中的~15行代码,这看起来相当可怕。顺便说一句,结果不对。我只是讨厌命令式、过程性代码,我不得不做点什么!但这仍然是不对的。。。无法引用递归CTE两次+无法使用聚合。我卡住了。不过,尝试得不错;-]顺便说一句:另一种方法是先建立一个帕斯卡三角形,然后从中更新。秩序很重要,明白了。关键是将总和拉入外部查询。我现在能拿到CTE奖吗?我想你应该
我有某种奖章+1代表坚韧!您可能会通过购物车将所有CTE向上拉,然后清空,可能是从递归CTE中取出。此外,也可以考虑将XX和YY的嵌套选择转换为它们的系列生成器。postGre没有cart的交叉连接子句吗?你也有可能达到这一点。就个人而言,考虑到问题的性质,我可能更喜欢递归查询。哇,如果递归CTEism处于其终端状态,这将是:如果您比较一下我的解决方案中的~15行代码,这看起来相当可怕。顺便说一句,结果不对。我只是讨厌命令式、过程性代码,我不得不做点什么!但这仍然是不对的。。。无法引用递归CTE两次+无法使用聚合。我卡住了。不过,尝试得不错;-]顺便说一句:另一种方法是先建立一个帕斯卡三角形,然后从中更新。秩序很重要,明白了。关键是将总和拉入外部查询。我现在拿到CTE奖了吗?我想你应该得到某种奖章+1代表坚韧!您可能会通过购物车将所有CTE向上拉,然后清空,可能是从递归CTE中取出。此外,也可以考虑将XX和YY的嵌套选择转换为它们的系列生成器。postGre没有cart的交叉连接子句吗?你也有可能达到这一点。就个人而言,考虑到问题的性质,我可能更喜欢递归查询。