ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
楼主: hyefeifei

[原创] 再讲ADO

  [复制链接]

TA的精华主题

TA的得分主题

发表于 2016-4-20 16:54 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
本帖已被收录到知识树中,索引项:ADO技术
hyefeifei 发表于 2016-4-20 16:17
已经帮你把数据提取到excel中了,另外,数据库文件也转换了,试过可以附加到sql2000,sql2008上
,数据库 ...

感激之情难以言表!台兄乃高人也。

TA的精华主题

TA的得分主题

发表于 2016-4-20 18:35 | 显示全部楼层
hyefeifei 发表于 2016-4-19 15:13
如何更新数据库表?

与插入 删除 相比 ,更新记录情况稍复杂一些。

学习,期待精彩下一讲。

TA的精华主题

TA的得分主题

发表于 2016-4-20 20:37 | 显示全部楼层
hyefeifei 发表于 2016-4-20 16:17
已经帮你把数据提取到excel中了,另外,数据库文件也转换了,试过可以附加到sql2000,sql2008上
,数据库 ...

能否请教兄台两个问题:
1. 我用你转换后的数据库附加在2008数据库中,再用2008自带的导出功能,把表cs导出为CSV后,为什么用excel打开后,不能保持原有的行列格式?而你给我的导出结果表却是正确的。
2. 原来那个数据库文件,兄台是用什么办法让它转换成为能用2008数据库附加了?

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-4-20 21:07 | 显示全部楼层
liu-aguang 发表于 2016-4-20 20:37
能否请教兄台两个问题:
1. 我用你转换后的数据库附加在2008数据库中,再用2008自带的导出功能,把表cs ...

1. csv为文本文件,应该不会出问题,导出过程注意尽量保持默认选项。
    但不建议用这种方式,可以用ado把数据取回excel,也可以在ssms里查询select * from cs
把结果复制粘贴到excel里,如果出于特殊理由需要csv文件的话,也可用上面的2种方法
先把数据取回excel,再在excel里另存为csv文件。
2.其实不需要转换,当附加到2000时,自动转换了,然后分离数据库,再把文件拷出来,
2008支持2000数据库直接附加,2012不支持,可再把附加到2008的数据库分离,然后就
可以附加到2012上了。

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2016-4-20 22:26 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
hyefeifei 发表于 2016-4-20 21:07
1. csv为文本文件,应该不会出问题,导出过程注意尽量保持默认选项。
    但不建议用这种方式,可以用ad ...

受教了!非常感谢你的无私讲解。
关于第一个问题用“导出导入”功能,ts表导出后行列格式不对,可能是该表的H列内容中含有回车换行符的缘故。后面我自己再研究研究你提供的另外两种方法。

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-4-20 22:31 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
本帖最后由 hyefeifei 于 2016-5-16 15:01 编辑

        凡是学sql的,几乎都是从scott数据库开始的,以后的讲解也要以这个库为例证,所以在这里把这个库提供给大家,见附件: 38-46.rar (1.53 MB, 下载次数: 77)

        在继续讲更新之前,先讲一下recordset的find方法和filter属性。  

        图一:  
1.jpg

        大家看图一这个表,deptno是部门编号,假如我想知道部门30所有人的工资,该如何做?  

        当然你会说用:  

        Select ename, sal from emp where deptno=30  

        就可以直接得到30部门的人员工资。  

        但这里我们要使用临时表得到结果,程序片断如下:  

    Set rst = CreateObject("adodb.recordset")
    strSQL = "select * from emp"
    rst.cursorlocation = adUseClient
    rst.Open strSQL, Conn, adOpenStatic, adLockOptimistic, adCmdText
    rst.Find "deptno=30"
    Do While Not rst.EOF
        Debug.Print rst!ename, rst!sal
        rst.Find "deptno=30", 1
    Loop

        下面我们依次解释一下这段程序:  

        1.Strsql=”select * from emp”,要知道,临时表必须先用select语句把数据取回,才能操作,但这一句把所有数据都取回了,实际工作中表的数据可能好几十万,显然必须用where加以限定才可以,即你只需把要处理的数据取回即可,比如说你是财务人员,通常会把当月数据取回。  

        2.rst.cursorlocation=adUseClient,这句是设定客户端临时表,这句不加,会有什么后果?因为服务器端临时表是默认的,所以省了这句,就会形成服务器端临时表,当你继续后面的操作时,比如说这里后面要执行find方法,程序就会连到服务器,用sql服务器的系统存储过程创建游标,移动游标取数据,每次取几条数据,由recordset的cachesize属性决定,默认每次取1,操作完成后释放游标。  

        你注意到后面的程序,在do  loop循环里使用find方法,这意味着频繁连数据库,移动游标取数据。  

        所以结论是:虽然服务器端临时表也可以用find方法,但你应该或者说建议你只有在客户端临时表时才用find方法,这也适用于后面要讲到的filter属性。  

        3.rst.Open strSQL, Conn, adOpenStatic, adLockOptimistic, adCmdText  

        Recordset的open方法,第三个参数为以什么方法形成临时表,adopenstatic为静态临时表,当cursorlocation属性为adUseClient时,也就是说对本地临时表,这个参数无论取什么值,都是静态临时表,或者说这个参数可省略。  

        因为建议大家做程序时,总是要用客户端临时表,所以这个参数无所谓,但还是建议大家明确地取adopenstatic,表明为静态临时表。  

        adLockOptimistic这个参数好象前面讲了,有空再细讲,这里就不说了。  
        adCmdText:它是告诉ado,strsql里放的是sql语句,这个参数如果你取别的值,比如adCmdTable,那么,程序要改为:  
        strSQL = "emp"  
        rst.cursorlocation = adUseClient  
        rst.OpenstrSQL, Conn, adOpenStatic, adLockOptimistic, adCmdTable  
        ……  
        当执行到open方法时,ado会把命令翻译为标准的sql语句select * from emp提交给服务器。建议不要这样做,这种简单的sql语句还是我们直接提供好了,不要让ado费事再转一次。当然复杂的语句它也转不了。  

        4.rst.Find "deptno=30",find是个方法,但这句你写成rst.find=”deptno=30”时,程序也能正常运行,不知为何。  

        5.rst.Find "deptno=30", 1,循环里一定要加这句,否则成死循环了。执行find方法,当找到符合条件的记录时,它会把这个记录置为当前记录,第二个参数设定从当前记录向后多少条记录开始查找,省略时,当然是从当前记录查找,设为1是,是说要从当前记录的下1条记录开始查找,2时为从当然记录的下2条记录开始查找,依此类推。  

TA的精华主题

TA的得分主题

发表于 2016-4-21 11:14 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
hyefeifei 发表于 2016-4-20 22:31
下面我们依次解释一下这段程序:  
        1.Strsql=”select * from emp”,要知道,临时表必须 ...

学习,多少有点眉目了,期待下一讲。

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-4-21 13:05 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
本帖最后由 hyefeifei 于 2016-5-16 15:03 编辑

  该讲Filter属性了,请看Emp表

  Emp表:


2016-04-21_095835.jpg

  现在我们要把部门30的人员工资打印出来,该怎么做呢?上一讲说到可以用Find方法,Find方法是每执行一次,都会把找到的记录置为当前记录,而隐藏当前记录之前的记录,filter属性不同,它是把凡是具有此属性(deptno=30这个属性)的记录显示,而不具有此属性的记录隐藏,且看程序片断:

      strSQL = "select * from emp"
      rst.cursorlocation = adUseClient
      rst.Open strSQL, Conn, adOpenStatic, adLockBatchOptimistic, adCmdText
      rst.Filter = "deptno=30"
      Do While Not rst.EOF
          Debug.Print rst!ename, rst!sal
          rst.movenext
     Loop

  下面还是依次讲解一下这段代码:

  1. rst.cursorlocation = adUseClient 这个属性要设置为客户端,原因不讲了。

  2. rst.Filter = "deptno=30",filter可值可以是字符串,如果有多个条件,可用and or 分隔,例如:你想找部门30,职务为SALESMAN,且名字以M开头的员工,可以设置过滤字符串为:

  Rst.Filter=”deptno=30 and job=’SALESMAN’ and ename like ‘M%’”

  这里要注意的是,如果字段名里有空格,则字段名要用[]括上。

  还有一处需要注意,大家看下图:

2016-04-21_102732.jpg

  我在表中增加了一个员工,名叫SMITHT’T,假如我想过滤出这名员工怎么办?

  了解sql语句的朋友都知道,sql语句应该为:SELECT * FROM emp WHERE ename='SMITH''T'

  所以这里正确的过滤字符串为:rst.Filter = "ename='SMITH''T'"

  Filter属性除了可以是字符串以外,还可以是ado内置常量,用得上的内置常量见下表:


FilterGroupEnum Values  
常量
值  
描述  
  adFilterNone  
0
  默认值:清除当前过滤器  
  adFilterPendingRecords  
1
  只显示当前具有未定改动的记录  
  adFilterConflictingRecords  
5
只显示在最近一次批更新中未成功的记录  

  比如,当我们用rst.Filter = "ename='SMITH''T'"时,不符合条件的会隐藏,现在我们想把所有记录全部显示出来,就可以这样:

  rst.Filter =adFilterNone(rst.Filter =0, rst.Filter =’’与之等价)

  其他两个常量用着的时候再讲吧,另外,filter也可以是2、3、4,因为基本用不上,所以省略了,学这个ADO,用不着急着求全。

  今天先到这里吧,待续……

TA的精华主题

TA的得分主题

 楼主| 发表于 2016-4-22 10:27 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
本帖最后由 hyefeifei 于 2016-5-16 15:04 编辑

  下面我们讲一下客户端临时表更新数据库,我们只讲客户端临时表。

    还是把Emp表贴在这里:


2.jpg      

     假如我要给ADAMS长500元工资,我该怎么做?

     看下面代码:

          strSQL = "select * from  emp"
          rst.cursorlocation =  adUseClient
          rst.Open strSQL, Conn,  adOpenStatic, adLockOptimistic, adCmdText
          rst.Find "ename='ADAMS'"
          rst!sal = rst!sal + 500
          rst.Update

  adLockOptimistic参数意味着当程序执行到rst.Update时,才需要连接服务器,把操作查询语句传给数据库,在此之前,其他人是可以对数据进行修改的。

  当程序执行到rst.Update时,ado向数据库递交查询语句,当然sql数据库只认sql语句,不会认识rst.update的,所以,ado会把ado的语句翻译成sql语句递交给数据库。

  问题来了,如果你是ado,你会把:

          rst.Find  "ename='ADAMS'"
          rst!sal = rst!sal + 500
          rst.Update

  翻译成什么sql语句呢?

  有朋友说了,因为我是要把ADAMS的工资增加500,所以应该把这段程序翻译成:
  UPDATE emp SET sal=sal+500 WHERE ename=’ADAMS’

  错了,ado会先把程序这句rst!sal = rst!sal + 500算出来,再更新数据库,难道可以翻译成下面的句子吗?
  UPDATE emp SET sal=1600 WHERE ename=’ADAMS’

  还是错,把这个语句递交给数据库执行的话,会把所有与ADAMS同名的员工都加500元工资,这当然不行,因为另一个ADAMS工资已经很高了,我只想给工资为1100元的ADAMS加薪。

  那么ADO会如何做呢?它首先要找到Emp表的主键,其次,确定当前记录的主键值是多少,然后把主键做为条件放到where的后面,所以可译成:
  UPDATE emp SET sal=1600 WHERE EMPNO=7876

  那么ADO是如何知道表的哪个字段是主键呢?答案是当程序执行到:
  rst.Open strSQL, Conn, adOpenStatic, adLockOptimistic, adCmdText
  时,ado会从服务器返回表的元信息。大家看下面的截图,可以看到表Emp的主键为EMPNO。


       3.jpg

  小钥匙表明EMPNO字段为主键。

  如果ADO把UPDATE emp SET sal=1600 WHERE  EMPNO=7876提交给数据库,那么ADAMS的工资就是变成1600,但这样做的话,想像一下,恰好在此之前,同样有加薪权限的另一人浏览数据,把ADAMS的工资已修改成1800,当你提交修改时,就会覆盖那个人的修改。

  如果规则是谁给涨的多就以谁的为准,那就会出现错误。

  当然,你在实际工作中可能会遇到各种情境。

  所以,ADO实际上会默认把代码译成下面的sql语句提交数据库:
  UPDATE emp SET sal=1600 WHERE EMPNO=7876 AND sal=1100

  这个语句如果直接在ssms中运行的话,不会修改任何数据,也没有任何提示(假设此前有人把工资修改成1600了)

  但是在ado中运行,却会返回错误,见图:


       22.jpg   

  出现错误正是我们需要的,我们可以捕捉错误号,以确定是否有人先于自己修改了记录,然后针对此种情况进行处理。

  简单的错误处理如下:


      Sub upEmp()
          Dim rst As Object
          Dim strSQL As String
          On Error GoTo Errorhandle
          If Conn Is Nothing Then  Connect
          If Conn.State = 0 Then  Conn.Open
          Set rst =  CreateObject("adodb.recordset")
          strSQL = "select * from  emp"
          With rst
              .cursorlocation =  adUseClient
              .Open strSQL, Conn,  adOpenStatic, adLockOptimistic, adCmdText
              .Properties("Update  Resync") = adResyncConflicts
              .Find  "ename='ADAMS'"
              !sal = !sal + 500
              .Update
          End With
      Exithere:
          Conn.Close
          Exit Sub
      Errorhandle:
          If Err.Number = -2147217864  Then
              'rst!sal.Value =  rst!sal.Value - rst!sal.OriginalValue + rst!sal.UnderlyingValue
              Resume
          Else
              GoTo Exithere
          End If
      End Sub

  在解释这段程序之前,先看一下ADO对象模型,如图:

       5.jpg

  有阴影的为集合,Fields集合存放Field对象,我们讲一下Field对象的三个属性:

      1. OriginalValue            2. UnderlyingValue           3. Value

   
属性名称
  
数据类型
描述
OriginalValue
Variant
存放字段原先的值
UnderlyingValue
Variant
存放数据库表中字段当前的值
Value
Variant
存放字段本地的当前值

  具体解释见下图:

    4.jpg

  Sub upEmp程序大家先自己理解一下,待抽空再依次讲解,暂到这里,待续……
   

TA的精华主题

TA的得分主题

发表于 2016-4-22 10:47 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
关注老师新作
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-12-4 17:02 , Processed in 0.037792 second(s), 9 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表