递归SQL

时间:2015-11-22 17:55:26

标签: sql oracle recursion

此问题是this question的扩展和简化版本。

我一直试图在SQL中解决以下迭代方程:

U^{F,D}_{t,p} = (\sum_{D} U^{F,D}_{t-1,p} + C_{t-1,p} )*R^{F,D}_{t-1,p}

导致:

enter image description here

我能想到的最接近的类比是U^{F,D}_{t,p}是一些品牌F的汽车,具有某种颜色(D),是汽车经销商(p })在时间t可用。所以上面的等式基本上说:从t-1前一天(即U^{F,D}_{t-1,p})获取汽车的单位,总和颜色(\sum_{D}),然后加上总和a {{ 1}}来自前一天的值(C,无论是什么),并且从前一天(C_{t-1,p})乘以其他数字R,无论那是什么)。

简化问题

我设法解决了上述等式的简化形式,即:

enter image description here

即没有汽车颜色的总和(R^{F,D}_{t-1,p})。示例数据和SQL查询是in the fiddle that I link,但我将其粘贴在此处以供参考:

完整数据:

D

示例数据:

以下演示了汽车经销商CREATE TABLE DYNAMICS ( T DATE, T_M1 DATE, P INTEGER, F VARCHAR(255), DELTA_F VARCHAR(255), R_T_M1 NUMBER, C_T_M1 NUMBER, U_T_M1 NUMBER, R_T NUMBER, C_T NUMBER, U_T NUMBER ); -- DAY 1, P_1 INSERT INTO DYNAMICS VALUES ( TO_DATE('01.01.2015','DD.MM.YYYY HH24:MI:SS'), TO_DATE('31.12.2014','DD.MM.YYYY HH24:MI:SS'), 1,'BMW','RED', 0.5, 0.6, NULL, 0.7,0.8,100.0 ); INSERT INTO DYNAMICS VALUES ( TO_DATE('01.01.2015','DD.MM.YYYY HH24:MI:SS'), TO_DATE('31.12.2014','DD.MM.YYYY HH24:MI:SS'), 1,'MERCEDES','RED', 0.5, 0.6, NULL, 0.7,0.8,50.0 ); -- DAY 1, P_2 INSERT INTO DYNAMICS VALUES ( TO_DATE('01.01.2015','DD.MM.YYYY HH24:MI:SS'), TO_DATE('31.12.2014','DD.MM.YYYY HH24:MI:SS'), 2,'BMW','RED', 0.5, 0.6, NULL, 0.7,0.8,10.0 ); INSERT INTO DYNAMICS VALUES ( TO_DATE('01.01.2015','DD.MM.YYYY HH24:MI:SS'), TO_DATE('31.12.2014','DD.MM.YYYY HH24:MI:SS'), 2,'MERCEDES','RED', 0.5, 0.6, NULL, 0.7,0.8,5.0 ); -- DAY 2, P_1 INSERT INTO DYNAMICS VALUES ( TO_DATE('02.01.2015','DD.MM.YYYY HH24:MI:SS'), TO_DATE('01.01.2015','DD.MM.YYYY HH24:MI:SS'), 1,'BMW','RED', 0.7, 0.8, 100, 0.9,0.9, NULL ); INSERT INTO DYNAMICS VALUES ( TO_DATE('02.01.2015','DD.MM.YYYY HH24:MI:SS'), TO_DATE('01.01.2015','DD.MM.YYYY HH24:MI:SS'), 1,'MERCEDES','RED', 0.7, 0.8, 50, 0.6,0.5, NULL ); -- DAY 2, P_2 INSERT INTO DYNAMICS VALUES ( TO_DATE('02.01.2015','DD.MM.YYYY HH24:MI:SS'), TO_DATE('01.01.2015','DD.MM.YYYY HH24:MI:SS'), 2,'BMW','RED', 0.7, 0.8, 10, 0.7,0.8, NULL ); INSERT INTO DYNAMICS VALUES ( TO_DATE('02.01.2015','DD.MM.YYYY HH24:MI:SS'), TO_DATE('01.01.2015','DD.MM.YYYY HH24:MI:SS'), 2,'MERCEDES','RED', 0.7, 0.8, 5, 0.3,0.3, NULL ); -- DAY 3, P_1 INSERT INTO DYNAMICS VALUES ( TO_DATE('03.01.2015','DD.MM.YYYY HH24:MI:SS'), TO_DATE('02.01.2015','DD.MM.YYYY HH24:MI:SS'), 1,'BMW','RED', 0.9, 0.9, NULL, 0.2,0.3, NULL ); INSERT INTO DYNAMICS VALUES ( TO_DATE('03.01.2015','DD.MM.YYYY HH24:MI:SS'), TO_DATE('02.01.2015','DD.MM.YYYY HH24:MI:SS'), 1,'MERCEDES','RED', 0.6, 0.5, NULL, 1.7,1.8, NULL ); -- DAY 3, P_2 INSERT INTO DYNAMICS VALUES ( TO_DATE('03.01.2015','DD.MM.YYYY HH24:MI:SS'), TO_DATE('02.01.2015','DD.MM.YYYY HH24:MI:SS'), 2,'BMW','RED', 0.7, 0.8, NULL, 0.2,0.3, NULL ); INSERT INTO DYNAMICS VALUES ( TO_DATE('03.01.2015','DD.MM.YYYY HH24:MI:SS'), TO_DATE('02.01.2015','DD.MM.YYYY HH24:MI:SS'), 2,'MERCEDES','RED', 0.3, 0.3, NULL, 0.8,0.9, NULL ); 的示例数据,颜色p=1的汽车模型F=BMW(数学等式中的D=RED在SQL中称为D )。初始条件(DELTA)在这里2015-01-01。对于t=0的所有日期,都会给出tt)和R_T, C_Tt-1)处的所有参数。了解它们,任务是计算所有日期R_T_M1, C_T_M1的汽车单位。

t > t=0

QUERY:

为了解决简化问题,我在此粘贴I have come up with the query in the linked fiddle以供参考:

|                         T |                       T_M1 | P |   F | DELTA_F | R_T_M1 | C_T_M1 | U_T_M1 | R_T | C_T |    U_T |
|---------------------------|----------------------------|---|-----|---------|--------|--------|--------|-----|-----|--------|
| January, 01 2015 00:00:00 | December, 31 2014 00:00:00 | 1 | BMW |     RED |    0.5 |    0.6 | (null) | 0.7 | 0.8 |    100 |
| January, 02 2015 00:00:00 |  January, 01 2015 00:00:00 | 1 | BMW |     RED |    0.7 |    0.8 |    100 | 0.9 | 0.9 | (null) |
| January, 03 2015 00:00:00 |  January, 02 2015 00:00:00 | 1 | BMW |     RED |    0.9 |    0.9 | (null) | 0.2 | 0.3 | (null) |

对于上面粘贴的示例数据,此查询会产生:

-- 
-- SQL
-- T -> t 
-- T_M1 -> t-1 
-- 
WITH RECU(  T, T_M1, P, F, DELTA_F, 
            R_T_M1, C_T_M1, U_T_M1, 
            R_T, C_T, U_T ) AS (
    -- Anchor member.
    SELECT  T, T_M1, P, F, DELTA_F, 
            R_T_M1, C_T_M1, 
            U_T_M1, 
            R_T, C_T, 
            U_T
    FROM DYNAMICS 
        -- Initial condition: U_{t-1} does not exist, and U_{t=0} is given
        WHERE  ( U_T_M1 IS NULL AND U_T IS NOT NULL )
    UNION ALL
    -- Recursive member.
    SELECT  NEW.T, NEW.T_M1, NEW.P, NEW.F, NEW.DELTA_F,  
            NEW.R_T_M1, NEW.C_T_M1, 
            RECU.U_T AS U_T_M1,
            NEW.R_T, NEW.C_T, 
            -- Here the magic happens, i.e., (U_{t-1} + C_{t-1})*R_{t-1} = U_{t}
            (RECU.U_T+NEW.C_T_M1)*NEW.R_T_M1 AS U_T
    FROM DYNAMICS NEW 
    INNER JOIN RECU
    ON
        -- Translates: yesterday (t-1) of the new record equals today (t) of the parent record
        NEW.T_M1 = RECU.T AND 
        NEW.P = RECU.P AND 
        NEW.F = RECU.F AND 
        NEW.DELTA_F = RECU.DELTA_F 
)
SELECT * FROM  RECU ORDER BY P, F, T;

效果很好,即适用于:2015-01-02,| T | T_M1 | P | F | DELTA_F | R_T_M1 | C_T_M1 | U_T_M1 | R_T | C_T | U_T | |---------------------------|----------------------------|---|-----|---------|--------|--------|--------|-----|-----|--------| | January, 01 2015 00:00:00 | December, 31 2014 00:00:00 | 1 | BMW | RED | 0.5 | 0.6 | (null) | 0.7 | 0.8 | 100 | | January, 02 2015 00:00:00 | January, 01 2015 00:00:00 | 1 | BMW | RED | 0.7 | 0.8 | 100 | 0.9 | 0.9 | 70.56 | | January, 03 2015 00:00:00 | January, 02 2015 00:00:00 | 1 | BMW | RED | 0.9 | 0.9 | 70.56 | 0.2 | 0.3 | 64.314 | ,2015-01-03,U_t = (100+0.8)*0.7 = 70.56

查询的编写方式使其适用于不同的汽车经销商和不同的汽车品牌,可以检查其运行the query in the linked fiddle

回到完整的问题

上面的查询无法正确处理原始等式中汽车颜色的总和:

enter image description here

这与简化数据无关,因为所有汽车(BMW和MERCEDES)仅在RED中出现,因此颜色总和有效消失。

这样的完整逻辑应该可以通过内置于上述原始查询中的U_t = (70.56+0.9)*0.9 = 64.314表达式来实现。 不幸的是,我不知道该怎么做。

所以,想象一下你的形状数据就像简化问题部分一样,但现在每个汽车品牌都有两种颜色,e.g., like in this linked fiddle

GROUP BY/SUM

鉴于此类数据,您希望经销商| T | T_M1 | P | F | DELTA_F | R_T_M1 | C_T_M1 | U_T_M1 | R_T | C_T | U_T | |---------------------------|----------------------------|---|----------|---------|--------|--------|--------|-----|-----|--------| | January, 01 2015 00:00:00 | December, 31 2014 00:00:00 | 2 | MERCEDES | BLACK | 0.2 | 0.6 | (null) | 0.5 | 0.8 | 5.5 | | January, 01 2015 00:00:00 | December, 31 2014 00:00:00 | 2 | MERCEDES | RED | 0.5 | 0.6 | (null) | 0.7 | 0.8 | 5 | | January, 02 2015 00:00:00 | January, 01 2015 00:00:00 | 2 | MERCEDES | BLACK | 0.5 | 0.8 | 5.5 | 1.3 | 0.5 | (null) | | January, 02 2015 00:00:00 | January, 01 2015 00:00:00 | 2 | MERCEDES | RED | 0.7 | 0.8 | 5 | 4.3 | 0.5 | (null) | | January, 03 2015 00:00:00 | January, 02 2015 00:00:00 | 2 | MERCEDES | BLACK | 1.3 | 0.5 | (null) | 0.3 | 0.9 | (null) | | January, 03 2015 00:00:00 | January, 02 2015 00:00:00 | 2 | MERCEDES | RED | 4.3 | 0.5 | (null) | 0.4 | 0.9 | (null) | p=2汽车动态显示如下:

F=MERCEDES

问题是如何调整上面的简化查询来解决此问题。

2 个答案:

答案 0 :(得分:2)

我不认为这是最好的答案,但我认为它会为您提供您正在寻找的结果。

WITH RECU(  T, T_M1, P, F, DELTA_F, 
            R_T_M1, C_T_M1, U_T_M1, 
            R_T, C_T, U_T ) AS (
    -- Anchor member.

    SELECT  T, T_M1, P, F, DELTA_F, 
            R_T_M1, C_T_M1, 
            U_T_M1, 
            R_T, C_T, 
-- Start SUM of u_t
              (select sum(u_t) from DYNAMICS d2
               where d2.T=d1.T and d2.T_M1=d1.T_M1 and d2.P=d1.P and d2.F=d1.F
               group by T, T_M1, P, F) as u_t
-- End SUM of u_t   
    FROM DYNAMICS d1
        -- Initial condition: U_{t-1} does not exist, and U_{t=0} is given
        WHERE  ( U_T_M1 IS NULL AND U_T IS NOT NULL )
    UNION ALL
    -- Recursive member.

    SELECT  NEW.T, NEW.T_M1, NEW.P, NEW.F, NEW.DELTA_F,  
            NEW.R_T_M1, NEW.C_T_M1, 
            RECU.U_T AS U_T_M1,
            NEW.R_T, NEW.C_T
              , 
            -- Here the magic happens, i.e., (U_{t-1} + C_{t-1})*R_{t-1} = U_{t}
            (
              RECU.U_T
              +NEW.C_T_M1)*NEW.R_T_M1 AS U_T
    FROM DYNAMICS NEW 
    INNER JOIN RECU
    ON
        -- Translates: yesterday (t-1) of the new record equals today (t) of the parent record
        NEW.T_M1 = RECU.T AND 
        NEW.P = RECU.P AND 
        NEW.F = RECU.F AND 
        NEW.DELTA_F = RECU.DELTA_F 
)
SELECT * FROM  RECU ORDER BY P, F, T;

我添加的内容是Start SUM of u_tEnd SUM of u_t条之间的评论,这里是fiddle

答案 1 :(得分:1)

解决方案比我想象的要容易(尽管我花了一天时间尝试各种各样的事情,现在看起来似乎都很简单)。

对原始小提琴数据进行工作(测试)的查询为:

WITH RECU(  T, T_M1, P, F, DELTA_F, 
            R_T_M1, C_T_M1, U_T_M1, 
            R_T, C_T, U_T ) AS (
    -- Anchor member.
    SELECT  T, T_M1, P, F, DELTA_F, 
            R_T_M1, C_T_M1, 
            U_T_M1, 
            R_T, C_T, 
            U_T
    FROM DYNAMICS 
        -- Initial condition: U_{t-1} does not exist, and U_{t=0} is given
        WHERE  ( U_T_M1 IS NULL AND U_T IS NOT NULL )
    UNION ALL
    -- Recursive member.
    SELECT  NEW.T, NEW.T_M1, NEW.P, NEW.F, NEW.DELTA_F,  
            NEW.R_T_M1, NEW.C_T_M1, 
            RECU.U_T AS U_T_M1,
            NEW.R_T, NEW.C_T,
            -- Here the magic happens, i.e., (U_{t-1} + C_{t-1})*R_{t-1} = U_{t}
            ( (( SUM(RECU.U_T) OVER (PARTITION BY NEW.T, NEW.T_M1, NEW.P, NEW.F) ) + NEW.C_T_M1)*NEW.R_T_M1 ) AS U_T
    FROM DYNAMICS NEW 
    INNER JOIN RECU
    ON
        -- Translates: yesterday (t-1) of the new record equals today (t) of the parent record
        NEW.T_M1 = RECU.T AND 
        NEW.P = RECU.P AND 
        NEW.F = RECU.F AND 
        NEW.DELTA_F = RECU.DELTA_F 
)
SELECT * FROM RECU 
ORDER BY P, F, T, DELTA_F;

这是对原始查询的最小更改(仅影响原始查询的一行),并使用ORACLE分析函数。