我正在尝试编写一些Oracle 11g SQL,但是遇到了一些鸡与蛋的问题。我正在寻找类似电子表格的行为。我找到了一个确实使用OracleMODEL子句的解决方案,但是性能并不理想。所以我想知道“非MODEL”解决方案在技术上是否可行。
MODEL
这是一个玩具示例,演示了我正在尝试做的事情。给定此表:
CREATE TABLE t (id NUMBER PRIMARY KEY, n NUMBER); INSERT INTO t (id, n) VALUES (2, 0); INSERT INTO t (id, n) VALUES (3, 1); INSERT INTO t (id, n) VALUES (5, 1); INSERT INTO t (id, n) VALUES (7, 2); INSERT INTO t (id, n) VALUES (11, 3); INSERT INTO t (id, n) VALUES (13, 5); INSERT INTO t (id, n) VALUES (17, 8); INSERT INTO t (id, n) VALUES (19, 13);
我想计算两个附加的派生列,分别将它们称为X和Y。
X
Y
以下是关于如何的规则X和Y将要被计算:
X:对于第一行(由ID的最小值定义),设置X为N。对于所有后续行,按排序,的值X应比上一个的值小1 。Y``ID Y:两次N加号X。
X:对于第一行(由ID的最小值定义),设置X为N。对于所有后续行,按排序,的值X应比上一个的值小1 。Y``ID
N
Y``ID
Y:两次N加号X。
接下来的几个步骤说明了如果我要手动执行此操作,我将如何填写所需的视图。首先,给定数据的前几行:
ID N X Y --- --- --- --- 2 0 3 1 5 1 7 2 ....
由于我们位于第一行,因此X应将其设置为N或0。 Y应该是2 * N + X或0。
0
2 * N + X
ID N X Y --- --- --- --- 2 0 0 0 3 1 5 1 7 2 ....
现在,由于我们不再位于第一行,因此从现在开始X应该总是比前一行小一Y。在第二行中,这意味着X=(上一个Y)- 1= 0 - 1= -1。第二行Y是2 * N + X或2 * (1) + (-1)= 1。
1
0 - 1
-1
2 * (1) + (-1)
ID N X Y --- --- --- --- 2 0 0 0 3 1 -1 1 5 1 7 2 ....
如果继续进行数学运算,则可以得到以下结果:
ID N X Y --- --- --- --- 2 0 0 0 3 1 -1 1 5 1 0 2 7 2 1 5 11 3 4 10 13 5 9 19 17 8 18 34 19 13 33 59
给定用于X和Y计算的规则,是否有可能不必诉诸该MODEL条款而获得此结果?
我不是在基于此特定示例寻找数学上的简化;这只是我想出的一个玩具例子,展示了我在实际问题中所面临的那种相互依存。
PS:这是MODEL我能够拼凑而成的示例,它确实生成了此输出;也许有可能进行改进以提高性能?
SQL> WITH u AS ( 2 SELECT ROW_NUMBER() OVER (ORDER BY t.id) r 3 , t.id 4 , t.n 5 FROM t 6 ) 7 SELECT r 8 , id 9 , n 10 , x 11 , y 12 FROM u 13 MODEL 14 DIMENSION BY (r) 15 MEASURES (id 16 , n 17 , CAST(NULL AS NUMBER) x 18 , CAST(NULL AS NUMBER) y) RULES AUTOMATIC ORDER 19 ( x[1] = n[cv()] 20 , y[r] = 2 * n[cv()] + x[cv()] 21 , x[r > 1] ORDER BY r = y[cv() - 1] - 1 22 ) 23 ; R ID N X Y ---------- ---------- ---------- ---------- ---------- 1 2 0 0 0 2 3 1 -1 1 3 5 1 0 2 4 7 2 1 5 5 11 3 4 10 6 13 5 9 19 7 17 8 18 34 8 19 13 33 59 8 rows selected. SQL>
谢谢。
您可以使用递归子查询分解(也称为递归CTE):
with tmp as ( select t.*, row_number() over (order by t.id) as rn from t ), r (id, n, x, y, rn) as ( select id, n, 0, 0, rn from tmp where rn = 1 union all select tmp.id, tmp.n, r.y - 1, (tmp.n * 2) + r.y - 1, tmp.rn from r join tmp on tmp.rn = r.rn + 1 ) select id, n, x, y from r order by rn; ID N X Y ---------- ---------- ---------- ---------- 2 0 0 0 3 1 -1 1 5 1 0 2 7 2 1 5 11 3 4 10 13 5 9 19 17 8 18 34 19 13 33 59
SQL小提琴。
它基本上是逐步执行您的手动步骤。锚构件是你的第一个手动工序,设置x与y第一行都为零。然后,递归成员将执行您指定的计算。(x在计算该行的时y,您不能引用新计算的值,因此您必须将其重复为(tmp.n * 2) + r.y - 1)。该rn只是通过ID,以保持行的订单,同时使其更容易找到下一行-所以你可以看看rn + 1,而不是直接找到下一个最大的ID值。
x
y
(tmp.n * 2) + r.y - 1
rn
rn + 1
样本数据的性能没有显着差异,但是添加一千行后,model子句大约需要5秒,而递归CTE大约需要1秒。使用另外一千行模型需要约20秒,而CTE需要约3秒;另一千行模型花费了大约40秒,而CTE花费了大约6秒;而另外一千行(总共4,008行)的模型则花费了约75秒,而CTE花费了约10秒。(我很无聊地等待更多版本的模型版本;五分钟之内用10,000杀死了它)。我真的不能说这将如何处理您的真实数据,但是在此基础上,可能值得尝试。