循环遍历SQL Server中的记录集

问题描述:

我被困在如何遍历行集并保存在变量中。循环遍历SQL Server中的记录集

请注意,这可能是伪代码,因为SQL不是我的专长。

@all_customers = select CustNum from [crrsql].[dbo].[Customer]; 
some loop(@all_customers as user) 
//I need to find out what the Acct_balance field is and either subtract or add to bring all the balances to 0 
    @balance = select Acct_balance from [crrsql].[dbo].[Customer] where CustNum = user; 
    if @balance > 0 
     update [crrsql].[dbo].[Customer] set Acct_balance = 0; 
     INSERT INTO [crrsql].[dbo].[AR_Transactions] (cashier_ID, CustNum, Balance) VALUES (100199, user, @balance); 
    else 
     update [crrsql].[dbo].[Customer] set Acct_balance = 0; 
     INSERT INTO [crrsql].[dbo].[AR_Transactions] (cashier_ID, CustNum, Balance) VALUES (100199, user, "-" + @balance); 
    end 
end loop 

正如你可以看到我循环通过客户和循环内我需要获得当前余额,并将其设为零,但首先我需要找出如果它是一个正数或负数是能够确定AR_Transactions表中每个用户的插入是否需要是正数或负数。你能帮忙解决缺失的部分吗?

+0

你那里的代码看起来总是会在AR_Transactions中产生一个正值,并且没有任何东西可以指示借记与贷记。 – 2011-05-23 19:08:17

+1

使用'“ - ”+ @ balance'转换为字符串很糟糕!您对数字进行否定的方式是“ - @ balance”。此外,你总是希望否定的数额,没有条件,使负的余额有一个积极的交易,反之亦然。 – ErikE 2011-05-23 21:02:23

+0

这可以在一个语句中完成。请看我的答案。 – ErikE 2011-05-23 21:15:29

这里是直接翻译过来的原密码为有效的SQL Server语法。我不能说与你正在做什么有关的业务规则,但请注意,这种方法避免使用游标,并使用ABS()函数来取消原始的if/else块。

declare @all_customers as table(
    customernumber int 
); 

/* 
--you can insert dummy data for testing purposes like so: 
insert into @all_customers 
select 5, 1 
union 
select 2, 1 
--*/ 


while (0 < (select count(*) from @all_customers)) begin 
    declare @current_customer int = (select top 1 customernumber from @all_customers); 

    declare @balance money = (select acct_balance from [crrsql].[dbo].[Customer] where CustNum = @current_customer); 
    update [crrsql].[dbo].[Customer] set Acct_balance = 0; 
    INSERT INTO [crrsql].[dbo].[AR_Transactions] (cashier_ID, CustNum, Balance) VALUES (100199, user, abs(@balance)); 

    delete @all_customers where customernumber = @current_customer; 
end 
+1

当一个简单的基于集合的解决方案是可能的时,为什么你会提供循环?你知道循环,就像你做的实际上比游标执行**糟糕吗? – ErikE 2011-05-23 21:20:59

我会看看cursors,但是IMO在应用程序逻辑中比在SQL中更好。

+0

我没有访问应用程序和要求要求SQL文件进行更改...我会看光标 – Trace 2011-05-23 18:53:12

+0

@谭,不用担心。喜欢给我我的答案和任何我认为合适的建议。每种情况都是独特的。 – 2011-05-23 18:55:51

+0

为什么你会建议使用游标来查找可以在一个基于集合的查询中完成的事情? – ErikE 2011-05-23 21:14:15

您应该可以在不使用游标或其他程序代码的情况下在几个语句中执行此操作。只要确保这一切都在一个交易:

BEGIN TRANSACTION 

INSERT INTO crrsql.dbo.AR_Transactions (
    cashier_id, 
    cust_num, 
    balance, 
    transaction_date) 
SELECT 
    100199, 
    cust_num, 
    -acct_balance, 
    DATEADD(MINUTE, -30, current_date) 
FROM crrsql.dbo.Customers 
WHERE acct_balance <> 0 

UPDATE crrsql.dbo.Customers SET acct_balance = 0 WHERE acct_balance <> 0 

COMMIT TRANSACTION 

当然,添加适当的错误处理,并确保先进行测试。

此外,我稍微改变了一些你的表和列的名称。我不想进入哪个特定的命名约定比其他命名约定更好,但在至少是一致的。如果你打算使用下划线,请使用它们。如果您打算使用驼峰符号,请使用该符号,但不要混用它们。复数与单数表名相同。

+0

hey @Tom ...感谢这段代码,但是这段代码什么时候能够看到什么余额将它更新为正数或负数 – Trace 2011-05-23 19:19:04

+0

在acct_balance周围添加ABS() – 2011-05-23 19:19:49

+0

您是否总是插入一个正值?如果是这样,那么凯德建议ABS()将这样做。我发布了对你的问题的评论,因为我认为这可能是一个编码错误,这也是为什么我现在离开这个原因。 – 2011-05-23 19:22:58

您正在寻找一个函数,如果@balance> 0,则返回@balance,否则 - @余额。

ABS(@balance)会这样做。 (除非你需要插入一个以“ - ”开头的字符串文字,但看起来很奇怪 - 我假设余额列是十进制类型。)

我猜在商业领域,但可能有也是您需要根据交易标志设置的信用卡/借记栏。在这种情况下,你可能想是这样的:

INSERT INTO [crrsql].[dbo].[AR_Transactions] (cashier_ID, CustNum, Balance, CR_DR) VALUES (100199, user, ABS(@balance), CASE WHEN @balance > 0 THEN 'CR' ELSE 'DR' END); 

作为一般规则,以为你需要在SQL做基于行的操作是,您(或他人)不正确地陷害问题的一个非常好的迹象:它代表了程序性而非集合性思维的代码味道。

你想要的伪代码,我认为,这些方针的东西:

-- Create a temporary table. A table starting with # is a temporary. It will be 
-- automatically dropped when the session ends. If two sessions creates temp 
-- tables with the same name, at the same time, they will still get one table each. 
create table #work 
(
    CustNum int , 
    Balance money , 
) 

insert #work (CustNum , Balance) 
select CustNum , Balance 
from Customer 
where Balance != 0 

begin transaction 

insert dbo.AR_Transactions (cashier_ID, CustNum, Balance) 
select cashier_ID = 100199 , 
     user  = CustNum , 
     adjustment = case sign(@balance) -- should always be +1 or -1 
        when 1 then @balance -- positive balance 
        when -1 then [email protected] -- negative balance 
        end 

update Customer set balance = 0 
from Customer c 
join #work w on w.CustNum = c.CustNum 

commit transaction 

-- Manual tidying up if the connection might be kept open. 
drop table #work 
+0

这看起来不错...这样做是否会在此过程中创建一个新表格 – Trace 2011-05-23 19:32:55

+0

在SQL Server中,以#开头的表名是临时表,并且是在tempdb中创建的。他们是本地会议,并在会议期间存在。如果在存储过程的主体中创建,则在存储过程执行终止时将被删除。 – 2011-05-23 20:19:45

+0

@Tamer它创建一个临时表,当连接断开或重置时它将自动丢弃。请参阅代码中的注释。 – 2011-05-23 20:30:45

对于SQL 2005和高达:

UPDATE C 
SET C.Acct_Balance = 0 
OUTPUT 100199, Inserted.CustNum, -Deleted.Acct_Balance, DateAdd(Minute, -30, GetDate()) 
INTO crrsql.dbo.AR_Transactions (Cashier_ID, CustNum, Balance, Transaction_Date) 
FROM crrsql.dbo.Customer C 
WHERE C.Acct_Balance <> 0 

因为有一些混乱,我会注意到,-Deleted.Acct_Balance是所有你需要带来的平衡为零。它否定了插入积极的负平衡,并且否定积极的平衡来插入否定。

对于SQL 2000,您需要多个语句。

+0

我不是按照如何工作...我试图剪切和粘贴,我得到消息207,级别16,状态1,行2 无效的列名'Trans_Amount'。 – Trace 2011-05-23 21:54:22

+0

@Tamer我可能列名错误。请尝试修复它们。 OUTPUT子句需要Customer表中的列名,而INTO子句需要AR_Transactions表中的列名。如果您仍然无法使其工作,请发布您的两个表的DDL脚本,我会为您提供查询。你正在使用SQL 2005或更高版本,对吗? – ErikE 2011-05-23 21:57:10

+0

好吧,我会尽力解决它们,但是插入的是什么。和 - 删除。 ...我不明白那部分 – Trace 2011-05-23 22:36:39