简体   繁体   English

将Excel业务逻辑转换为T-SQL

[英]Translate Excel business logic to T-SQL

I need to 'translate' some business logic from Excel to T-SQL, I'm having a hard time with it. 我需要将一些业务逻辑从Excel翻译成T-SQL,我很难用它。

It's about figures from gates that count how many customers go IN and OUT of the stores. 这是关于门的数字,计算有多少客户进出商店。

All DATA you need is in the following table: 您需要的所有数据如下表所示:

CREATE TABLE #ResultsTable 
(
    Datum DATETIME,
    window CHAR(10),
    countersOUT INT,
    countersIN INT,
    RESULT INT
)

INSERT INTO #ResultsTable 
VALUES ('20180104 08:30:00.000', '08:30', 0, 0, 0),
       ('20180104 09:00:00.000', '09:00', 2, 1, 1),
       ('20180104 09:30:00.000', '09:30', 1, 0, 2),
       ('20180104 10:00:00.000', '10:00', 25, 9, 18),
       ('20180104 10:30:00.000', '10:30', 45, 41, 22),
       ('20180104 11:00:00.000', '11:00', 38, 37, 23),
       ('20180104 11:30:00.000', '11:30', 50, 51, 22),
       ('20180104 12:00:00.000', '12:00', 21, 24, 19),
       ('20180104 12:30:00.000', '12:30', 12, 19, 12),
       ('20180104 13:00:00.000', '13:00', 25, 18, 19),
       ('20180104 13:30:00.000', '13:30', 35, 27, 27),
       ('20180104 14:00:00.000', '14:00', 81, 9, 52),
       ('20180104 14:30:00.000', '14:30', 113, 18, 70),
       ('20180104 15:00:00.000', '15:00', 116, 34, 71),
       ('20180104 15:30:00.000', '15:30', 123, 36, 54),
       ('20180104 16:00:00.000', '16:00', 127, 35, 50),
       ('20180104 16:30:00.000', '16:30', 103, 19, 47),
       ('20180104 17:00:00.000', '17:00', 79, 31, 27),
       ('20180104 17:30:00.000', '17:30', 50, 16, 26),
       ('20180104 18:00:00.000', '18:00', 28, 11, 17),
       ('20180104 18:30:00.000', '18:30', 16, 15, 2),
       ('20180104 19:00:00.000', '19:00', 0, 2, 0),
       ('20180104 19:30:00.000', '19:30', 0, 0, 0),
       ('20180104 20:00:00.000', '20:00', 0, 0, 0),
       ('20180104 20:30:00.000', '20:30', 0, 0, 0),
       ('20180104 21:00:00.000', '21:00', 0, 0, 0),
       ('20180104 21:30:00.000', '21:30', 0, 0, 0),
       ('20180104 22:00:00.000', '22:00', 0, 0, 0)

select * from #ResultsTable

'RESULT' is the column that should be calculated, based on 'countersOUT and 'countersIN'. 'RESULT'是应根据'countersOUT和'countersIN'计算的列。

'countersOUT and 'countersIN' is the INPUT data you need for the calculation. 'countersOUT和'countersIN'是计算所需的INPUT数据。

The business user made a help column in Excel to make the calculation (column AA) . 业务用户在Excel中创建了帮助列以进行计算(列AA)。 . . . . . . From business point of view: this is the number of customers at the end of each half hour present in the shop. 从商业角度来看:这是商店中每半小时结束时的客户数量。

在此输入图像描述

Then, the actual calculation below: (screenshot also from Excel) 然后, 实际计算如下:(也可以从Excel截图)

Note: the calculation makes use of the help column (col. AA) 注意:计算使用帮助栏(col.AA) 在此输入图像描述

Now my task is to make over this calculation in T-SQL. 现在我的任务是在T-SQL中完成这个计算。

The business user its only input data is 'countersIN' and 'countersOUT', in other words it should be achievable in T-SQL. 业务用户唯一的输入数据是'countersIN'和'countersOUT',换句话说,它应该可以在T-SQL中实现。 Only, I don't manage, that's why I came to ask my question. 只是,我没有管理,这就是为什么我来问我的问题。

Lastly, I can advise to have a look at the Excel file (mediafire link) http://www.mediafire.com/file/mtdvlgmmbj3f8dd/Example_20190725_SQLforum.xlsx/file 最后,我建议您查看Excel文件(mediafire链接) http://www.mediafire.com/file/mtdvlgmmbj3f8dd/Example_20190725_SQLforum.xlsx/file

Thanks a lot in advance for any help 非常感谢您提供任何帮助

select *, ROW_NUMBER() over (order by datum) as rw 
into #temp 
from #ResultsTable 
order by datum

select a.datum, a.window, a.countersout, a.countersin, countersout-countersin as result, rw
into #temp1 from #temp a 

select a.datum, a.window, a.countersOUT, a.countersIN, 
case when isnull(b.result,0) + a.countersOUT - a.countersIN < 0 then a.countersIN
else (case when (isnull(b.result,0) +a.result) + a.countersOUT - a.countersIN > isnull(c.countersIN,0) + isnull(d.countersIN,0) then  isnull(c.countersIN,0) + isnull(d.countersIN,0) + a.countersIN -  (isnull(b.result,0) +a.result)
else a.countersOUT end)
end as Result, a.result + b.RESULT as A88
from #temp1 a left join #temp b
 on a.rw =b.rw + 1
 left join #temp c 
 on a.rw + 2 = c.rw
 left join #temp d 
 on a.rw + 3 = d.rw
 order by a.datum

I have copied your logic as is. 我按原样复制了你的逻辑。 But your logic stops making sense after line 12. If you can explain how that makes sense, I will edit this to give you what you want or you can tweak it yourself. 但是你的逻辑在第12行之后就没有意义了。如果你能解释一下这是有道理的,我会编辑它来给你你想要的东西,或者你可以自己调整它。

Here's a solution using a Cursor. 这是使用Cursor的解决方案。 No good style but effective, since you move through the rows. 没有好的风格,但有效,因为你穿过行。 The attribut [RESULT_by_Cursor] is the computed one in comparison to your target value. attribut [RESULT_by_Cursor]是与您的目标值相比较的计算值。

Btw: Your SQL-example has an error, the in and out - columns are twisted. 顺便说一句:你的SQL示例有错误,进出列是扭曲的。

CREATE TABLE #ResultsTable 
(
    Datum DATETIME,
    window CHAR(10),
    countersIN INT,--countersOUT INT,
    countersOUT INT,--countersIN INT,
    RESULT INT,
    RESULT_by_Cursor INT,
    countersIN_corrected INT
);


INSERT INTO #ResultsTable 
VALUES ('20180104 08:30:00.000', '08:30', 0, 0, 0, NULL, NULL),
       ('20180104 09:00:00.000', '09:00', 2, 1, 1, NULL, NULL),
       ('20180104 09:30:00.000', '09:30', 1, 0, 2, NULL, NULL),
       ('20180104 10:00:00.000', '10:00', 25, 9, 18, NULL, NULL),
       ('20180104 10:30:00.000', '10:30', 45, 41, 22, NULL, NULL),
       ('20180104 11:00:00.000', '11:00', 38, 37, 23, NULL, NULL),
       ('20180104 11:30:00.000', '11:30', 50, 51, 22, NULL, NULL),
       ('20180104 12:00:00.000', '12:00', 21, 24, 19, NULL, NULL),
       ('20180104 12:30:00.000', '12:30', 12, 19, 12, NULL, NULL),
       ('20180104 13:00:00.000', '13:00', 25, 18, 19, NULL, NULL),
       ('20180104 13:30:00.000', '13:30', 35, 27, 27, NULL, NULL),
       ('20180104 14:00:00.000', '14:00', 81, 9, 52, NULL, NULL),
       ('20180104 14:30:00.000', '14:30', 113, 18, 70, NULL, NULL),
       ('20180104 15:00:00.000', '15:00', 116, 34, 71, NULL, NULL),
       ('20180104 15:30:00.000', '15:30', 123, 36, 54, NULL, NULL),
       ('20180104 16:00:00.000', '16:00', 127, 35, 50, NULL, NULL),
       ('20180104 16:30:00.000', '16:30', 103, 19, 47, NULL, NULL),
       ('20180104 17:00:00.000', '17:00', 79, 31, 27, NULL, NULL),
       ('20180104 17:30:00.000', '17:30', 50, 16, 26, NULL, NULL),
       ('20180104 18:00:00.000', '18:00', 28, 11, 17, NULL, NULL),
       ('20180104 18:30:00.000', '18:30', 16, 15, 2, NULL, NULL),
       ('20180104 19:00:00.000', '19:00', 0, 2, 0, NULL, NULL),
       ('20180104 19:30:00.000', '19:30', 0, 0, 0, NULL, NULL),
       ('20180104 20:00:00.000', '20:00', 0, 0, 0, NULL, NULL),
       ('20180104 20:30:00.000', '20:30', 0, 0, 0, NULL, NULL),
       ('20180104 21:00:00.000', '21:00', 0, 0, 0, NULL, NULL),
       ('20180104 21:30:00.000', '21:30', 0, 0, 0, NULL, NULL),
       ('20180104 22:00:00.000', '22:00', 0, 0, 0, NULL, NULL)




-- PDO: Apply Cursor to run through datasets


DECLARE @Datum DATETIME,
    @window CHAR(10),
    @countersOUT INT,           -- U
    @countersIN INT,            -- V
    @countersOUT_next INT,          -- V + 1 row
    @countersOUT_nextnext INT,          -- V + 2 rows
    @countersIN_corrected INT,
    @RESULT_by_Cursor INT;         --AA

DECLARE C_Tag CURSOR FAST_FORWARD FOR 
    SELECT Datum,
           window,
           countersOUT,
           countersIN

    FROM #ResultsTable
    ORDER BY Datum ASC
    ;


-- PDO: Cursor open and first fetch
SET @countersIN_corrected=0;
SET @RESULT_by_Cursor=0;

OPEN C_Tag

    FETCH NEXT FROM C_Tag INTO @Datum,
                               @window,
                               @countersOUT,
                               @countersIN

                               ;

    WHILE @@FETCH_STATUS=0

        BEGIN

            -- PDO: Get upcoming data in case we need them
            SET @countersOUT_next = ISNULL((SELECT TOP 1 r.countersOUT
                FROM #ResultsTable r
                WHERE r.Datum > @Datum
                ORDER BY r.Datum
                ),0)
            ;


            SET @countersOUT_nextnext = ISNULL((SELECT TOP 1 r.countersOUT
                FROM #ResultsTable r
                WHERE r.Datum > (SELECT TOP 1 r2.Datum
                                FROM #ResultsTable r2
                                WHERE r2.Datum > @Datum
                                ORDER BY r2.Datum
                                )
                ORDER BY r.Datum
                ),0)
            ;

            -- PDO: Compute correction according to Formula
            SET @countersIN_corrected=IIF(@RESULT_by_Cursor + @countersIN - @countersOUT < 0 ,

                                            @countersOUT ,

                                            IIF(@RESULT_by_Cursor + @countersIN - @countersOUT > (@countersOUT_next + @countersOUT_nextnext)    ,

                                                @countersOUT_next + @countersOUT_nextnext + @countersOUT - @RESULT_by_Cursor ,

                                                @countersIN

                                            )

                                        );



            -- PDO: Compute Result by cursor

            SET @RESULT_by_Cursor=@RESULT_by_Cursor + @countersIN_corrected - @countersOUT;



            -- PDO: Update Table with computed result

            UPDATE #ResultsTable
            SET RESULT_by_Cursor=@RESULT_by_Cursor,
                countersIN_corrected=@countersIN_corrected
            WHERE Datum=@Datum
            ;



            FETCH NEXT FROM C_Tag INTO @Datum,
                                       @window,
                                       @countersOUT,
                                       @countersIN

                                       ;


        END -- @@Fetch_Status C_Tag

CLOSE C_Tag;
DEALLOCATE C_Tag;



-- PDO: Clean Up

select * from #ResultsTable;


DROP TABLE #ResultsTable;

     select r.*,
      isnull(LAG(result)  over (order by datum) ,0) - countersOUT + countersIN as AA
    from #ResultsTable r 

(Not enough reputation to comment yet.) (还没有足够的声誉发表评论。)

The approach from Rahul is promising, but will not work in this special case. 拉胡尔的方法很有希望,但在这种特殊情况下不会起作用。 Any solution requires some sort of iteration - to compute the value of step n you need to use the computed value of step n-1. 任何解决方案都需要某种迭代 - 计算步骤n的值,您需要使用步骤n-1的计算值。 (With an initial value for n=0 given as 0 in the Excel file.) This is owed to the correction term. (在Excel文件中,n = 0的初始值为0。)这是修正项。 While using static data from each record respectively, the task cannot be solved. 在分别使用每个记录的静态数据时,无法解决任务。

Recursive CTE proposed by Lukasz Szozda implies an iteration and might do the job with some effort. Lukasz Szozda提出的递归CTE意味着迭代,可能会付出一些努力。

The challenge here is to get the values for the next row and the values for the row next to the next row and the values for the prev row. 这里的挑战是获取下一行的值以及下一行旁边的行的值和prev行的值。 so to get the values for the next and next next row we will use LEAD, for the prev row we cant use LAG as its calculated step by step so I will use CTE with recursive join to go row by row. 因此,为了获得下一行和下一行的值,我们将使用LEAD,对于prev行,我们不能一步一步地使用LAG作为其计算,因此我将使用CTE和递归连接逐行进行。 My results match your expected result all the way. 我的结果一直与您的预期结果相符。 :) :)

please see the below, 请看下面的,

;with excel as (
select 
    Datum,window,countersOUT [U5],countersIN [T5],
        LEAD(countersIN,1) over (order by Datum) [T6],
        LEAD(countersIN,2) over (order by Datum) [T7],
        RESULT
    from #ResultsTable 
),prev as (
select top(1) *
    ,IIF(U5-T5<0,T5,U5) [V] --first row equation := IF(U4-T4<0,T4,U4)
    ,IIF(U5-T5<0,T5,U5)-T5 [Z]  --first row equation :=V4-T4
    from excel order by Datum
union all
    select cur.*
    --=IF(Z4+U5-T5<0,T5,IF(Z4+U5-T5>T6+T7,T6+T7+T5-Z4,U5))
    ,IIF((prev.Z+cur.U5-cur.T5)<0,cur.T5,IIF((prev.Z+cur.U5-cur.T5)>(cur.T6+cur.T7),(cur.T6+cur.T7+cur.T5-prev.Z),cur.U5))  [V]
    --:=Z4+V5-T5 (V from the above line)
    ,prev.Z+IIF((prev.Z+cur.U5-cur.T5)<0,cur.T5,IIF((prev.Z+cur.U5-cur.T5)>(cur.T6+cur.T7),(cur.T6+cur.T7+cur.T5-prev.Z),cur.U5))-cur.T5 [Z] 
    from prev inner join excel cur on cur.Datum=dateadd(MINUTE,30,prev.Datum)
)
select Datum,window,U5 countersOUT,T5 countersIN,RESULT,Z [CalResult] from prev

The first CTE is to make our life easier by naming the columns to something similar to excel so mapping the function will be easier, anyhow in the end I return them back , my results are as below:- 第一个CTE是通过将列命名为类似于excel的东西来使我们的生活更轻松,因此映射函数将更容易,无论如何最终我将它们返回,我的结果如下: -

Datum   window  countersOUT countersIN  RESULT  CalResult
2018-01-04 08:30:00.000 08:30       0   0   0   0
2018-01-04 09:00:00.000 09:00       2   1   1   1
2018-01-04 09:30:00.000 09:30       1   0   2   2
2018-01-04 10:00:00.000 10:00       25  9   18  18
2018-01-04 10:30:00.000 10:30       45  41  22  22
2018-01-04 11:00:00.000 11:00       38  37  23  23
2018-01-04 11:30:00.000 11:30       50  51  22  22
2018-01-04 12:00:00.000 12:00       21  24  19  19
2018-01-04 12:30:00.000 12:30       12  19  12  12
2018-01-04 13:00:00.000 13:00       25  18  19  19
2018-01-04 13:30:00.000 13:30       35  27  27  27
2018-01-04 14:00:00.000 14:00       81  9   52  52
2018-01-04 14:30:00.000 14:30       113 18  70  70
2018-01-04 15:00:00.000 15:00       116 34  71  71
2018-01-04 15:30:00.000 15:30       123 36  54  54
2018-01-04 16:00:00.000 16:00       127 35  50  50
2018-01-04 16:30:00.000 16:30       103 19  47  47
2018-01-04 17:00:00.000 17:00       79  31  27  27
2018-01-04 17:30:00.000 17:30       50  16  26  26
2018-01-04 18:00:00.000 18:00       28  11  17  17
2018-01-04 18:30:00.000 18:30       16  15  2   2
2018-01-04 19:00:00.000 19:00       0   2   0   0
2018-01-04 19:30:00.000 19:30       0   0   0   0
2018-01-04 20:00:00.000 20:00       0   0   0   0
2018-01-04 20:30:00.000 20:30       0   0   0   0
2018-01-04 21:00:00.000 21:00       0   0   0   0
2018-01-04 21:30:00.000 21:30       0   0   0   0
2018-01-04 22:00:00.000 22:00       0   0   0   0

You can see that your result match the Cal Result. 您可以看到您的结果与Cal Result匹配。 Hope this helps. 希望这可以帮助。

You seem to want cumulative sums: 你似乎想要累积总和:

select r.*,
       sum(countersout - countersin) over (order by datum) as result
from #ResultsTable r;

Here is a db<>fiddle. 是一个db <>小提琴。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM