【问题标题】:Go through each row of a table, do a calculation on that row, insert in temp table and use temp table for next row遍历表的每一行,对该行进行计算,插入临时表并将临时表用于下一行
【发布时间】:2019-06-07 10:30:24
【问题描述】:

问题:
我们有一个事务表。该表中的所有记录都具有以下交易类型之一:钱包存款(付款)、钱包取款(销售)和现金返还(用于未来销售的折扣)。我想在显示现金返还余额的每一行中添加一列。现金返还用于新销售的折扣或减少负总余额。

交易表:

customer (int)
transaction_date (date)
amount (int)
transaction_type (varchar(25))

我尝试使用 lag 函数来获取前一行的值并将其用于当前行的计算。但这并不总是有效,因为 lag 函数会回溯到它具体指向的行。

在计算中使用滞后函数:

case when
isnull(lag(balance_cashback) over (partition by client_id order by transaction_date), 0)
+ case when type = "cashback" then amount else 0 end 
+ case when type = "revenu"  and amount < 0 then amount else 0 end 
<= 0 then 0
else
lag(balance_cashback) over (partition by client_id order by transaction_date)
+ case when type = "cashback" then amount else 0 end 
+ case when type = "revenu"  and amount < 0 then amount else 0 end 
end

在互联网上搜索我认为我应该使用循环或光标?

想法:
这个想法是使用事务表并添加两个行号列。我要循环遍历的事务表中所有行的行号。每个客户的所有交易都有第二个行号。下一步似乎是创建一个空的余额表,其中包含字段 rownumclient、client_id、overall_balance 和 cashback_balance。

行数列的计算:

row_number () over (order by client_id, transaction_date) as rownumber_all
row_number () over (partition by client_id order by client_id, transaction_date) as rownumber_client

带有行号的事务表:

rownumber_all (int)
rownumber_client (int)
client (int)
transaction_date (date)
amount (int)
transaction_type (varchar(25))

余额表:

rownumber_client (int)
client_id (int)
overall_balance (int)
cashback_balance (int)

带有行号的示例事务表:

rownumbwr_all | rownumber_client | client_id | transaction_date | amount | transaction_type  
1           1              123         2018-10-12         10       wallet deposit  
2           2              123         2018-10-27         5        cashback  
3           3              123         2018-11-03         -2,5     wallet withdrawal  
4           4              123         2018-11-13         -5       wallet withdrawal  
5           5              123         2018-12-18         10       wallet deposit  
6           6              123         2018-12-19         20       wallet deposit  
7           7              123         2018-12-21         5        cashback  
8           1              456         2018-10-11         -45      wallet withdrawal  
9           2              456         2018-10-23         5        cashback  
10          3              456         2018-11-01         5        cashback  
11          4              456         2018-11-04         10       wallet deposit  
Etc.  

有了额外的行号列和新的余额表,我必须创建一个遍历事务表中所有行的循环。使用列 rownumber_all 从第一个开始。新创建的余额表用于计算当前行的返现余额。我们将此表与具有行号列的事务表的左连接一起使用。当我们遍历第一行时,余额表是空的,但从第二行开始,有一个从前一行计算的返现余额。

用于计算当前现金返还余额的选择语句:

select  
 t1.rownumall,
 t1.rownumclient,
 t1.client_id,
 t2.overall_balance + t1.amount as overall_balance,
 case
 when (t2.overall_balance + case when t1.type = 'cashback' then t1.amount else 0 end) < 0 then 0
 when t1.type in (sales, cashback) then amount 
 else null 
 end + t2.cashback_balance as cashback_balance
/*insert into balance*/
from
 transactions as t1
 left join cashback as t2 on t2.client_id = t1.client_id and t2.rownumber_client = t1.rownumber_client-1

只要有可用的交易记录,就应该将通过上述 select 语句的结果循环的每一行插入余额表中。如前所述,现金返还余额要么用于新销售的折扣,要么用于减少总体负余额。也就是说,我正在寻找的预期结果如下,cashback_balance 是最重要的字段。

带有余额的预期交易表:

client_id | transaction_date | amount | transaction_type | overall_balance | cashback balance  
123         2018-10-12         10       wallet deposit        10                0 
123         2018-10-27         5        cashback              15                5
123         2018-11-03         -2,5     wallet withdrawal     12,5              2,5
123         2018-11-13         -5       wallet withdrawal     7,5               0
123         2018-12-18         10       wallet deposit        17,5              0
123         2018-12-19         20       wallet deposit        37,5              0
123         2018-12-21         5        cashback              42,5              5
456         2018-10-11         -45      wallet withdrawal     -2,5              0
456         2018-10-23         5        cashback              2,5               2,5
456         2018-11-01         5        cashback              7,5               7,5
456         2018-11-04         10       wallet deposit        17,5              7,5
Etc.  

我试图尽可能多地解释,并希望这个想法和预期的结果是清楚的。我无法想象我需要的东西以前没有做过,但我似乎无法在任何地方找到具体的用例。

那么,哪位 SQL 专家会好心地用简单的英语告诉我如何使用循环、游标或任何其他方式来实现这一点?任何帮助将不胜感激。如果需要任何澄清,请告诉我。

【问题讨论】:

  • 很难在没有看到最终结果与您开始的结果相比的情况下在这里提出一种方法。循环、游标等可能代价高昂,通常有更好的方法来完成它们的工作。如果您可以发布最终结果应该是什么样子,那么您可能会更幸运地获得一些帮助。
  • 我会尽快添加预期结果。
  • 样本数据最好使用DDL + DML。请edit您的问题包括它,您当前的尝试和您想要的结果。更多详情,read this.

标签: sql-server loops foreach while-loop cursor


【解决方案1】:

经过搜索和反复试验,我找到了一种遍历所有行并计算每行正确现金返还余额的方法。我要感谢所有试图帮助我的人和Jorge E. Hernández 为我的“循环问题”提供solution

这是我使用的最终代码。

-- declare the start and end variable
declare
    @counter int = 1,
    @max_rownumber int = (select max(rownumber_all) as max_rownumber from dbo.transactions)

-- loop 
while @counter <= @max_rownumber
begin

-- calculate overall_balance and cashback_balance for each row in the transactions table filtered by the rownumber_all field
insert into dbo.transactions_enriched
select  
    t1.rownumber_client as rownumber
  , t1.client_id
  , t1.transaction_date
  , t1.amount
  , t1.transaction_type
  , t1.payment_method
  , isnull(t2.overall_balance ,0) + t1.amount as overall_balance
  , case 
    when t1.transaction_type = 'cashback' and isnull(t2.overall_balance, 0) >= 0 then isnull(t2.cashback_balance, 0) + t1.amount
    when (case when t1.transaction_type = 'revenue' and t1.amount < 0 then t1.amount else 0 end) + isnull(t2.cashback_balance, 0) <= 0 then 0
    else (case when t1.transaction_type = 'revenue' and t1.amount < 0 then t1.amount else 0 end) + isnull(t2.cashback_balance, 0)
    end as cashback_balance
from
    dbo.transactions as t1
    left join dbo.transactions_enriched as t2 on t2.client_id = t1.client_id and t2.rownumber_client = t1.rownumber_client - 1
where 
    t1.rownumber_all = @counter

-- update the counter by adding 1
set @counter = @counter + 1

end

【讨论】:

    【解决方案2】:

    您是否正在寻找像银行对帐单一样的运行总计。这个查询就是这样做的

    SELECT
        client_id,
        Transaction_Date,
        Trans_Type,
        Amount,
        Balance = SUM(CASE WHEN Trans_Type = 'Cash back' Then Amount ELSE 0 END) OVER(ORDER BY RowNumber)
    
    FROM
    (
        SELECT 
            client_id,
            Transaction_Date,
            Trans_Type,
            Amount,
            ROW_NUMBER() OVER(ORDER BY Client_id) AS RowN
    
        FROM temp.dbo.Trans_Table) RC
    
        GROUP BY client_id, Trans_Type, Transaction_Date, Amount, RowN
    

    样本数据

    【讨论】:

    • 余额仅用于返现,那么每行返现的余额是多少。我已经添加了我正在考虑的问题。我会尽快添加预期结果。
    • 我刚刚在运行总计中添加了一个案例,该案例仅获得现金返还的运行总计。该图像还显示了更新的结果
    • 感谢您的努力,但这还不是我想要的。困难在于销售额应该从现金返还中扣除。此外,当总余额为负数时(因为客户购买了很多但没有支付那么多),它总是充满现金返还。明天在工作中,我将添加一个真正显示手头问题的示例。再次感谢您迄今为止所做的努力。
    猜你喜欢
    • 2016-06-21
    • 1970-01-01
    • 1970-01-01
    • 2016-07-01
    • 2018-02-06
    • 2016-02-15
    • 2014-05-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多