ABAP 如何判断多用户电能表同时操作同一张表

多用户同时处理同一条数据解决办法 - CSDN博客
多用户同时处理同一条数据解决办法
在c/s或多层中,如果两个用户同时打开一条记录,修改后提交会产生更新冲突;
据说办法有二:1。打开同时锁定表的记录 2。浦获错误,撤消其中一个用户的修改,但是很少见到具体实现的代码;请大家告诉具体的代码怎么写:
1。打开时如何锁定一条记录?
2。如何扑获更新错误?在delphi中调试时会报“该记录读出后已经被再次修改”,而在运行时如何判定错误为更新冲突?因为更新时其他的错误如输入不合法等也可能报错,如何把更新冲突和其他的分开?
=====================================================================
首先,这个问题只有在特殊情况下才算是问题,大多数情况下可以不作考虑。
然后,这是问题很难描述清楚,解决方案有多种,下面提供一种较方便易用的方式
场景(问题)描述如下:
0,用户A、B同时打开一个页面,页面显示,客户表T_CUSTOMER字段(C_NAME、C_AGE)
姓名:张三,年龄:25
1,A 将姓名“张三”改为“张三1”,然后保存
2,B 将年龄“25”改为“30”,然后保存
这样A的操作就被覆盖了,姓名又变回“张三”了,大家一般怎么处处这种情况?
这里给出一个较易用的解决方案
给表添加一字段:LAST_UPDATE,即最后更新时间
0,用户A、B同时打开一页面,面页显示:
姓名:张三,年龄:25,LAST_UPDATE: 13:45:00
1,A 将姓名“张三”改为“张三1”,然后保存
重点在这里:更新数据时WHERE条件里多一条件:AND LAST_UPDATE = ' 13:45:00'
更新成功,此时触发器会将当前时间“ 13:46:00”赋值给LAST_UPDATE
2,B 将将年龄“25”改为“30”,然后保存
B更新数据时WHERE条件里也有这个条件:AND LAST_UPDATE = ' 13:45:00',但此时LAST_UPDATE的值已经在A修改记录时变成 13:46:00
下面要做的就是给出提示了:喔哟,此信息在你发呆这段时间已被人改过啦,所以你需要返工。
触发器代码如下:
===================================================
CREATE OR REPLACE TRIGGER T_CUSTOMER
BEFORE UPDATE ON T_CUSTOMER
FOR EACH ROW
记录最后修改时间
:NEW.LAST_UPDATE := SYSDATE;
===================================================
如果触发器不熟悉或者只是不喜欢用触发器,完全可以修改记录时同时给LAST_UPDATE字段赋值,以此替代触发器的作用。
----------------------------------------------------------------------------------------------------------------
【并发操作】多用户并发操作的解决方案
【问题】在以前的系统开发中,经常遇到一个同样问题,就是多个用户同时并发操作一条记录,这次在交易系统开发过程中,又出现了这样问题。比如交易商A提交单子,由审核人员B审核,此时A正在修改单位,B也正在查看这条记录,A先修改保存后B再审核保存,导致B审核通过的记录不是他所看到的。
【分析】仔细考虑问题,大概分析了三个方法, 并确定了一个可行的方案,可能还有不完善的地方,但解决现有问题还是绰绰有余的。
最先想的是在交易业务代码中用lock对修改方法加锁,运行时注入单例,并且对方法加同步锁,保证了业务数据的正确性, 但是效率低下,用户在使用中不方便,背离了系统可以并发操作的原则。
然后考虑使用悲观锁,这次运行时注入原型,并发操作效率提高, 但是,在同步处理数据库表时又造成锁表,这样并发效率依旧很低。
最后考虑乐观锁, 只是一种数据版本校验机制,它不做数据库层次上的锁定,需要在要并发操作的主表中加入一个&VERSION&字段或者“LASTUPDATETIME”,如果研究过oracle的SCN应该有异曲同工之妙,它的原理是这样:运行时注入原型,这时数据表并不锁住,只是每次update或insert时将VERSION更新, 当下次update,insert时,会校验VERSION,如果不一致,此时提示信息已经修改过了,并且在事务控制下rollback,这样,保证了数据的正确性,并且也不影响并发操作的效率。但是出现的问题是:如果A读了数据,未来及更新,B先更新了数据,
那么他将提交不上数据,因为VERSION已经失效,这样就造成了A重新读取数据,重新填写单据信息。 这也是乐观锁一个缺点,可以在更新前临时保存A填写的数据,再在跳转页中加载这些数据,保证了交易商不用麻烦再次输入这些数据,更新成功则清空这些临时数据
乐观锁在并发操作问题上,保证了业务效率和数据的正确性,基本可以采用这种方案解决交易中并发问题。
●悲观锁:指在应用程序中显式地为数据资源加锁。悲观锁假定当前事务操
纵数据资源时,肯定还会有其他事务同时访问该数据资源,为了避免当前
事务的操作受到干扰,先锁定资源。尽管悲观锁能够防止丢失更新和不可
重复读这类并发问题,但是它会影响并发性能,因此应该很谨慎地使用悲
●乐观锁:乐观锁假定当前事务操纵数据资源时,不会有其他事务同时访问
该数据资源,因此完全依靠数据库的隔离级别来自动管理锁的工作。应用
程序采用版本控制手段来避免可能出现的并发问题。
----------------------------------------------------------------------------
.Net中的事务处理(多用户同时操作一条信息时是用-并发) [Web Applicaion in C#]
SqlConnection myConnection = new SqlConnection(&Data Source=Initial Catalog=NIntegrated Security=SSPI;&);
myConnection.Open();
SqlTransaction myTrans = myConnection.BeginTransaction(); //使用New新生成一个事务
SqlCommand myCommand = new SqlCommand();
myCommand.Transaction = myT
mandText = &Update Address set location='23 rain street' where userid='0001'&;
myCommand.ExecuteNonQuery();
Console.WriteLine(&Record is udated.&);
catch(Exception e)
myTrans.Rollback();
Console.WriteLine(e.ToString());
Console.WriteLine(&Sorry, Record can not be updated.&);
myConnection.Close();
需要注意的是,如果使用OleDb类而不是Sqlclient类来定义SQL命令和连接,我们就必须使用OleTransation来定义事务。
数据库系统程序员需要比一般应用软件程序员懂得更多。一般程序员对事务处理的理解不够全面。事务处理的关键是在提交事务或者取消事务时,万一系统崩溃了,数据库在再次启动时,仍然需要保持数据可逻辑一致性。
最简单的事务处理过程如下:
1. 开始一个事务。进入“事务待命”状态。
2. 在“事务待命”状态,记录事务中改变的数据库记录。此改变不能直接改变数据库中的值,必须先用一个顺序的“事务日志”记录在一边。同时,对于要改变的原始记录加锁,让其它用户无法读和写。(考虑银行取款问题,可以发现如果此时两个用户都在对同一帐号取数,然后减去一定金额,在先后写回数据库,银行就亏钱了)。如果记录已经被其它事务加锁,则报错;同一事务中却可以重复加锁。
3. 在“事务待命”,如果用户给出commit transaction命令,则进入“事务拷贝”状态,拷贝所有加锁的记录成备份。
4. 上面3执行完,则进入“事务更新”状态,用“事务日志”中记录一一更新实际的数据库记录。
5. 上面4执行完,则进入“事务结束”状态,释放所有的记录锁,然后抛弃“事务日志”和备份的原数据库记录。
6. 上面5做完后,事务被删除。
但是,最为关键的是,事务系统必须执行以下过程:一但数据库由于软件、硬件问题发生故障,重启动后,一旦有事务没正常删除,则:
7. 如果在“事务待命”、“事务结束”状态,则重新从5中结束事务的动作开始执行。
8. 如果在“事务更新”状态,则重新从4开始更新记录,并继续想下执行。结果,虽然系统崩溃过,但事务仍然能正常提交。
Informix、Oracle、DB2等数据库的实际事务处理流程更复杂,目的是具有更好的抵抗系统错误性质,因为事务保护是业务系统安全稳定的最后一道防线(比用2个CPU、热备份等更重要。因为那些方法对已经混乱错误的数据照样保护,结果经常造成更多问题)。由于事务处理的流程比一般程序想像得复杂,因此可能会感到用起来比较繁琐。但是了解事务在保护数据库方面的良苦用心,就可以更好地设计出更灵活的业务流程。
如果“一个完整的事务可能包括a,b,c,d四个小事务”就比较奇怪。既然是个过程构成一个事务,没必要中间在划分为4个小事务,问为中间任何操作出错都需要整个“回滚”到a之前。在执行完a后用一个if语句判断要不要再执行“b,c,d”就行,end if之后提交事务。
应用中包含的事务应当尽量让它“瞬间”完成,避免在比较忙时造成用户进程的互锁。事务比较频繁的系统,一秒钟有几个用户互锁就可能造成严重问题,因为事务是以一个稳定的频率来的,而服务器上互锁的进程越积越多,几个小时后,可能有上百台机器都死掉了,只能一台一台地重新开机,花费很多时间、金钱,还会被用户骂死!
但是并不是说事务之间就不能有用户交互。可以在用户3分钟还没确认后,就自动报告错误,并且取消事务。不过我从不冒这个险,而是使用下面方法。
将事务拆分成两段,需要非常深入地研究用户业务流程,研究用户在发现业务数据不一致时是如何纠正的,并且继承用户手工操作流程。这样,软件分析的工作量相应加大了。有时,这是必然的,应当说服用户接受这种现象,并且与开发者一起逐步解决。
比如,卖彩票的交易,用户输入彩票号码,此时在POS机打印彩票并且记账。这并不需要在卖彩票时与后台服务器实时联网,只要每隔10分钟上传一次数据就行了。只有这样,才能在现有的硬件和网络条件下使得彩票销售开展起来。如果开发者固守者彩票号码录入、服务器记账、前台打印合并为一个事务的天真想法,其产品一定会在激烈的市场中败阵。
当然,随着硬件、软件、网络的不断变化,如何灵活应用事务的方法会不断变化,没有一定的规矩,关键要看软件的效果。虽然大的事务可能不断拆分成小的事务,中间用业务流程联系起来;同时,合并一些小的事务或者由计算机自动处理业务数据不一致问题,从而给用户提供网络上的“傻瓜相机”一样的易用、稳定的产品仍然是今天最有挑战意义的软件工程目标。
--------------------------------------------------------------------------------------
这个问题不是C#处理的,而是你调用的存储过程或批查询需要处理的,如果你用C#执行一系列的更新语句的话,你可以使用ADO .NET SqlClient的事务来控制并发时的数据完整性!
SqlConnection conn=new SqlConnection(connectionstr);
SqlTransaction mytran=conn.BeginTransaction();
SqlCommand cmd=new SqlCommand();
cmd.Connection=
cmd.Transaction=
<mandText=.....;
int rc=cmd.ExecuteNoQuery();
你可以通过错误处理机制来控制事务提交还是会滚...
--------------------------------------------------------------------------------------------
我在程序中使用一种原始的方法处理需要锁定的内容。
比如我的表中存储有订单的行项目,每次只允许一个用户对行项目进行编辑。
建立“锁定表”,每当用户编辑订单时,在&锁定表&中加入订单号。当加入失败时则说明已有用户在编辑订单,当用户退出订单或锁定时间超过一个阈&#20540;时则删除锁定记录,允许其他用户编辑并锁定订单。
这样处理适合锁定多行的订单类数据,锁定表中可以保存其他附加信息。
还有一种方法在数据库中使用事务。使用事务更新数据库,这样可以保证同一时间只有一个用户可以对行更新。
本文已收录于以下专栏:
相关文章推荐
create table account (
id int primary key auto_increment,
name varchar(20),
money double
1.两种查询引擎查询速度(myIsam 引擎 )
InnoDB 中不保存表的具体行数,也就是说,执行select count(*) from table时,InnoDB要扫描一遍整个表来计算有多少行...
为了解决大型网站的访问量大、并发量高、海量数据的问题,我们一般会考虑业务拆分和分布式部署。我们可以把那些关联不太大的业务独立出来,部署到不同的机器上,从而实现大规模的分布式系统。但这之中也有一个问题,...
分布式是以缩短单个任务的执行时间来提升效率的,而集群则是通过提高单位时间内执行的任务数来提升效率。
集群主要分为:高可用集群(High Availability Cluster),负...
转载博客地址:http://blog.csdn.net/zxl333/article/details/8454319
一个小型的网站,比如个人网站,可以使用最简单的html静态页面就...
事务处理(多用户同时操作一条信息时是用-并发)
在c/s或多层中,如果两个用户同时打开一条记录,修改后提交会产生更新冲突;&#160;
据说办法有二:1。打开同时锁定表的记录 2。浦获错误,撤消其中一个...
在以前的系统开发中,经常遇到一个同样问题,就是多个用户同时并发操作一条记录,这次在交易系统开发过程中,又出现了这样问题。比如交易商A提交单子,由审核人员B审核,此时A正在修改单位,B也正在查看这条记录...
使用三个线程实现ABCABC……循环。
代码如下://标记类,用来让三个线程共享,同时也是三个线程中同步代码快的标记对象。
//之前这个标记我设置成Integer,但是发现Integer进行加法运算...
题目:有A,B,C三个线程, A线程输出A, B线程输出B, C线程输出C,要求, 同时启动三个线程, 按顺序输出ABC, 循环10次。
解题思路:要按顺序输出ABC, 循环10次,就要控制三个线程...
事务处理(多用户同时操作一条信息时是用-并发)
在c/s或多层中,如果两个用户同时打开一条记录,修改后提交会产生更新冲突;
据说办法有二:1。打开同时锁定表的记录 2。浦获错误,撤消其中一个用户...
他的最新文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)内表是每个ABAP开发人员都必须懂的,数据从R3取出来后,就是放在内表里处理的,其实就是Java中的集合框架,只是没有那么多不同数据结构的内表,目前只有标准、排序、Hash三种,这还是新的语法,老的只有个标准的,关于内表这方面的定义、性能,以后我专贴一篇文章吧。这里只是对内表的常用操作,这也是项目中用得最多的点!
3.4.适合所有类型的内表操作
COLLECT [&wa&INTO] && 将具有相同关键字段值的行中同名的数字字段的值累计到一条记录上,只有非表关键字段被累加;当在内表中找不到指定的被累加行时,COLLECT语句的功能与APPEND语句是一样的,即将一个工作区的内容附加到itab内表中。使用COLLECT操作的内表有一个限制,即该的行结构中,除了表键字段以外的所有字段都必须是数字型(i、p、f)
INSERT &wa& INTO TABLE &itab&."单条插入
INSERT LINES OF &itab1& [FROM &n1&] [TO &n2&] INTO TABLE&itab2&"批量插入
向UNIQUE 的排序表或哈希表插入重复的数据时,不会抛异常,但数据不会被插入进去,这与是不一样的
"只要根据关键字或索引在内表中读取到相应数据,不管该数据行是否与COMPARING 指定的字段相符,都会存储到工作区
&itab& WITH KEY{&k1& = &f1& ... &kn& = &fn&...[]}
&&&&&&&&&&&&&& INTO &wa& [COMPARING &f1&&f2& ...|ALL FIELDS]
[TRANSPORTING &f1&&f2& ...|ALL FIELDS|NO FIELDS]
READ TABLE &itab& FROM &wa&&以表关键字为查找条件,条件值来自&wa&
COMPARING:系统根据&k1&...&kn&(关键字段)读取指定的单行与工作区&wa&中的相应组件进行比较。
如果系统找根据指定&k1&...&kn&找到了对应的条目,且进行比较的字段内容相同,则将 SY-SUBRC 设置为0,如果进行比较的字段内容不同,则返回值 2;如果系统根据&k1&...&kn&找不到条目,则包含 4。如果系统找到条目,则无论比较结果如何,都将其读入wa中
&itab& FROM &wa& [TRANSPORTING &f1& &f2& ...]"修改单条MODIFY TABLE && 修改哈希表,且itab内表带表头)。这里的&wa&扮演双重身份,不仅指定了要修改的行(条件),还包括要修改的新的值。系统以整个表的所有关键字段来搜索要修改的行;USING KEY:如果未使用此选项,则会使用默认的主键primary table key来修改相应的行;如果找到要修改的行,则将&wa&中所有非关键字段的内容拷贝到对应的数据行中对应的字段上;如果有多行满足条件时只修改第一条
MODIFY &itab& FROM &wa& TRANSPORTING&f1&&f2&...WHERE&cond&"修改多条
DELETE TABLE&itab& FROM &wa& "删除单条。多条时,只会删除第一条。条件为所有表关键字段,值来自&wa&
DELETE TABLE &itab& &k1& = &f1& ..."删除单条。多条时只会删除第一条,条件为所有表关键字
DELETE itab WHERE ( col2 & 1 ) AND ( col1 & 4 )"删除多行
DUPLICATESFROM &itab& [COMPARING&f1&&f2& ... | ALL FIELDS]
注,在未使用COMPARING 选项时,要删除重复数据之前,一定要按照内表关键字声明的顺序来进行排序,才能删除重复数据,否则不会删除掉;如果指定了COMPARING 选项,则需要根据指定的比较字段顺序进行排序(如COMPARING &F1&&F2&时,则需要sort by &F1&&F2&,而不能是sort by &F2&&F1&),才能删除所有重复数据&
3.5.适合索引内表操作
APPEND &wa& TO &itab&
APPEND LINES OF &itab1& [FROM&n1&] [TO&n2&] TO&itab2&
INSERT &wa& INTO &itab& &idx&"如果不使用 INDEX 选项,则将新的行插入到当前行的前面,一般在Loop中可省略INDEX 选项
INSERT LINES OF &itab1& [FROM &n1&] [TO &n2&] INTO &itab2& INDEX &idx&
APPEND/INSERT操作不能用于Hash表
APPEND/INSERT&INDEX用于排序表时条件:附加/插入时一定要按照Key的升序来附加;如果是Unique排序表,则不能附加/插入重附的数据,这与是不一样的
READ TABLE &itab& INDEX &idx&
&&&&&& INTO &wa& [COMPARING &f1&&f2& ...|ALL FIELDS]
[TRANSPORTING &f1&&f2& ...|ALL FIELDS|NO FIELDS]
&&&&&&&&&&&&&&&&&&&&& | ASSIGNING &fs&
MODIFY &itab& [INDEX &idx& ] FROM &wa& [TRANSPORTING &f1& &f2& ... ]"如果没有 INDEX 选项,只能在
DELETE &itab& [INDEX&idx&]"删除单条。如果省略&index&选项,则DELETE &itab&语句只能用在循环语句中
DELETE&itab& [FROM&n1&] [TO&n2&] WHERE&condition& "删除多条
3.1.LOOP AT循环内表
LOOP AT itab {INTO wa}|{ASSIGNING &fs& [CASTING]}|{TRANSPORTING NO FILDS} [[USING&KEY key_name|(name)] [FROM idx1] [TO idx2] [WHERE log_exp|(cond_syntax)]].
FROM & TO: 只适用于标准表与排序表&&&&&&&&& WHERE && : 适用于所有类型的内表
如果没有通过USING KEY选项的key_name,则循环读取的顺序与表的类型相关:
l标准表与排序表:会按照primary table index索引的顺序一条条的循环,且在循环里SY-TABIX为当前正在处理行的索引号
l哈希表:由于表没有排序,所以按照插入的顺序来循环处理,注,此时SY-TABIX 总是0
可以在循环内表时增加与删除当前行:If you insert or delete lines in the statement block of a LOOP , this will have the following effects:
If you insert lines behind(后面) the current line, these new lines will be processed in the subsequent loop(新行会在下一次循环时被处理) passes. An endless loop(可能会引起死循环)can result
If you delete lines behind the current line, the deleted lines will no longer be processed in the subsequent loop passes
If you insert lines in front(前面) of the current line, the internal loop counter is increased by one with each inserted line. This affects sy-tabix in the subsequent loop pass(这会影响在随后的循环过程SY-TABIX)
If you delete lines in front of the current line, the internal loop counter is decreased by one with each deleted line. This affects sy-tabix in the subsequent loop pass
如果在 AT - ENDAT 块中使用 SUM,则系统计算当前行组中所有行的数字字段之和并将其写入工作区域中相应的字段中
3.1.2.AT...ENDAT
内表的第一行时触发
内表的最后一行时触发
相邻数据行中相同&f&字段构成一组,在循环到该组的开头时触发
END Of &f&
相邻数据行中相同&f&字段构成一组,在循环到该组的最末时触发
在使用AT...... ENDAT之前,一这要先按照这些语句中的组件名进行排序,且排序的顺序要与在AT...... ENDAT语句中使用顺序一致,排序与声明的顺序决定了先按哪个分组,接着再按哪个进行分组,最后再按哪个进行分组,这与SQL中的Group By 相似
用在AT...... ENDAT语句中的中的组件名不一定要是结构中的关键字段,但这些字段一定要按照出现在AT关键字后面的使用顺序在结构最前面进行声明,且这些组件字段的声明之间不能插入其他组件的声明。如现在需要按照&f1&, &f2&, ....多个字段的顺序来使用在AT...... ENDAT语句中,则首先需要在结构中按照&f1&, &f2&, ....,多字段的顺序在结构最前面都声明,然后按照&f1&, &f2&, ....,多字段来排序的,最后在循环中按如下的顺序块书写程序(请注意书写AT END OF的顺序与AT NEW 是相反的,像下面这样):
LOOP AT &itab&.
AT FIRST. ... ENDAT.
AT NEW &f1&. ...... ENDAT.
AT NEW &f2&. ...... ENDAT.
&single line processing&
AT END OF &f2&.... ENDAT.
AT END OF &f1&. ... ENDAT.
AT LAST. .... ENDAT.
一旦进入到 AT...&f1&...ENDAT 块中时,当前工作区(或表头)中的从&f1&往后,但不包括&f1&(按照在结构中声明的次序)所有字段的字符类型字段会以星号(*)号来填充,而数字字设置为初始值(注:在测试过程中发现String类型不会使用*来填充,而是设置成empty String,所以只有固定长度类型的非数字基本类型才设置为*)。如果在 AT 块中使用了SUM,则会将所有数字类型字段统计出来将存入当前工作区(或表头);但一旦离开AT....ENDAT块后,又会将当前遍历的行恢复到工作区(或表头)中
DATA: BEGIN&OF th_mseg OCCURS&10,&&&&& TYPE mard-matnr,TYPE mard-werks,&&&&& TYPE mard-lgort,TYPE mseg-shkzg,&&&&& menge TYPE mseg-menge, budat TYPE mkpf-budat,LOOP&AT th_mseg.& AT&END&OF&."会根据shkzg及前面所有字段来进行分组&&& sum.&&& WRITE: / th_mseg-matnr, th_mseg-werks,th_mseg-lgort,&&&&&&&&&&&& th_mseg-shkzg,th_mseg-menge,th_mseg-budat.& ENDAT.ENDLOOP.
AS-101&&&&&&&&&&&&
S&&&&&&&&&& 10.000& ****.**.**
AS-100&&&&&&&&&&&&
S&&&&&&&&&& 10.000& ****.**.**
AS-100&&&&&&&&&&&&
S&&&&&&&&&& 20.000& ****.**.**
上面由于没有根据matnr + werks + lgort + shkzg 进行排序,所以结果中的第三行其实应该与第一行合并。其实这个统计与SQL里的分组(Group By)统计原理是一样的,Group By 后面需要明确指定分组的字段,如上面程序使用SQL分组写法应该为 Group Bymatnr werks lgort shkzg,但在ABAP里你只需要按照 matnr werks lgort shkzg按照先后顺序在结构定义的最前面进行声明就可表达了Group By那种意义,而且不一定要将matnr werks lgort shkzg这四个字段全部用在AT语句块中AT NEW、AT END OF shkzg 才正确,其实像上面程序一样,只写AT END OF shkzg这一个语句,前面三个字段matnr werks lgort都可以不用在AT语句中出现,因为ABAP默认会按照结构中声明的顺序将shkzg前面的字段也全都用在了分组中了
DATA: BEGIN OF line,&&&&& "C2、C3组件名声明的顺序一定要与在AT...... ENDAT块中使用的次序一致,即这里不能将C3声明在C2之前,且不能在C2与C3之间插入其他字段的声明&&&&& c2(5) TYPE c,&&&&& c3(5) TYPE c,&&&&& c4(5) TYPE c,&&&&& i1 TYPE i,&&&&& i2 TYPE i,&&&&& c1(5) TYPE c,&&&&& END OF line.
"使用在AT...... ENDAT语句中的字段不一定要是关键字段DATA: itab LIKE TABLE OF line WITH HEADER LINE WITH NON-UNIQUE KEY i1.PERFORM append USING 2 'b' 'bb' 'bbb' '2222' 22.PERFORM append USING 3 'c' 'aa' 'aaa' '3333' 33.PERFORM append USING 4 'd' 'aa' 'bbb' '4444' 44.PERFORM append USING 5 'e' 'bb' 'aaa' '5555' 55.PERFORM append USING 6 'f' 'bb' 'bbb' '6666' 66.PERFORM append USING 7 'g' 'aa' 'aaa' '7777' 77.PERFORM append USING 8 'h' 'aa' 'bbb' '8888' 88.SORT&itab ASCENDING BY&c2 c3.LOOP AT itab.& WRITE: / itab-c2,itab-c3,itab-c1,itab-c4,itab-i1,itab-i2.ENDLOOP.SKIP.LOOP AT itab.& AT FIRST.&&& WRITE:/ '&&&& AT FIRST'.& ENDAT.& AT NEW&c2.&&& WRITE: / '&&& &&&& Start of' , itab-c2.& ENDAT.& AT NEW c3.&&& WRITE: / '&&&&&&& &&&& Start of' , itab-c2, itab-c3.& ENDAT.& "只要一出 AT 块,则表头的数据又会恢复成当前被遍历行的内容& WRITE: / itab-c2,itab-c3,itab-c1,itab-c4,itab-i1,itab-i2.& AT END OF c3.&&& SUM.&&& WRITE: / itab-c2,itab-c3,itab-c1,itab-c4,itab-i1,itab-i2.&&& WRITE: / '&&&&&&& &&&& End of' , itab-c2, itab-c3.& ENDAT.& AT END OF c2.&&& SUM.&&& WRITE: / itab-c2,itab-c3,itab-c1,itab-c4,itab-i1,itab-i2.&&& WRITE: / '&&& &&&& End of' , itab-c2.& ENDAT.& AT LAST.&&& SUM.&&& WRITE: / itab-c2,itab-c3,itab-c1,itab-c4,itab-i1,itab-i2.&&& WRITE:/ '&&&& AT LAST'.& ENDAT.ENDLOOP.TYPES: c5(5) TYPE c.FORM append& USING&&& value(p_i1) TYPE Ivalue(p_c1) TYPE c5 value(p_c2) TYPE c5&&&&&&&&&&&&&&&&&&&&& value(p_c3) TYPE c5 value(p_c4) TYPE c5 value(p_i2) TYPE i.& itab-i1 = p_i1. itab-c1 = p_c1. itab-c2 = p_c2.& itab-c3 = p_c3. itab-c4 = p_c4. itab-i2 = p_i2.& APPEND itab.ENDFORM.
aa&&& aaa&& c&&&& 3333&&&&&&&&&& 3&&&&&&&&& 33
aa&&& aaa&& g&&&& 7777&&&&&&&&&& 7&&&&&&&&& 77
aa&&& bbb&& d&&&& 4444&&&&&&&&&& 4&&&&&&&&& 44
aa&&& bbb&& h&&&& 8888&&&&&&&&&& 8&&&&&&&&& 88
bb&&& aaa&& a&&&& 1111&&&&&&&&&& 1&&&&&&&&& 11
bb&&& aaa&& e&&&& 5555&&&&&&&&&& 5&&&&&&&&& 55
bb&&& bbb&& b&&&& 2222&&&&&&&&&& 2&&&&&&&&& 22
bb&&& bbb&& f&&&& 6666&&&&&&&&&& 6&&&&&&&&& 66
&&&& AT FIRST
&&&& Start of aa
&&&& Start of aa&&& aaa
aa&&& aaa&& c&&&& 3333&&&&&&&&&& 3&&&&&&&&& 33
aa&&& aaa&& g&&&& 7777&&&&&&&&&& 7&&&&&&&&& 77
aa&&& aaa&& ***** *****&&&&&&&&& 10&&&&&&&& 110
&&&& End of aa&&& aaa
&&&& Start of aa&&& bbb
aa&&& bbb&& d&&&& 4444&&&&&&&&&& 4&&&&&&&&& 44
aa&&& bbb&& h&&&& 8888&&&&&&&&&& 8&&&&&&&&& 88
aa&&& bbb&& ***** *****&&&&&&&&& 12&&&&&&&& 132
&&&& End of aa&&& bbb
aa&&& ***** ***** *****&&&&&&&&&& 22&&&&&&&& 242
&&&& End of aa
&&&& Start of bb
&&&& Start of bb&&& aaa
bb&&& aaa&& a&&&& 1111&&&&&&&&&& 1&&&&&&&&& 11
bb&&& aaa&& e&&&& 5555&&&&&&&&&& 5&&&&&&&&& 55
bb&&& aaa&& ***** *****&&&&&&&&&& 6&&&&&&&&& 66
&&&& End of bb&&& aaa
&&&& Start of bb&&& bbb
bb&&& bbb&& b&&&& 2222&&&&&&&&&& 2&&&&&&&&& 22
bb&&& bbb&& f&&&& 6666&&&&&&&&&&& 6&&&&&&&&& 66
bb&&& bbb&& ***** *****&&&&&&&&&& 8&&&&&&&&& 88
&&&& End of bb&&& bbb
bb&&& ***** ***** *****&&&&&&&&&& 14&&&&&&&& 154
&&&& End of bb
***** ***** ***** *****&&&&&&&&&&& 36&&&&&&&& 396
&&&& AT LAST
3.1.3.自已实现AT...ENDAT
如果循环的内表不是自己定义的,有时无法将分组的字段按顺序声明在一起,所以需要自己实现这些功能,下面是自己实现AT NEW与AT END OF(另一好处是在循环内表时可以使用Where条件语句)(注:使用这种只需要按照分组的顺序排序即可,如要分成bukrs与bukrs anlkl两组时,需要按照BY bukrs anlkl排序,而不能是BYanlkl bukrs):
DATA: lp_bukrs TYPE bukrs, "上一行bukrs字段的值&&&&& lp_anlkl TYPE anlkl. "上一行anlkl字段的值
"下面假设按bukrs,bukrs anlkl分成两组SORT itab_data BY bukrs&anlkl.DATA: i_indx& TYPE i .
DATA: lwa_data& Like itab_data LOOP AT itab_data where flg = 'X'.
i_indx& = sy-tabix.& **********AT NEW 对当前分组首行进行处理
IF& itab_data-bukrs && lp_bukrs. "Bukrs组&&&&& ".........&&& ENDIF.&&& IF& itab_data-bukrs && lp_bukrs OR itab_data-anlkl && lp_anlkl. "bukrs anlkl 分组&&&&& ".........&&& ENDIF.&&& IF& itab_data-bukrs && lp_bukrs OR itab_data-anlkl && lp_anlkl OR& itab_data-.. && lp_.. . "bukrs anlkl .. 分组&&&&& ".........&&& ENDIF.
& "**********普通循环处理& ".........& "**********AT END OF 对当前分组末行进行处理&
&DATA : l_nolast1,l_nolast12 . "不是分组中最末行
& "这里还是要清一下,以防该代码直接写在报表程序的事件里,而不是Form里(直接放在Report程序事件里时,l_nolast1,l_nolast12将会成为全局变量)& CLEAR:& l_nolast1,l_nolast12,l_nolast...&&& DO.&&&&& i_indx& = i_indx& + 1.&&&&& READ TABLE itab_data INTO lwa_data& INDEX& i_indx."尝试读取下一行&&&&& IF sy-subrc && 0."当前行已是内表中最后一行&&&&&&& EXIT.&&&&&&& 如果第一分组字段都发生了变化,则意味着当前行为所有分组中的最后行&&&&&&& "注:即使有N 个分组,这里也只需要判断第一分组字段是否发生变化,不&&&&&&& "需要对其他分组进行判断,即这里不需要添加其他 ELSEIF 分支&&&&& ELSEIF lwa_data-bukrs && itab_data-bukrs.&&&&&&& EXIT.&&&&& ENDIF.********断定满足条件的下一行不是分组最的一行&&&&& "如果Loop循环中没有Where条件,则可以将下面条件 lwa_data-flg = 'X' 删除即可&&&&& IF sy-subrc = 0 AND lwa_data-flg = 'X' .&&&&&&& IF lwa_data-bukrs = itab_data-bukrs ."判断当前行是否是 bukrs 分组最后行&&&&&&&&& l_nolast1 = '1'.&&&&&&&&& IF lwa_data-nanlkl = itab_data-nanlkl ."判断当前行是否是 bukrs nanlkl 分组最后行&&&&&&&&&&& l_nolast2 = '1'.&&&&&&&&&&& IF lwa_data-.. = itab_data-..."判断当前行是否是 bukrs nanlkl ..分组最后行&&&&&&&&&&&&& l_nolast.. = '1'.&&&&&&&&&&& ENDIF.&&&&&&&&& ENDIF.&&&&&&&&& EXIT."只要进到此句所在外层If,表示找到了一条满Where条件的下一行数据,因此,只要找到这样的数据就可以判断当前分组是否已完,即一旦找到这样的数据就不用再往后面找了,则退出以防继续往下找&&&&&&& ENDIF.&&&&& ENDIF.
&&& ENDDO.
&& IF l_nolast..IS INITIAL"处理 bukrs nanlkl ..分组&&&&& ......&&& ENDIF.
&&& IF l_nolast2 IS INITIAL ."处理 bukrs nanlkl 分组&&&&& ......&&& ENDIF.&&& IF l_nolast1 IS INITIAL ."处理 bukrs 分组&&&&& ......&&& ENDIF.
& lp_bukrs = itab_data-bukrs.& lp_anlkl = itab_data-anlkl.
lp_.. = itab_data-.. .ENDLOOP.
3.2.在LOOP AT中修改当前内表行
3.2.1.循环中修改索引表
: BEGIN OF line&,& key&,& val TYPE i ,& END OF line&.DATA: itab1 TYPE line OCCURS 0 WITH HEADER LINE .DATA: itab2 TYPE line OCCURS 0 WITH HEADER LINE .itab1-key&= 1.itab1-val = 1.APPEND itab1.itab2 = itab1.APPEND itab2.itab1-key&= 2.itab1-val = 2.APPEND itab1.itab2 = itab1.APPEND itab2.LOOP AT itab1.& WRITE: / 'itab1 index: ' , sy-tabix.& READ TABLE itab2 INDEX 1 TRANSPORTING NO FIELDS."试着读取其他内表& "READ TABLE itab1 INDEX 1 TRANSPORTING NO FIELDS."读取本身也不会影响后面的 MODIFY 语句& WRITE: / 'itab2 index: ', sy-tabix.& itab1-val = itab1-val + 1.& "在循环中可以使用下面简洁方法来修改内表,修改的内表行为当前正被循环的行,即使循环中使用了& "READ TABLE语句读取了其他内表(读取本身也没有关系)而导致了发生了改变,因为以下& 语句不是根据sy-tabix来修改的(如果在前面读取内表导致sy-tabix 发生了改变发生改变后,再使用
MODIFY itab1 INDEX sy-tabix语句进行修改时,反而不正确。而且该语句还适用于Hash内表,需在"MODIFY后面加上TABLE关键字后再适用于Hash表&&请参见后面章节示例)
& MODIFY itab1.ENDLOOP.itab1.& WRITE: / itab1-key,itab1-val.ENDLOOP.
3.2.2.循环中修改HASH表
: BEGIN OF line&,& key&,& val TYPE i&,& END OF line&.DATA: itab1 TYPE HASHED TABLE OF line WITH HEADER LINE& WITH UNIQUE KEY key.DATA: itab2 TYPE line OCCURS 0 WITH HEADER LINE .itab1-key&= 1.itab1-val = 1.INSERT itab1 INTO TABLE itab1.itab2 = itab1.APPEND itab2.itab1-key&= 2.itab1-val = 2.INSERT itab1 INTO TABLE itab1.itab2 = itab1.APPEND itab2.
LOOP AT itab1.& WRITE: / 'itab1 index: ' , sy-tabix."循环哈希表时,sy-tabix永远是0& READ TABLE itab2 INDEX 1 TRANSPORTING NO FIELDS.& WRITE: / 'itab2 index: ', sy-tabix.& itab1-val = itab1-val + 1.&&& &MODIFY TABLE&itab1."注:该语句不一定在要放在循环里才能使用&&循环外修改Hash也是一样的,这与上面的索引表循环修改是不一样的,并且修改的条件就是itab1表头工作区,itab1即是条件,也是待修改的值,修改时会根据内表设置的主键来修改,而不是索引号
ENDLOOP.LOOP AT itab1.& WRITE: / itab1-key,itab1-val.ENDLOOP.
3.3.第二索引
三种类型第二索引:
2UNIQUE& HASHED:&&&&&&&&&& 哈希算法第二索引
2UNIQUE& SORTED:&&&&&&&&&&&& 唯一升序第二索引
2NON-UNIQUE& SORTED:非唯一升序第二索引
sbook_tab TYPE STANDARD TABLEOF sbook&&&&& "主索引:如果要为主索引指定名称,则只能使用预置的&primary_key,但可以通过后面的 ALIAS 选项来修改(注:ALIAS选项只能用于排序与哈希表)&&&&& WITH NON-UNIQUE KEY primary_key "ALIAS my_primary_key&&&&&&&&&& COMPONENTS carrid connid fldate bookid&&&&& "第一个第二索引:唯一哈希算法&&&&& WITH UNIQUE HASHED KEY hash_key&&&&&&&&&& COMPONENTS carrid connid&&&&& "第二第二索引:唯一升序排序索引&&&&& WITH UNIQUE SORTED KEY sort_key1&&&&&&&&&& COMPONENTS carrid bookid&&&&& "第三第二索引:非唯一升序排序索引&&&&& WITH NON-UNIQUE SORTED KEY sort_key2&&&&&&&&&& COMPONENTS customid.
3.3.1.使用第二索引
1、可以在READ TABLE&itab、MODIFY&itab、DELETE&itab、LOOP AT&itab内表操作语句中通过[TABLE] KEYkey_nameK1=V1 ...或者USING KEY key_name,语句中的为第二索引名:
READ TABLE itab WITH TABLE KEY[key_nameCOMPONENTS] {K1|(K1)} = V1... INTO&wa
READ TABLE itab&WITH KEY key_nameCOMPONENTS& {K1|(K1)} = V1... INTO&wa
itab&FROM wa [USING KEY&key_name] &wa
READ TABLE itab&INDEX idx [USING KEY&key_name] INTO&wa
itab [USING KEY&key_name] FROM wa
MODIFY itab [USINGKEY loop_key] FROM wa此语句只能用在语句中,并且此时 USING KEY loop_key 选项也可以省略(其实默认就是省略的),其中loop_key是预定义的,不能写成其他名称
MODIFY itab INDEX idx [USING&KEY&key_name] FROM wa
MODIFY itab FROM wa [USING&KEY&key_name] ...WHERE ...
DELETE TABLE&itab FROM wa [USING&KEY key_name]
DELETE TABLE&itab WITH TABLE KEY [key_nameCOMPONENTS] {K1|(K1)} = V1...
DELETE itab INDEX idx [USING&KEY&key_name|(name)]
DELETE itab [USING KEY&loop_key]
DELETE itab [USING&KEY&key_name ] ...WHERE ...
DELETE ADJACENT DUPLICATES FROM itab [USING&KEY&key_name] [COMPARING K1 K2...]
LOOP AT itab USING KEY key_name WHERE... . ENDLOOP.
2、可以在INSERTitab与APPEND语句中通过USING KEY选项来使用第二索引
INSERT wa [USING KEY key_name] INTO TABLE itab
APPEND wa [USING KEY key_name] TO itab
3.3.2.示例
DATA itab TYPE HASHED TABLE OF dbtab WITH UNIQUE KEY col1 col2 ... "向内表itab中添加大量的数据&... READ TABLE itab "使用非主键进行搜索,搜索速度将会很慢&&&&&&&&&& WITH KEY&col3 = ... col4 = ... &&&&&&&&&& ASSIGNING ... 上面定义了一个哈希内表,在读取时未使用主键,在大数据量的情况下速度会慢,所以在搜索字段上创建第二索引:DATA itab TYPE HASHED TABLE OF&dbtab & WITH UNIQUE KEY col1 col2 ... & "为非主键创建第二索引& WITH NON-UNIQUE SORTED KEY second_key &&& COMPONENTS col3 col4 ... & "向内表itab中添加大量的数据&... READ TABLE itab "根据第二索引进行搜索,会比上面程序快&&&&&&&&&& WITH TABLE KEY&&&&&&&&&&&& COMPONENTS col3 = ... col4 = ... &&&&&&&&&& ASSIGNING ... "在循环内表的Where条件中,如果内表不是排序内表,则不会使用二分搜索,如果使用SORTED KEY,则循环时,会用到二分搜索?LOOP AT itab USING KEY second_key where col3 = ... col4 = ... . ENDLOOP.
阅读(...) 评论()}

我要回帖

更多关于 kd型多用户电能表 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信