sql与PB程序的优化

来源:互联网 发布:windows qt开发环境 编辑:程序博客网 时间:2024/04/27 10:33

下面内容从CSDN的一篇贴子中找到的。

我用过的一个优化方法是:当检索表和视图混合的查询时,若需要检索参数,且表与视图均可用于检索参数的话,应使用视图作检索条件,速度将有明显提升。

另附两篇关于 pb 代码优化的网摘:

优化你的 sql 和 dw

  一、处理 SQL 语句

  1、缓冲 SQL 语句
  在应用程序中,有时需要反复调用同一组SQL语句,在这种情况下,可以通过在应用中为这些 SQL建立缓冲区来提高执行性能。在缺省情况下,SQL语句的缓冲区是关闭的,你可以通过如下语句打开它:
  SQLCACHE = n
  n 表示装入缓冲区的 SQL 语句数量(缺省为0)。

  例如:
  dw_1.SetTransObject(sqlca)
  SQLCA.dbParm = "SQLCache = 0"
  dw_1.retrieve()

  如果将上例的 "SQLCache = 0" 改为 "SQLCache = 25",此句的执行效率将提高五分之一左右。但应注意缓冲区的大小,否则也将影响程序执行的性能。

  注:此方法对用 ODBC 和 ORACLE 连接的数据库非常有效。

  2、捆绑变量

  请看下例:

  SQLCA.DBPARM = "DISABLEBIND=1"
  INSERT INTO DA_DH VALUES("1","河南0")
  INSERT INTO DA_DH VALUES("2","河南1")
  INSERT INTO DA_DH VALUES("3","河南2")
  INSERT INTO DA_DH VALUES("4","河南3")
  INSERT INTO DA_DH VALUES("5","河南4")
  INSERT INTO DA_DH VALUES("6","河南5")

  这里未使用捆绑变量,再插入时 PB 将重新处理每个带有新值的SQL语句。

  如果将上例改为:

  SQLCA.DBPARM = "DISABLEBIND=0"
  INSERT INTO DA_DH VALUES("1","河南0")
  INSERT INTO DA_DH VALUES("2","河南1")
  INSERT INTO DA_DH VALUES("3","河南2")
  INSERT INTO DA_DH VALUES("4","河南3")
  INSERT INTO DA_DH VALUES("5","河南4")
  INSERT INTO DA_DH VALUES("6","河南5")

  则系统将把 INSERT 语句按如下格式进行处理:
  INSERT INTO DA_DH VALUES(?,?)
  其中 "?" 称为占位符。系统性能将有所增强。

  3、用数据窗口代替 SQL 语句

  通常,为了获得某些数据,采用数据窗口和 SQL 语句都是可行的,但是PB 对数据窗口和 SQL 语句采用不同的处理机制,因此,具有不同的效率。

  例:为里检索电话档案中的用户名,可以利用 SQL 语句,将所有的数据检索到一个多行编辑中,也可以检索到一个数据窗口中。

  如果使用第一种方法:

  首先定义一个游标:
    DECLARE CUR CURSOR FOR
    SELECT "DA_DH"."HM"
    FROM "DA_DH";

  然后可以:
    STRING stxt[],st
    int li
    open cur
    do li = li + 1
    fetch cur
    into :stxt[li] ;
    st=st+stxt[li] + "~r~n"
    loop while stxt[li] <>""
    close cur;
    mle_1.txt = st

  也可以使用第二种方法:

  dw_1.settransobject(sqlca)
  dw_1.retrieve()

  利用 POWERBUILDER PROFILER 工具进行检查,对比两种方法所需时间如下

  方法     所需时间 (百分之一秒)
  SQL 语句   100.9857
  数据窗口   49.0133

  由于数据窗口或DATASTORE使用了标准的内嵌代码,而不是由开发人员进行全部编码,同时编译执行的速度比解释执行的速度快的多,因此在开发过程中应尽量使用数据窗口和DATASTORE.即使是必须用SQL语句的时候,也应该尽量将它们定义为存储过程(特别是在多用户的环境中),以提高应用程序的性能. 

  二、数据窗口的编程和执行

  数据窗口是PB最值得被称道的, 其具有如下特点:
  1. 多种显示方式.
  2. 多种编辑方式.
  3. 使用方法简单.
  4. 具有多种报表形式.
  5. 可实现屏幕滚动.
  6. 可实现数据的有效性校验.
  7. 执行性能显著提高.
  8. 编程工作变少.
  9. 可以在数据窗口内部实现数据哭的更新.

  下面, 我将介绍一些用于提高数据窗口性能的技术.

  1. 减少连接数据库的次数

  连库操作是非常影响执行速度的操作. 因此在程序中,一旦与数据库连接后就应当尽量保持与数据库的连接, 减少连接数据库的次数.PowerBuilder 提供里两个函数来建立数据窗口与事务对象的连接:
  SetTrans()
  SetTransObject()

  在程序中应当尽量使用 SETTRANSOBJECT(), 因为SETTRANS() 函数在每次调用 RETRIEVE(), UPDATE() 等函数之后, 都要执行数据库的连接和断开操作.

  2. 下拉数据窗口与表的连接

  对于数据库服务器来说, 表的连接操作是一项非常大的开销, 而 POWERBUILDER 提供的下拉数据窗口在某些情况下可以代替表的连接操作.例如, 为了在数据窗口上显示用户的电话号码和姓名:如果用表的连接的方法, 数据窗口对应的 SQL 语句应是这样的:
  SELECT "DA_DH"."DHHM","DA_HTH"."DWM"
  FROM "DA_HTH", "DA_DH"
  WHERE ("DA_HTH"."DHHM"="DA_DH"."DHHM")

  同样的程序可用下拉数据窗口来完成, 这里不再具体介绍.但是, 应当注意, 以上两种方法究竟哪一种数据更快, 与表的结构, 表的数量, 连接的方法等均有关系, 应当具体分析.

  3. 共享数据

  在一个应用程序中, 某些数据需要频繁的使用, 如果在每次使用时都从数据库中进行检索, 则需占用大量的服务器资源和网络资源. 为了减少开销, 可以在客户端对这些数据只进行一次检索, 然后允许其它任务共享这些数据.

  例如, 如果有两个数据窗口, 都需要从第三方表中检索出用户的电话号码, 且此列用下拉数据窗口给出. 如果每次都对电话号码进行检索, 则性能较低. 因此, 可以单独建立一个关于电话号码的数据窗口. 在每次打开窗口时, 首先将电话号码检索到此数据窗口中, 然后另外两个数据窗口中关于电话号码的下拉数据窗口可以共享此数据窗口中的数据.

  在窗口的 OPEN 事件中编写如下程序:
  dw_1.settransobject(sqlca)
  dw_2.settransobject(sqlca)
  dw_3.settransobject(sqlca)
  // 检索 dw_1
  dw_1.retrieve()
  // 使 DW_2 的下拉数据窗口共享 DW_1
  datawindowchild child1
  dw_2.getchild('dhhm',child1)
  child1.settransobject(sqlca)
  dw_1.sharedata(child1)
  // 使 DW_2 的下拉数据窗口共享 DW_1
  datawindowchild child2
  dw_3.getchild('dhhm',child2)
  child2.settransobject(sqlca)
  dw_1.sharedata(child1)

  使用这种方法, 避免了各个数据窗口间物理的拷贝数据, 因此减少了空间上的开销,提高了应用程序的综合性能.

  4. 数据窗口间数据的拷贝

  如果需要在数据窗口间共享数据, 应当尽量使用 SHAREDATA() 函数, 但是, SHAREDATA() 函数并不是物理地在数据窗口间拷贝数据, 如果在显示数据的同时, 还要对数据进行操作, 则需要进行数据的拷贝.

  例如, 要求将 DW_1 中选定的行拷贝到 DW_2 中:

  在窗口的 OPEN 事件中编程:
  dw_1.settransobject(sqlca)
  dw_2.settransobject(sqlca)
  dw_1.retrieve()

  在数据窗口 DW_1 的 ROWFOCUSCHANGED 事件中编写下列程序:
  long lr
  lr = dw_1.selectrow(0,false)
  lr = dw_1.getrow()
  lr = dw_1.selectrow(lr,true)

  要完成从 DW_1 到 DW_2 的拷贝工作, 有两种方法:

  第一种:

  在按钮 "拷贝" 的 CLICKED 事件中编程
  long lr
  lr = dw_1.getselectedrow(0)
  dw_1.rowscopy(lr,lr,primary!,dw_2,100,primary!)

  执行程序, 利用 POWERBUILDER PROFILER 得出所需时间为 1.7034(百分之一秒)

  第二种:

  在按钮 "拷贝" 的 CLICKED 事件中编程
  dw_2.object.data = da_1.object.data.selected

  执行程序, 利用 POWERBUILDER PROFILER 得出所需时间为 0.8062(百分之一秒)

  5. 数据窗口属性的访问和修改

  A. 数据窗口属性的访问

  在程序中访问数据窗口的属性有下列几种方法:

  A1. 采用点表达式访问

  A2. 应用多个独立的 DESCRIBE() 函数访问

  A3. 只使用一个 DESCRIBE() 函数, 采用复合参数访问多个属性

  上面三中方法, 通常第一种方法最慢, 第三种方法最快.

  例如:
  在窗口的 OPEN 事件中
  DW_1.SETTRANSOBJECT(SQLCA)
  DW_1.RETRIEVE()

  第一种方法:

  在检索按钮的 CLICKED 事件中编程.
  string dx, dy, dh, dw
  dx = dw_1.object.da_dh.x
  dy = dw_1.object.da_dh.y
  dx = dw_1.object.da_dh.height
  dy = dw_1.object.da_dh.width
  st_1.text =dx+","+dy+","+dh+","+dw
  
  第二种方法:

  string dx, dy, dh, dw
  dx=dw_1.describe("da_dh.x")
  dx=dw_1.describe("da_dh.y")
  dx=dw_1.describe("da_dh.height")
  dx=dw_1.describe("da_dh.width")
  st_1.text =dx+","+dy+","+dh+","+dw

  第三种方法:

  string dx, dy, dh, dw
  st_1.text=dw_1.describe("da_dh.x" +&
  "da_dh.y" +&
  "da_dh.height" +&
  "da_dh.width")

  实验证明, 第三种方法的速度是最快的. 但是程序的可读性最差.

  B. 数据窗口属性的修改

  在程序中修改数据窗口的属性有下列几种方法:

  A1. 采用点表达式修改

  A2. 应用多个独立的 MODIFY() 函数访问

  A3. 只使用一个 MODIFY() 函数, 采用复合参数访问多个属性

  上面三种方法, 通常第一种方法最慢, 第三种方法最快.

  例如:
  在窗口的 OPEN 事件中
  DW_1.SETTRANSOBJECT(SQLCA)
  DW_1.RETRIEVE()

  第一种方法:

  在检索按钮的 CLICKED 事件中编程.
  DW_1.SETREDRAW(FALSE)
  dw_1.object.da_dh.x = 18
  dw_1.object.da_dh.y = 16
  dw_1.object.da_dh.height = 100
  dw_1.object.da_dh.width = 200
  DW_1.setredraw(true)
  st_1.text =dx+","+dy+","+dh+","+dw

  第二种方法:

  DW_1.SETREDRAW(FALSE)
  dw_1.modify("da_dh.x = 18")
  dw_1.modify("da_dh.y = 16")
  dw_1.modify("da_dh.height = 100")
  dw_1.modify("da_dh.width = 200")
  dw_1.setredraw(true)

  第三种方法:

  dw_1.modify("da_dh.x=18" +&
  "da_dh.y=16" +&
  "da_dh.height=100" +&
  "da_dh.width=200")

  实验证明, 第三种方法的速度是最快的. 但是程序的可读性最差.注意, 在方法一和方法二中, 都使用的 setredraw() 函数以减少屏幕的重绘, 否则, 执行速度将严重下降.

 6. 数据窗口中数据的访问

  在程序中, 经常会需要动态的修改数据窗口中的数据. 对此, PB 提供了多种方法, 各种方法在性能上会有一些差异.

  A. 数据窗口中数据的访问

  目的: 将数据窗口中的电话号码存放在一个数组中.请比较下面两中方法.

  方法一:
  string da_dh[]
  long ll,i
  ll = dw_1.rowcount()
  for i = ll to 1 stet -1
  da_dh[i] = dw_1.getitemstring(i,"dhhm")
  next

  方法二:
  string da_dh[]
  da_dh[] = dw_1.object.dhhm.current

  测试发现, 第二种方法比第一种方法快将近一倍. 数据量越大这种差异越明显.

  B. 数据窗口中数据的修改

  目的: 修改数据窗口中的电话号码列的值.

  请比较下面两中方法.

  方法一:
  dw_1.setitem(i,"dhhm",l_name)

  方法二:
  dw_1.object.name[i] = l_name

  测试发现, 第二种方法比第一种方法快将近一倍. 数据量越大这种差异越明显.

  7. 数据窗口事件对性能的影响

  对于数据窗口控制中经常发生的事件, 应当尽量减少其中的程序代码. 特别是如下事件:
  a. itemchanged
  b. editchanged
  c. itemfocuschanged
  d. pbm_dwnkey
  e. rowfocuschanged
  f. retrieverow

  在这些事件中的任何处理程序, 都会降低应用程序的处理速度. 所以应当尽量减少这些事件中的处理程序, 必要时, 可以考虑只将重要的代码放在这些事件中, 而将剩余的代码放在一个传递的事件中.

  例如,如果需要用到数据窗口的 ROWFOCUSCHANGED 事件,可以为数据窗口定义一用户事件 "UE_RE",而在数据窗口的 ROWFOCUSCHANGED 事件中写如下代码:
  PARENT.postevent("ue_re")

  在 UE_RE 事件中再编写相应的程序代码,如:
  string code
  dw_1.selectrow(0,false)
  dw_1.selectrow(rownum,true)
  code = getitemstring(dw_1,rownum,"dhhm")
  dw_2.retrieve(code)

  另外, 为了获得当前行号,应尽量使用数据窗口的 CURRENTROW 变量,而少用 GETROW() 函数。

  8. 数据窗口的列名称与列编号

  对数据窗口的某列进行访问, 可以采用该列的名称, 也可以使用该列的编号。

  例如:
  采用列编号:
  dw_1.object.data[ll_row,2]
  dw_1.getitemstring(3,2)

  采用列名称表示某列:
  dw_1.object.article_text[ll_row]
  dw_1.getitemstring(3,"dhhm")
  dw_1.setitem(3,"date",1999-03-31)
  ...
 
  对于以上两种方法,如果只进行一次查询,在速度上并没有太大的区别,如果需要循环访问数据窗口上的某一列,则速度上的差异就表现的比较明显,采用第一种方法要快, 但是程序的可读性比较差。但是,在使用函数时(如 GETITEM() 和 setitem() ), 执行速度往往没有很大差别。

  9. 计算域

  数据窗口的计算域会对数据的操作性能产生影响。 如果数据窗口中包含许多复杂的计算域,将严重影响数据窗口的执行速度。


-----------------------------------------------------------------------------------------------


条件和循环类语句的优化

现今计算机的运行速度已经很快了,并且由于老板时常在耳边念着紧箍咒,因此,我们有意或者无意的忘记优化我们的代码,只要能完成任

务就行了(我也是)。不过,我们闲下来的时候,不妨也来看看我们的代码是否有需要改进的地方。下面就是我觉得值得优化的几种情况。


第一种情况:

  IF condition1 AND condition2 THEN

      //Code goes here.

END IF



IF condition1 THEN

      IF condition2 THEN

            //Code goes here.

      END IF

END IF


对于书写的第一种方式,由于PB编译方式与常见的方式不同,就是无论条件1的值如何,都要对条件2进行运算。这样一来,当条件1为false

时,就可能要无谓的对条件2进行运算了。就按随机的概率而言,可能会多进行一半的运算。因此,对于大多数情况而言,我想最好是以第二

种方式书写。当然,特殊情况也是有的,那就是你的确想对条件2进行运算。类似地,对于or也一样。


IF condition1 OR condition2 THEN

      //Code goes here.

END IF



IF condition1 THEN

      //Code goes here.

ELSE

      IF condition2 THEN

            //Code goes here.

      END IF

END IF
  第二种情况:


IF NOT condition THEN

      //Code goes here.

END IF



IF condition THEN

      //No code goes here.

ELSE

      //Code goes here.

END IF


对于上一种方式,条件表达式返回false并且再进行一个非运算,才执行下面的代码。这样相对于下面一种书写方式可能多执行了一个非运算

。如果大家有什么疑问,您不妨测试一下下面这个例子:


//小测试:其中的判断条件只是为了表示一下,实际情况可能更复杂。


long i                  //计数器

long ll_start          //执行开始时间

long ll_used1      //方式一耗时

long ll_used2      //方式二耗时


//方式一

ll_start = Cpu()

for i = 1 to 900000

      if not (1 > 1) Then

            i = i

      end if

next       

ll_used1 = Cpu() - ll_start

//方式二

ll_start = Cpu()

for i = 1 to 900000

      if 1 > 1 Then

           

      else

            i = i

      end if

Next

ll_used2 = Cpu() - ll_start


//查看结果

If ll_used2 > ll_used1 Then

      MessageBox("提示","前者运行时间短!")

Else

      MessageBox("提示","后者运行时间短!")

End If


可能有人会说,用下面的那种方式,如果在条件表达式返回false的时候,那么,if下就没有代码,这样看起来就不太舒服。的确是这样。因

此,我们在写成上面的那种方式时,尽量保持不要使用not运算,而保持条件表达式本身就返回希望的true值。

  第三种情况:
IF condition1 THEN

      //condition1

ELSEIF condition2 THEN

      //condition2 

ELSEIF condition3 THEN

      //condition3

ELSE

      //Other

END IF



choose case /*expression*/

      case /*item*/

            /*statementblock*/

      case /*item*/

            /*statementblock*/

      case else

            /*statementblock*/

end choose


对于形如这样的表达式,我想我们一般都没去考虑先后顺序。但是,其实我们应该把最可能发生的情况,放在前面,这样可以避免在对一大

堆条件进行判断后,才到我们真正需要运行代码的地方。
  

第四种情况:


 FOR ... TO ...

   

      IF condition THEN

            //true

      ELSE

            //false

      END IF

   

NEXT



IF condition THEN

      //true

      FOR ... TO ...

            //Code goes here

      NEXT

ELSE

      //false

      FOR ... TO ...

            //Code goes here

      NEXT

END IF


尽管下面这种书写方式,看起来好象代码多了一些,但是却极大的避免了在每个循环中都进行条件判断。其实,一个原则就是,我们应当尽

量避免在循环中进行条件判断,而是把条件判断放到循环体的外面进行。


其实,真正对于PB语言有特殊性的,也就是第一种情况,对于后面三种情况,对于别的编程语言,我想也同样适用。

原创粉丝点击