打印本文 打印本文  关闭窗口 关闭窗口  
在VFP里来操作和管理SQL-SERVER
作者:佚名  文章来源:不详  点击数  更新时间:2008/6/19 14:50:40  文章录入:杜斌  责任编辑:杜斌

  用VFP和SQL SERVER来做系统的网友越来越多。怎么从VFP里来操作或者管理SQL SERVER也成为很多人关心的问题。 对SQL SERVER的数据操作,可以用视图,SPT或者ADO来做,但对服务器本身的管理,似乎只有用SPT来发送一些命令了。其实, 微软为客户端操作SQL SERVER开发了一套完整的工具, 这就是SQL - DMO (Distributed Management Objects). 这是一套COM组件,可以在各种语言里使用, VFP里自然也可以用了。
  我准备花点时间,写些这方面的介绍文章,然后做个实例,算是我通过MCDBA后,给大家的礼物吧。下面的介绍和例子,是针对SQL SERVER 2000和VFP7的。

  一. 用SQL -DMO的必要性

  大部分VFPER开发的程序,都是中小规模的系统,因为这是VFP最适用的范围。而这样的系统,很多情况下是,客户端没有专业IT人士维护,更不要说专门的DBA了。对于使用SQL SERVER的系统,就带来了一些问题。 SQL SERVER是一套比较大的数据库专业软件, 是经常需要维护和清理的,而我们这些系统开发者, 总不能老跑去用户那里做维护工作吧,尤其对商业软件来说,这是很大的成本了。如果可以把对SQL SERVER的维护工作放到自己的程序里,甚至把SQL SERVER的安装无缝并入到自己的程序的安装包里,相信大家都会很乐意的。 DMO的功能就能满足我们的这些需要。通过它,可以从程序里用命令方式来操作SQL SERVER:停止/启动服务,建立数据库和表,添加用户和权限,备份/恢复数据库,数据导入/导出/发布... DMO的作用就是把我们能在Enterprise Manager里完成的功能,完全用代码来做.

  二. 微软的桌面数据引擎 (Desktop Database Engine)

  我们都知道SQL SERVER 2000有四个版本: 个人版,开发者版,标准班和企业版. 其实,还有一个版本,就是桌面数据引擎(DDE)。 DDE几乎具有
  SQL SERVER标准版和企业版的所有功能,不能支持的只是少数几个不常用的功能。而它和其它版本的区别是, DDE没有任何图形管理界面,没有ENTERPRISE MANAGER,没有查询分析器等等工具。 对它的管理,主要是通过DMO或者专门的API来进行,它的另一个优点是, 提供了安装程序,可以直接加到第3方软件里进行安装。每个版本的SQL SERVER CD里都带有DDE, 在MSDE目录下,运行SETUP程序就可以安装。至于怎么把它加到自己的程序里安装,大家查一下SQL SERVER的帮助文件就可以找到。
  DDE的限制是: 只适宜中小型系统(比如没有5个以上的用户同时连接和运行大型SQL命令), 如果同时访问的用户很多, DDE就会比正常的SQL SERVER慢了。
  这样, DDE+DMO就可以实现我们上面所要求的功能了。这种做法甚至可以不让用户知道你是在使用SQL SERVER做为数据库。当然, DMO不是只能操作DDE,它可以操作所有版本的SQL SERVER。

  三. DMO的安装

  其实DMO主要就是一个DLL文件而已: SQLDMO.DLL。具体位置在C:\Program Files\Microsoft SQL Server\80\Tools\Binn目录里, 在同一目录里还有个帮助文件Sqldmo80.hlp. 如果你在程序里不能使用DMO, 找到这个DLL文件,注册一下,就可以了。另外在C:\Program Files\Microsoft SQL Server\80\Tools\ Devtools\Samples\Sqldmo 目录下,有SQLDMO的例子,但都是VB和VC++的。VB的例子和VFP很接近的.

  四. 初步接触DMO

  在这里写几个简单的句子,来看看DMO的强大. 这些命令可以直接在命令窗口里一行一行测试, 只是用它们来显示数据库的信息。至于添加数据库,数据表,备份和恢复等比较复杂的功能,得在实例里做。
  oServer=CreateObject("SQLDMO.SQLServer") &&建立SERVER对象
  oServer.Connect("Snoopy","sa","778899") &&连接到你的服务器
  ? oServer.Databases.Count &&显示服务器上的数据库总数
  ? oServer.Databases.Item(1).Name &&显示第一个数据库的名字
  oDB=oServer.Databases("PUBS") &&建立数据库对象
  ? oDB.Tables.Count &&显示库里表的数目
  oTable=oDB.Tables("Titles") &&建立表对象
  ? oTable.Rows &&显示表里的记录数
  ? oTable.Columns.Count &&显示表的列数
  **显示列的属性
  ? oTable.Columns(1).Name
  ? oTable.Columns(1).type
  ? oTable.Columns(1).Datatype
  ? oTable.Columns(1).Identity
  ? oTable.Columns(1).IsPrimaryKey 

  备份和恢复数据库
  oServer=CreateObject("SQLDMO.SQLServer") &&建立SERVER对象
  oServer.Connect("Snoopy","sa","778899") &&连接到你的服务器
  **备份
  oBackup=CreateObject("SQLDMO.Backup") &&建立备份对象
  oBackup.Database="PUBS" &&指定备份数据库
  oBackup.Password="8899" &&给备份文件加密码
  oBackup.Files="D:\Temp\PUBBack.Dat" &&指定目标文件
  oBackup.SQLBackup(oServer) &&运行备份命令,速度很快的
  ***注: 备份数据时默认为追加方式,可以加一句
  oBackup.initialize=.T.
  这样就会覆盖原来的文件。或者每次BACKUP时用不同的文件名,比如把日期作为文件名的一部分.
  **备份恢复
  oRestore=CreateObject("SQLDMO.Restore")
  oRestore.Database="PUBS"
  oRestore.Files="D:\Temp\PUBBack.Dat"
  oRestore.SQLRestore(oServer) &&先试不加密码,备份失败
  oRestore.Password="8899"
  oRestore.SQLRestore(oServer)
  SQL SERVER提供几种数据库备份方式,
  一种是完整备份 (Full Backup), 第二种是差异备份(Differential Backup), 第三种是日志备份, 第四种是文件备份
  完整备份是把整个数据库做个备份,差异备份只是备份进行了完整备份后数据库里的新变化。当数据库很大时, 完整备份很费空间和时间,可以根据情况定期做,比如每周或者每月做一次完整备份。 其它时间可以进行差异备份,比如每天一次,或者半天一次, 也可以结合进行日志备份。文件备份是直接备份数据库的数据和日志文件。和在操作系统里做备份一样.
  在恢复的时候,只要先恢复完整备份,然后恢复最后一个差异备份就可以。如果有日志备份,还需要恢复差异备份后所做的日志备份。
  备份种类通过 备份对象的Action属性来决定.
  oBackup.Action=0 && 参数: 0 - 完整备份,1- 差异备份, 2- 文件备份, 3 -日志备份

  用DMO来建立任务,并让它定时运行. 执行环境必须是NT或者WINDOWS 2000, 因为需要运行SQL AGENT
  ***建立任务(JOB)
  oJob=CreateObject("SQLDMO.Job") &&创建任务对象
  ojob.Name="Pubs_Daily_Backup" &&任务名称
  oServer.JobServer.Jobs.Add(oJob) &&加到SQL SERVER的任务里
  oJob.BeginAlter &&开始定义任务
  oJobStep=CreateObject("SQLDMO.JobStep") &&创建任务步骤对象
  oJobStep.Name = "Step_1" &&步骤名称
  oJobStep.StepID = 1 &&步骤号码
  oJobStep.DatabaseName="Pubs" &&数据库名称
  oJobStep.SubSystem= "TSQL" &&任务类型,可以是TSQL或者操作系统命令(CmdExec), 或者ActiveScripting
  cFName="PubBack"+Dtoc(Date(),1)+".dat" &&备份目标文件名
  oJobStep.Command ="Backup database Pubs to Disk=''''D:\Temp\"+cFname+"'''' With Password=''''7788'''' " &&执行备份的TSQL命令
  oJobStep.OnFailAction = 2 && 如果任务失败退出。也可以设成发送EMAIL或者用NT里的NETSEND发信息
  oJobStep.OnSuccessAction= 1 && 如果任务成功,退出。
  oJob.JobSteps.Add(oJobStep) &&添加任务步骤
  oJob.StartStepID = 1 &&从第一步开始执行,对多步骤的任务起作用
  oJob.DoAlter &&保存修改
  ***把任务加到SCHEDULER里
  oJobSchedule =CreateObject("SQLDMO.JobSchedule") &&创建Schedule对象
  oJob = oServer.JobServer.Jobs("Pubs_Daily_Backup")&&创建任务对象
  oJobSchedule.Name = "Daily_Execution" &&名称
  oJobSchedule.Schedule.FrequencyType= 4 && 运行频率, 4是每日运行
  oJobSchedule.Schedule.FrequencyInterval = 1 &&运行间隔, 1是每天
  oJobSchedule.Schedule.ActiveStartDate =Dtoc(Date(),1) &&开始日期(今天), 必须是yyyymmdd格式
  oJobSchedule.Schedule.ActiveStartTimeOfDay = "233000" &&开始时间(晚上11点30), 必须是 hhmmss格式
  **下面2句设置是使任务永不过期
  oJobSchedule.Schedule.ActiveEndDate=99991231 &&no end date
  oJobSchedule.Schedule.ActiveEndTimeOfDay =235959 &&No end time
  添加任务到SCHEDULE里
  oJob.BeginAlter
  oJob.JobSchedules.Add(oJobSchedule)
  oJob.DoAlter
  在VFP里来操作和管理SQL SERVER (3) (介绍SQL-DMO)
  创建数据库,表和用户
  1. 建新数据库
  oServer=CreateObject("SQLDMO.SQLServer")
  oServer.Connect("Snoopy","sa","778899")
  ? oserver.VerifyConnection(oServer.ConnectionID) &&检查连接是否成功
  oDB=CreateObject("SQLDMO.Database") &&创建数据库对象
  oLogFile=CreateObject("SQLDMO.LogFile") &&创建表日志文件对象
  oDBFile=CreateObject("SQLDMO.DBFile") &&创建数据文件对象
  oDB.Name="MyNewDB" &&数据库名
  oDBFile.Name="MyNewDBData1" &&数据文件的逻辑名字,可以在SQL各种命令里使用,不一定和物理名字相同
  oDBFile.PhysicalName="c:\program files\microsoft sql server\mssql\data\MyNewDBData.mdf" &&数据文件物理名字
  oDBFile.PrimaryFile = .T. &&主文件群
  oDBFile.FileGrowthType= 0 && 文件增长类型 0 - MB, 1 - 百分比
  oDBFile.FileGrowth=1 &&增长大小 (1M)
  oDB.FileGroups("PRIMARY").DBFiles.Add (oDBFile) &&添加到文件群里
  oLogFile.Name="MyNewDBLog" &&日志文件逻辑名
  oLogFile.PhysicalName = "c:\program files\microsoft sql server\mssql\data\northwnd.ldf" &&物理名
  oDB.TransactionLog.LogFiles.Add(oLogFile) &&添加到数据库里
  oServer.Databases.Add(oDB) &&添加数据库。 这时候应该会出错。 因为日志文件指向了Northwind.ldf, 这是已经存在的文件
  oLogFile.Remove &&从数据库里删除
  oLogFile.PhysicalName = "c:\program files\microsoft sql server\mssql\data\MyNewDB.ldf" &&重新指定物理名
  oDB.TransactionLog.LogFiles.Add(oLogFile) &&重新添加
  oServer.Databases.Add(oDB) &&建立数据库
  2.建表
  oDB=oServer.Databases("MyNewDB") &&数据库对象
  oTable=CreateObject("SQLDMO.Table") &&表对象
  oTable.Name="FirstTable" &&表名
  oTable.FileGroup="Primary" &&文件群
  oCol1=CreateObject("SQLDMO.Column") &&列对象(字段)
  oCol1.Name="Num" &&列名
  oCol1.Datatype="Int" &&数据类型
  oCol1.Identity=.T. &&是否IDENTITY字段
  oCol1.IdentityIncrement=1 &&增加量
  oCol1.IdentitySeed=1 &&种子
  oCol1.AllowNulls=.F. &&是否允许NULL
  **第2列
  oCol2=CreateObject("SQLDMO.Column")
  oCol2.Name="UserName"
  oCol2.Datatype="varchar"
  oCol2.Length=10
  oCol2.AllowNulls=.F.
  **第3列
  oCol3=CreateObject("SQLDMO.Column")
  oCol3.Name="Email"
  oCol3.Datatype="varchar"
  oCol3.Length=30
  oCol3.AllowNulls=.F.
  **添加列到表对象里
  oTable.Columns.Add(oCol1)
  oTable.Columns.Add(oCol2)
  oTable.Columns.Add(oCol3)
  **添加表到数据库里
  oDB.Tables.Add(oTable)
  3. 给表增加主键
  oFirstTable=ODB.Tables("FirstTable") &&创建表对象
  oKey=CreateObject("SQLDMO.Key") &&创建索引对象
  oKey.Name="FirstTable_PK" &索引名字
  oKey.KeyColumns.Add("Num") &&索引列
  oKey.Clustered=.T. &&是否物理索引
  oKey.Type=1 && 索引类型 1- 主键 2- 唯一键 3 - 外部键(Foreign)
  oFirstTable.BeginAlter &&修改表对象
  oFirstTable.Keys.Add(oKey) &&添加索引
  oFirstTable.DoAlter &&保存修改

  4. 添加登录用户
  oLogin=CreateObject("SQLDMO.Login") &&创建登录对象
  oLogin.Name="myf1" &&登录名
  oLogin.Type= 2 && 登录类型 2 - SQL 登录 0 -NT 用户 1 -NT 用户群
  oServer.Logins.Add(oLogin) &&添加到服务器。 密码为空
  oServer.Logins("myf1").SetPassword("","www1") &&添加成功后,修改密码
  oDBUser=CreateObject("SQLDMO.User") &&创建数据库用户对象
  oDBUser.Name="myf1" &&数据库用户名,可以和登录名不同
  oDBUser.Login="myf1" &&登录名
  oDB.Users.Add(oDBUser) &&添加到数据库
  5. 给用户授权
  **假设上边的表对象还存在
  oFirstTable.Grant(1,"myf1") &&第一个参数: 1 - Select 2 - Insert 4 - Update 8 - Delete 63 - 所有权限
  oFirstTable.Deny(4,"myf1","Email") &&可以把权限限制到列。 这条命令是拒绝该用户对EMAIL列的修改
  oFirstTable.Grant(63,"myf1") &&所有权限

  数据导入和导出
  1. 导出SQL SERVER数据到TEXT文件
  oServer=CreateObject("SQLDMO.SQLServer") &&创建SERVER对象
  oServer.Connect("Snoopy","sa","778899") &&连接到服务器snoopy
  oTable=oServer.Databases("PUBS").Tables("Titles") &&建立表对象
  oBulk=CreateObject("SQLDMO.BulkCopy") &&建立BULKCOPY对象
  oBulk.DataFilePath="C:\Temp\Title.Txt" &&目标文件和路径
  oBulk.DataFileType= 1 && 数据文件类型 1- 文本文件,以逗号作为字段分隔符, 回车和换行符作为行分隔符
  oBulk.TruncateLog=.T. &&导出成功后,删除日志文件,否则日志文件会变得很大
  oBulk.FirstRow=1 &&从第一行开始,可以任意设置,但必须在文件包含的行数范围内,下面的属性也是这样
  oBulk.LastRow=10 &&最后一行
  oBulk.ErrorFilePath="C:\Temp\ErrLog.txt" &&错误信息存放文件
  oBulk.MaximumErrorsBeforeAbort=1 &&遇到几个错误后退出
  oTable.ExportData(oBulk) &&执行导出任务
  2. 从TEXT文件导入数据到SQL表
  oTable=oServer.Databases("MyNewDB").Tables("FirstTable")
  oBulk=CreateObject("SQLDMO.BulkCopy")
  oBulk.DataFilePath="C:\Temp\Test1.txt"
  oBulk.DataFileType= 1
  oBulk.TruncateLog=.T.
  oBulk.ErrorFilePath="C:\Temp\ErrLog.txt"
  oBulk.ColumnDelimiter="," &&字段分隔符为逗号
  oBulk.ServerBCPKeepIdentity=.T. &&该属性控制IDENTITY字段的表现,如果为.T., 则文本文件的该字段值会插入到SQL表里,即SQL表的INDENTITY功能暂时关闭。 如果为.F., 则SQL表会自动生成IDENTITY值,文本文件里该列的值被忽略(但该列不能省略,否则格式不对会出错)
  oBulk.ServerBCPKeepNulls=.T. &&是否允许NULL
  oBulk.UseServerSideBCP=.T. &&执行服务器端的BCP(而不是ODBC端的), 因为有IDENTITY,设置必须如此设置
  oTable.ImportData(oBulk) &&导入数据
  **改变IDENTITY设置,再试一次
  oBulk.ServerBCPKeepIdentity=.F.
  oTable.ImportData(oBulk)
  TEST1.TXT文件里包含下面的内容:(因为论坛会自动给@前加IE图标,EMAIL地址里我没有加这个符号)
  1,Zhang3,zhang3.abc.com
  2,Li4,li4.abc.com
  5,Wang5,wang5.abc.com
  如果在VFP里使用COPY TO Delimited把DBF文件输出到TEXT文件,然后再导入到SQL SERVER里,有一个毛病,就是字符型数据全部加上了引号,而SQL SERVER会把引号当作字段内容导入。所以在导入前需要先想法把引号去掉。

  运行SQL命令和获取数据
  DMO的SERVER对象,可以运行TSQL命令或者操作系统的命令,这也为我们在程序里运行操作系统命令提供了一个接口。
  oServer.CommandShellImmediate("Calc.exe") &&打开WINDOWS下的计算器
  oServer.CommandShellImmediate("Notepad.exe") &&运行Notepad
  **DATABASE对象,可以运行TSQL命令
  oDB=oServer.Databases("PUBS") &&创建数据库对象
  oDB.ExecuteImmediate("Update Titles Set Title=''''Sushi, anyone else?'''' Where Title_ID=''''TC7777''''")&&执行Update命令
  oResult=oDB.ExecuteWithResults("Select * From Titles") &&运行SELECT命令,结果放到一个结果集里
  ? oResult.Rows &&显示总行数
  ? oResult.Columns &&总列数
  ? oResult.ColumnName(1) &&第一列字段名
  ? oResult.ColumnType(5) &&第5列数据类型
  **注意下面的命令,因为这是完全在代码方式下操作,返回的数据结果总是一个值,可以用行号和列号来返回任何一个记录的某个字段的值,但只能用列号,不能用列名, 如果一定想用列名,那就得通过oResult.Column(i).Name来判断,这样可以得到列号。
  ? oResult.GetColumnString(5,3) &&以字符串格式返回第5行第3列数据
  ? oResult.GetRangeString(1,1,10,5) &&将范围从第一行第一列数据到第10行第5列数据组合成一个字符串
  SQL-DMO系列文章暂时写到这里。有时间我想在VFP里做个界面,把大部分DMO的功能加进去, 用做VFP里管理SQL SERVER的界面, 做完后会贴出来. 我把这些文章都合并到第一个贴子里。
  有个比较重要的部分是, DMO很多对象都有自己的事件,比如BACKUP对象,有PercentCompletes事件,可以返回完成备份的百分比。在VFP里需要建立相应的类,把这些事件捆绑到VFP自己的事件里,才能捕捉到这些事件。因为没法在命令窗口里测试,而且需要比较多的时间,也许我会放在以后的实例里。

打印本文 打印本文  关闭窗口 关闭窗口