【问题标题】:Translate Excel business logic to T-SQL将 Excel 业务逻辑转换为 T-SQL
【发布时间】:2019-07-29 10:26:36
【问题描述】:

我需要将一些业务逻辑从 Excel '翻译'到 T-SQL,我很难做到。

这是关于大门的数据,用于计算有多少顾客进出商店。

您需要的所有数据都在下表中:

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' 是应根据 'countersOUT 和 'countersIN' 计算的列。

'countersOUT 和 'countersIN' 是计算所需的 INPUT 数据。

业务用户在 Excel 中创建了一个帮助列来进行计算(AA 列) . . . . . . .从商业角度来看:这是每半小时在店内的顾客数量。

然后,实际计算如下:(截图同样来自 Excel)

注意:计算使用帮助列(AA 栏)

现在我的任务是在 T-SQL 中完成这个计算。

业务用户的唯一输入数据是“countersIN”和“countersOUT”,换句话说,它应该可以在 T-SQL 中实现。只是,我不会,所以我才来问我的问题。

最后,我建议您查看一下 Excel 文件(mediafire 链接)http://www.mediafire.com/file/mtdvlgmmbj3f8dd/Example_20190725_SQLforum.xlsx/file

非常感谢您的任何帮助

【问题讨论】:

  • 。 .您需要尝试解释正在实施的逻辑。
  • @laurens 它是上限运行总计的一种变体。使用 T-SQL 可以完美实现,但您必须使用递归 CTE 来调整 Excel 中的逻辑。 Conditional SUM on Oracle
  • @LukaszSzozda :这是个有趣的信息,谢谢,我会调查一下
  • 您以 sql 表的形式提供的数据将 counterout 和 counterin 列互换。这是正确的吗?
  • 给我解释一下。第 11 行的结果为 27,第 12 - 81 行进来,第 9 行出去,所以结果不应该是 27 + 81 - 9?

标签: sql sql-server excel tsql


【解决方案1】:
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

我照原样复制了你的逻辑。但是你的逻辑在第 12 行之后就不再有意义了。如果你能解释它的意义,我会编辑它给你想要的东西,或者你可以自己调整它。

【讨论】:

  • 请根据 excel 文件检查我的解决方案,您可以看到即使在第 11 行之后,结果也符合请求者的预期,是的,我同意您在没有光标的情况下 sql 可以做到这一点。
【解决方案2】:

这是使用光标的解决方案。没有好的风格但很有效,因为你在行中移动。属性 [RESULT_by_Cursor] 是与您的目标值相比计算得出的。

顺便说一句:您的 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;

【讨论】:

    【解决方案3】:
         select r.*,
          isnull(LAG(result)  over (order by datum) ,0) - countersOUT + countersIN as AA
        from #ResultsTable r 
    

    【讨论】:

      【解决方案4】:

      (还没有足够的声誉来发表评论。)

      Rahul 的方法很有希望,但在这种特殊情况下行不通。任何解决方案都需要某种迭代 - 要计算步骤 n 的值,您需要使用步骤 n-1 的计算值。 (在 Excel 文件中,n=0 的初始值为 0。)这是由于校正项所致。分别使用每条记录的静态数据时,任务无法解决。

      Lukasz Szozda 提出的递归 CTE 意味着迭代,并且可能需要付出一些努力才能完成这项工作。

      【讨论】:

      • 我通过迭代连接实现了同样的目标。在大多数情况下,游标可以被一些连接和/或动态 sql 替换。如果您查看光标在做什么以及我提出的查询在做什么,它们几乎是一回事。另外,你能解释一下第 12 行的结果是 52 吗?
      • 如果excel可以做到,sql也可以不用游标。
      • @Rahul:我无法用您的代码重现想要的结果。据我所知,您的解决方案的问题是术语“ a.result ”,它是预先计算的静态而不是动态的迭代。如果你证明我错了,我会很高兴。第 12 行是修正项开始的第一行。您可能需要查阅 excel 文件。
      • 你能解释一下文件是如何在 AA 中得到 52 的吗?也许我没有明白逻辑,但我完全迷失了。因为在屏幕截图 1 中显示 AA88 + U89 - V89。如果您考虑下一个屏幕截图(不同的列应该是 V13(值),
      • @Rahul:它是 27 + (34 - 9 ) = 52 。从第 2 行开始,您需要列 W 而不是 V。 W 是使用包括先前结果的校正项计算的,这就是需要迭代的原因。这样的交互可以是 with / Recursive CTE 子句(只不过是一个循环)或游标(也是一个循环)。游标应使用唯一名称,最好使用 with (nolock) ,以避免出现主要问题。另一方面,对于以后管理它的人来说,它更容易理解。
      【解决方案5】:

      这里的挑战是获取下一行的值和下一行的值以及上一行的值。 因此,要获得下一行和下一行的值,我们将使用 LEAD,对于上一行,我们不能使用 LAG 作为其逐步计算的值,因此我将使用 CTE 和递归连接来逐行进行。 我的结果完全符合您的预期结果。 :)

      请看下文,

      ;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
      

      第一个 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
      

      您可以看到您的结果与 Cal Result 匹配。 希望这会有所帮助。

      【讨论】:

      • 如果您在#ResultsTable 表中将U5 与T5 一起翻转,则可以正常工作。
      • 嗨阿里,今天晚些时候我会看看并测试它。非常感谢您的回复
      【解决方案6】:

      您似乎想要累积总和:

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

      Here 是一个 dbfiddle。

      【讨论】:

      • 嗨,戈登,感谢您的建议。我做了同样的事情,它适用于前 10 行,但它不再正确。请查看 excel 文件(mediafire 链接)或上面的屏幕截图。
      • @laurens 。 . .根据您的描述 -- 从业务角度来看:这是商店中每半小时结束时的顾客数量 -- 这应该是您想要的。您的计数器值似乎不正确。
      • 这就是帮助栏的解释。实际计算在“W”列中
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-11
      • 2014-07-09
      • 1970-01-01
      相关资源
      最近更新 更多