Excel与Sql Server互通导入导出跨语言

目录

Excel与Sql Server互通导入导出跨语言

1、目标Excel缺少表的列标题字段

1.1、问题的提出从这里开始

1.2、参数的正确写法

1.3、附带说一下Jet 4.0

1.4、附带说一下OLE DB调用数据库存储过程、Select语句、系统表或视图对象

2、获取Excel文件数据库的数据结构

    2.1、Excel文件数据库表数据集的描述字段:

    2.2、Excel文件数据库中都有哪些Sheet表:

    2.3、Excel文件数据库中Columns字段数据集的描述字段(字段数据集的字段定义):

    2.4、Excel文件数据库中某张Sheet表的Columns字段定义的描述:

    2.5、给Excel文件的某个Sheet新增1个字段

  最简单和直接的方法:

3、通过SQL语句及其数据库架构对象直接实现Excel与数据库的导入导出-BCP命令(dos命令)

   BCP是SQL Server中负责导入导出数据的一个命令行工具,它是基于DB-Library的,并且能以并行的方式高效地导入导出大容量的数据

   得出以下若干条件总结:

    3.1、创建本地Excel链接服务器及其登录,并将其数据引入Sql Server

        将下面的代码,作为DML语言在delphi客户端:

        3.1.1、供数据集类.ExecSQL

        3.1.2、或存储过程类.Execute

        3.1.3、或供数据库连接.Execute(SqlStr)

4、在Sql Server查询分析器中(相当于:或你写的App调用Sql Server存储过程)通过master..xp_cmdshell扩展存储过调用执行Bcp

5、dos下如何调用执行数据库的sql脚本

    5.1、SqlCmd.exe

    在使用命令xp_cmdshell的时候需要设置权限

    SQL2005 BCP使用心得

    SQL Server 2012 xp_cmdshell不能执行的解决办法(说是杀毒软件和防火墙惹的祸)

    5.2、打开Windows PowerShell程序或运行Cmd.exe进入命令行工具执行Bcp.exe

6、delphi中如何实现用Cmd.exe命令行工具调用执行Bcp指令:

    6.1、delphi中如何用Cmd.exe命令行工具调用执行Bcp指令调用数据库系统表或视图对象

    6.2、delphi中如何用Cmd.exe命令行工具调用执行Bcp指令调用数据库query视图对象

    6.3、delphi中如何用Cmd.exe命令行工具调用执行Bcp指令调用数据库存储过程

        6.3.1、书写好你的“存储过程”并让其在Sql Server中实际存在。

        6.3.2、创建1个Sql Server能识别的数据库实体对象Query视图,在视图中使用OpenRowSet实现视图对存储过程的查询结果集。

            6.3.2.1、你可以用Sql Server的视图设计器去产生

            6.3.2.2、你也可以用代码去动态产生

            6.3.2.3、你可能会跨数据库甚至跨域(跨服务器)设计输入:

            6.3.2.4、特别需要注意的事项

        6.3.3、Bcp调用上一步你创建的数据库query视图对象

            6.3.3.1、在Cmd命令行工具下先用Bcp指令验证一下我们的Excel文件中存储过程的输出结果

            6.3.3.2、然后我们把Bcp指令放到Delphi来调用且实现其输出结果

 

本博客相关:

喜欢的话,就在下面点个赞、收藏就好了,方便看下次的分享:


 

Excel与Sql Server互通导入导出跨语言

1、目标Excel缺少表的列标题字段

1.1、问题的提出从这里开始

        服务器: 消息 7352,级别 16,状态 1,行 1
        OLE DB 提供程序 'Microsoft.ACE.OLEDB.12.0' 提供的元数据不一致。对象 '[Microsoft.ACE.OLEDB.12.0]' 缺少预期的列 'F8'
        OLE DB 错误跟踪[Non-interface error:  OLE DB provider supplied inconsistent metadata: missing column: ProviderName='Microsoft.ACE.OLEDB.12.0' ColumnName='F8', Ordinal=8, Nullable=Y, Variable length=Y, Type=Unknown Data Type, Rowset=[Microsoft.ACE.OLEDB.12.0]]。

        服务器: 消息 7399,级别 16,状态 1,行 1
        OLE DB 提供程序 'Microsoft.ACE.OLEDB.12.0' 报错。 
        [OLE/DB provider returned message: 找不到可安装的 ISAM。]
        OLE DB 错误跟踪[OLE/DB Provider 'Microsoft.ACE.OLEDB.12.0' IDBInitialize::Initialize returned 0x80004005:   ]。

        SELECT *--count(*) FROM Excel_my...A1_NoField$  --FROM Excel_my...[A1$]  --均可识别 A1_NoField$

        【架构问题】
        服务器: 消息 7312,级别 16,状态 1,行 1
        对 OLE DB 提供程序 'Microsoft.Jet.OLEDB.4.0' 的架构和/或目录的使用无效。提供了由四部分构成的名称,但提供程序并未表现必要的接口来使用目录和/或架构。OLE DB 错误跟踪[Non-interface error]


1.2、参数的正确写法

        Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\开发包\饲料分批次进销存-存货计价和条码\ImportAndExport_xls\A1_NoField.xls;Extended Properties="Excel 8.0;HDR=YES;IMEX=1";Persist Security Info=False

        Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\开发包\饲料分批次进销存-存货计价和条码\ImportAndExport_xls\A1_NoField.xls;Mode=ReadWrite;Extended Properties=Excel 8.0;Persist Security Info=False

        Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\开发包\饲料分批次进销存-存货计价和条码\ImportAndExport_xls\A1_NoField.xls;Mode=ReadWrite;Extended Properties="Excel 8.0;HDR=YES;IMEX=0";Persist Security Info=False

        Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\开发包\饲料分批次进销存-存货计价和条码\ImportAndExport_xls\A1_NoField.xls;Extended Properties="Excel 8.0;HDR=YES;IMEX=1";Persist Security Info=False 

        Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\开发包\饲料分批次进销存-存货计价和条码\ImportAndExport_xls\A1_NoField.xls;Extended Properties="Excel 8.0;HDR=YES;IMEX=2";Persist Security Info=False

        Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\开发包\饲料分批次进销存-存货计价和条码\ImportAndExport_xls\A1_NoField.xls;Extended Properties="Excel 8.0;HDR=NO;IMEX=0";Persist Security Info=False

        SELECT a.*
           FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0',
              'Excel 8.0;HDR=YES;IMEX=2;DataBase=D:\开发包\饲料分批次进销存-存货计价和条码\ImportAndExport_xls\A1_NoField.xls',
              [A1_NoField$]) a
        GO

1.3、附带说一下Jet 4.0

        数据库连接:HDR=yes;IMEX=1起什么作用?
        String strCon = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source ='" + filename + "';Extended Properties='Excel 8.0;HDR=yes;IMEX=1'";中“HDR=yes;IMEX=1”起什么作用?
        HDR=Yes,这代表第一行是bai标题,不做为数据使du用;IMEX ( IMport EXport mode )设置
          IMEX 有三dao种模式:
          0 is Export mode
          1 is Import mode------------IMEX=1就是指混合型转换为文本
          2 is Linked mode (full update capabilities)
  我这zhuan里特别要说明shu的就是 IMEX 参数了,因为不同的模式代表著不同的读写行为:
  当 IMEX=0 时为“汇出模式”,这个模式开启的 Excel 档案只能用来做“写入”用途。
  当 IMEX=1 时为“汇入模式”,这个模式开启的 Excel 档案只能用来做“读取”用途。
  当 IMEX=2 时为“连结模式”,这个模式开启的 Excel 档案可同时支援“读取”与“写入”用途。
        意义如下:
        0 ---输出模式;
        1---输入模式;
        2----链接模式(完全更新能力)

          1)、hdr=yes时可以把xls的第1行作为字段看待,如第1个中hdr=no的话,where时就会报错

          2)、[]和$必须要

1.4、附带说一下OLE DB调用数据库存储过程、Select语句、系统表或视图对象


--OLE DB调用存储过程的错误的写法:
select * from OpenDataSource
  (
    'SQLOLEDB', 
    'Data Source=192.168.3.101;User ID=sa;Password=ad*****048'
   ).TestTemp.dbo.sp_Test_Bcp
go
--OLE DB调用数据库存储过程的正确的写法:
select a.* from OpenRowSet
  (
     'SQLOLEDB','192.168.3.101';'sa';'ad*****048',
     'TestTemp.dbo.sp_Test_Bcp'
  ) AS a
go
--OLE DB调用一个数据库Select语句的正确的写法:
select a.* from OpenRowSet
  (
     'SQLOLEDB','192.168.3.101';'sa';'ad*****048',
     'SELECT * FROM TestTemp.dbo.sdf00504'
  ) AS a
go
--OLE DB调用一个数据库表对象的正确的写法:
select * from OpenDataSource
  (
    'SQLOLEDB', 
    'Data Source=192.168.3.101;User ID=sa;Password=ad*****048'
   ).TestTemp.dbo.sdf00504
go
--OLE DB调用一个数据库Query视图对象的正确的写法:
select * from OpenDataSource
  (
    'SQLOLEDB', 
    'Data Source=192.168.3.101;User ID=sa;Password=ad*****048'
   ).TestTemp.dbo.view_sdd02010
go


declare @LVersion sysname
--启动Ad Hoc Distributed Queries(即席分布式查询):
exec sp_configure 'show advanced options',1
set @LVersion=@@Version  --select @LVersion--select substring(@LVersion,22,4) --select ltrim(rtrim(substring(@LVersion,22,5)))
if ltrim(rtrim(substring(@LVersion,22,5))) >'2000'
begin
  exec sp_configure 'Ad Hoc Distributed Queries',1  --:这一句:Sql2000/Sql2005无效也无需,只需启动MSDTC
  reconfigure with override
  --重置对Sql Server服务器的配置改变,让其生效  
end else
begin
  reconfigure  --即席更新Sql Server服务器的配置改变,让其生效
end
--1、知识点:--select len(Char(9) ) as s  --制表符的字节长度=1
--2、知识点:--Sql只支持全局临时表的CRUD语句:两个##号即为全局临时表,1个#为局部临时表(只能动态create,但不能执行CRUD的DML语句)
declare @tempTable_ACE varchar(22),@Sql varchar(1000)--,@Sql_CreatetempTable varchar(100)--:中间测试
  --set  @tempTable_ACE='##tempTable_ACE'+  right( '00000'+cast( cast( ceiling(rand() * 10) as int ) as varchar ),6 ) 
  --:字符串表达的唯一性:不要在Sql中使用低效的随机函数rand(),改由执行带毫秒的时间截的整型转字符串
set  @tempTable_ACE='##tempTable_ACE'+convert(varchar,  convert(bigint, convert(timestamp ,getdate()) ) )
set @Sql = 
  'select distinct ( ''公司编码''  +Char(9) ) as 公司编码,'
   +'( ''开票时间''  +Char(9) ) as 开票时间 '
   +' into '+@tempTable_ACE -- select into 必须显式的出现在union之前:SELECT INTO 必须是包含 UNION 运算符的 SQL 语句中的第一个查询
   +' from TestTemp.dbo.SDd02020 '-- select into 必须from 数据源对象
set @Sql = @Sql +
  'union all select ( (''@''+convert(char(10),com_id) )  +Char(9) ) as 公司编码,'
   +'( ''@''+convert(varchar(23),so_consign_date,121) +Char(9) ) as 开票时间 '
   --+' into '+@tempTable_ACE  --union不能带into,它的select主句才select into 
   +' from TestTemp.dbo.SDd02020 ; ' -- union 必须带all才不会自动去除重复的数据!!!
exec(@Sql) 
  --set @Sql = @Sql +  'select * from '+@tempTable_ACE+';'--:中间测试
  --exec(@Sql)--select * from ##tempTable_ACE1897001 --drop table ##tempTable_ACE1897001 --:中间测试

--3、知识点:下面用bcp的dos命令将【全局临时表】写入导出到excel(临时表主要是为的产生excel表头行,否则用户看不懂):
  --:3.1、使用MS Sql Server内置Bcp工具执行SQL对象与MS操作系统磁盘I/O之间的交互:
    --:前提:Sql Server安装和启动其服务的系统账户的权限,以操作系统账户Administrator +sa混合模式的安装均无问题,否则不行:
    --:遇到这样的情况就只能在操作系统的下应用调用Cmd.exe来执行Bcp命令
  --:3.2、MS操作系统用户组策略管理器,默认对每个会话并发调用Windows PowerShell程序或运行Cmd.exe命令行工具次数有默认值限制为=6次
  --go --不能加go语句 --否则:不识别【全局临时表变量】作为表名
  --......(你的Bcp指令)代码:
  -- bcp ##tempTable_ACE1897001 out D:\##tempTable_ACE1897001.xls -S"192.168.3.101" -U"sa" -P"admin81919048" -m -c -t
set @Sql = 'BCP '+@tempTable_ACE+' out D:\'+@tempTable_ACE+'.xls -S"127.0.0.1" -U"sa" -P"admin81919048" -m -c -t'
exec master..xp_cmdshell @Sql
--4、知识点:若使用OLE DB,则前提是Excel文件及其列头字段行已经存在,否则报错
  --:4.1、如果 OLE DB 提供程序在指定的数据源中支持多个目录和架构,那么就需要目录及架构名称。如果 OLE DB 提供程序并不支持目录和架构,那么可以省略 catalog 及 schema 的值。 
    --:如果提供程序只支持架构名,那么必须指定一个两部分名称,形式为 schema.object。如果提供程序只支持目录名,那么必须指定一个三部分名称,形式为 catalog.schema.object。
  --:4.2、OPENROWSET 不接受显式参数变量,只能将OPENROWSET整个函数部分或直接DML语句拼接字符串变量后exec执行@sql。
  --:4.3、权限:OPENROWSET和OpenDataSource的权限由传递到 OLE DB 提供程序的用户名的权限确定
  --:4.4、返回值:
    --:4.4.1、OPENROWSET的返回值:相当于一个子查询的行集(数据集)
    --:4.4.2、OpenDataSource的返回值:返回所链接的OpenDataSource().数据对象:是成功0;或是失败1
      --:OPENDATASOURCE 函数可以在能够使用链接服务器名的相同 Transact-SQL 语法位置中使用。
        --:因此,就可以将 OPENDATASOURCE 用作四部分名称的第一部分,
        --:该名称指的是 SELECT、INSERT、UPDATE 或 DELETE 语句中的表或视图的名称;
        --:或者指的是 EXECUTE 语句中的远程存储过程。
        --:当执行远程存储过程时,OPENDATASOURCE 应该指的是另一个 SQL Server。
      --:OPENDATASOURCE 不接受显式的参数变量,只能将OPENDATASOURCE整个函数部分或直接DML语句拼接字符串变量后exec执行@sql。
  --......(你的ACE的insert into OpenDataSource或OpenRowSet函数select from)代码:
      --若要使用OLE DB分布式查询进行insert into导出Excel,则前提是Excel文件及其列头字段行已经存在,否则报错:
  --set @Sql = '''select * from '+@tempTable_ACE+''''
  --set @Sql =
  --  'select * from '
  --    +'OpenRowSet'
  --    +'(  ''SQLOLEDB'',''127.0.0.1'';''sa'';''admin81919048'','
  --    +@Sql
  --    +')'-- select @Sql--测试

--5、知识点:顺便说一下OLE DB的OPENQUERY ( linked_server , 'query' )
  --:5.1、首先要动态增加1个不重名的链接服务器:EXEC sp_addlinkedserver 参数序列。sp_serveroption:为远程服务器和链接服务器设置服务器选项
  --:5.2、执行OPENQUERY:在给定的链接服务器(一个 OLE DB 数据源)上执行指定的直接传递查询。
    --:可以在查询的 FROM 子句中像引用表名那样引用 OPENQUERY 函数。
    --:依据 OLE DB 提供程序的能力,还可以将 OPENQUERY 函数引用为:
    --:INSERT、UPDATE 或 DELETE 语句的目标表。
    --:尽管查询可能返回多个结果集,但是 OPENQUERY 只返回第一个。
  --:5.3、卸载连接服务器 
--6、知识点:用完全局临时表不要忘记删除,否则它们会在Sql Server系统临时数据库tempdb中越积越多占用资源
set @Sql = ' drop table '+@tempTable_ACE+';'
exec(@Sql) --删除全局临时表--drop table ##tempTable_ACE951118


2、获取Excel文件数据库的数据结构

    2.1、Excel文件数据库表数据集的描述字段:


        TABLE_CATALOG,TABLE_SCHEMA,TABLE_NAME,TABLE_TYPE,TABLE_GUID,DESCRIPTION,TABLE_PROPID,DATE_CREATED,DATE_MODIFIED

    2.2、Excel文件数据库中都有哪些Sheet表:


        ,,库存结余数量及资金占用统计$,TABLE,,,,2020-11-29 23:14:06,2020-11-29 23:14:06

    2.3、Excel文件数据库中Columns字段数据集的描述字段(字段数据集的字段定义):


        TABLE_CATALOG,TABLE_SCHEMA,TABLE_NAME,
        COLUMN_NAME,COLUMN_GUID,
        COLUMN_PROPID,ORDINAL_POSITION,
        COLUMN_HASDEFAULT,COLUMN_DEFAULT,
        COLUMN_FLAGS,
        IS_NULLABLE,
        DATA_TYPE,TYPE_GUID,
        CHARACTER_MAXIMUM_LENGTH,CHARACTER_OCTET_LENGTH,
        NUMERIC_PRECISION,NUMERIC_SCALE,
        DATETIME_PRECISION,
        CHARACTER_SET_CATALOG,CHARACTER_SET_SCHEMA,CHARACTER_SET_NAME,
        COLLATION_CATALOG,COLLATION_SCHEMA,COLLATION_NAME,
        DOMAIN_CATALOG,DOMAIN_SCHEMA,DOMAIN_NAME,
        DESCRIPTION

    2.4、Excel文件数据库中某张Sheet表的Columns字段定义的描述:


        ,,库存结余数量及资金占用统计$,公司编码,,,1,0,,106,-1,130,,255,510,,,,,,,,,,,,,
        ,,库存结余数量及资金占用统计$,行种子,,,2,0,,106,-1,130,,255,510,,,,,,,,,,,,,
        ,,库存结余数量及资金占用统计$,产品编码,,,3,0,,106,-1,130,,255,510,,,,,,,,,,,,,
        ,,库存结余数量及资金占用统计$,销量下限,,,4,0,,106,-1,130,,255,510,,,,,,,,,,,,,
        ,,库存结余数量及资金占用统计$,销量上限,,,5,0,,106,-1,130,,255,510,,,,,,,,,,,,,
        ,,库存结余数量及资金占用统计$,现场折扣,,,6,0,,106,-1,130,,255,510,,,,,,,,,,,,,
        ,,库存结余数量及资金占用统计$,其它扣率,,,7,0,,106,-1,130,,255,510,,,,,,,,,,,,,
        ,,库存结余数量及资金占用统计$,备注,,,8,0,,106,-1,130,,255,510,,,,,,,,,,,,,

    2.5、给Excel文件的某个Sheet新增1个字段

  最简单和直接的方法:

    就是在该Sheet中新增1个列标题栏即可。这样通过OLE DB从Sql Server数据库导出数据到Excel文件时,才能识别,类似这样的错误提示才不会出现:OLE DB 提供程序 'Microsoft.ACE.OLEDB.12.0' 提供的元数据不一致。

3、通过SQL语句及其数据库架构对象直接实现Excel与数据库的导入导出-BCP命令(dos命令)

   BCP是SQL Server中负责导入导出数据的一个命令行工具,它是基于DB-Library的,并且能以并行的方式高效地导入导出大容量的数据

   BCP除了可以在Cmd控制台执行外,还可以通过调用SQL Server的一个系统存储过程xp_cmdshell以SQL语句的方式运行BCP。如:EXEC master..xp_cmdshell 'BCP NTS.dbo.T_User out c:/User.txt -c -U"sa" -P"password"'

   得出以下若干条件总结:

    3.1、创建本地Excel链接服务器及其登录,并将其数据引入Sql Server

        将下面的代码,作为DML语言在delphi客户端:

        3.1.1、供数据集类.ExecSQL

        3.1.2、或存储过程类.Execute

        3.1.3、或供数据库连接.Execute(SqlStr)

-- 创建本地Excel链接服务器及其登录,并将其数据引入Sql Server
declare @SRV_NAME sysname
create table #temp_sp_linkedservers( 
  SRV_NAME sysname,SRV_PROVIDERNAME nvarchar(128),SRV_PRODUCT nvarchar(128),
  SRV_DATASOURCE nvarchar(1000),SRV_PROVIDERSTRING nvarchar(1000) null, 
  SRV_LOCATION nvarchar(1000) null,SRV_CAT sysname null 
)
insert into #temp_sp_linkedservers( 
  SRV_NAME ,SRV_PROVIDERNAME ,SRV_PRODUCT ,
  SRV_DATASOURCE , 
  SRV_PROVIDERSTRING ,SRV_LOCATION ,SRV_CAT
  ) 
  exec sp_linkedservers

if not exists(  select SRV_NAME from #temp_sp_linkedservers where  SRV_NAME='Excel_my' )
--if @@rowcount = 0 
begin 
  EXEC sp_addlinkedserver 'Excel_my',
   'Jet40',
   'Microsoft.Jet.OLEDB.4.0',
   'D:\开发包\饲料分批次进销存-存货计价和条码\ImportAndExport_xls\A1.xls',
   null,
   'Excel 5.0'
end
GO
drop table #temp_sp_linkedservers
go
EXEC sp_addlinkedsrvlogin 'Excel_my', 'false', NULL, NULL, NULL  
  --改变设置连接服务器的安全设置的映射=不需要设置安全上下文:与查询分析器中UI设置效果应当是一致的:否则会提示类似有【验证失败】字样
go
SELECT *
FROM Excel_my...A1$  --FROM Excel_my...[A1$]--均可识别
GO
EXEC sp_droplinkedsrvlogin  'Excel_my' ,null
go

4、在Sql Server查询分析器中(相当于:或你写的App调用Sql Server存储过程)通过master..xp_cmdshell扩展存储过调用执行Bcp

        条件:Sql Server服务的启动账户必须提权调用操作系统资源管理器的写入权限

        换个思路,通过本身具备权限的Windows的Dos或Shell工具,用它们去掉Sql:

5、dos下如何调用执行数据库的sql脚本

    5.1、SqlCmd.exe

        SqlCmd -S "172.17.0.12" -U "sa数据库的SysAdmin组的成员账号" -P "sa的密码" -d "CarveoutSiliaoShouyaoERP" -i "E:\牵引软件\bcpStr.sql"

sqlcmd -S "172.17.0.12" -U "sa" -P "kingdee" -d "CarveoutSiliaoShouyaoERP" -i "E:\牵引软件\bcpStr.sql"
报错:
消息 15121,级别 16,状态 21,服务器 172_17_0_12,过程 xp_cmdshell,第 1 行
在执行 xp_cmdshell 的过程中出错。调用 'CreateProcess' 失败,错误代码: '5'。

sqlcmd -S "10_105_210_32" -U "sa" -P "Admin81919048" -d "TW" -i "D:\全局临时表产生excel所需的表头后再bcp.sql"


C:\>sqlcmd /?
用法: Sqlcmd            [-U 登录 ID]          [-P 密码]
  [-S 服务器]            [-H 主机名]          [-E 可信连接]
  [-d 使用数据库名称] [-l 登录超时值]     [-t 查询超时值]
  [-h 标题]           [-s 列分隔符]      [-w 屏幕宽度]
  [-a 数据包大小]        [-e 回显输入]        [-I 允许带引号的标识符]
  [-c 命令结束]            [-L[c] 列出服务器[清除输出]]
  [-q "命令行查询"]   [-Q "命令行查询" 并退出]
  [-m 错误级别]        [-V 严重级别]     [-W 删除尾随空格]
  [-u unicode 输出]    [-r[0|1] 发送到 stderr 的消息]
  [-i 输入文件]         [-o 输出文件]        [-z 新密码]
  [-f <代码页> | i:<代码页>[,o:<代码页>]] [-Z 新建密码并退出]
  [-k[1|2] 删除[替换]控制字符]
  [-y 可变长度类型显示宽度]
  [-Y 固定长度类型显示宽度]
  [-p[1] 打印统计信息[冒号格式]]
  [-R 使用客户端区域设置]
  [-b 出错时中止批处理]
  [-v 变量 = "值"...]  [-A 专用管理连接]
  [-X[1] 禁用命令、启动脚本、环境变量[并退出]]
  [-x 禁用变量情况]
  [-? 显示语法摘要]





    在使用命令xp_cmdshell的时候需要设置权限

        sql server 2005 sa登录后,运行【1.4、附带说一下OLE DB调用数据库存储过程、Select语句、系统表或视图对象,第2组代码】时,提示:

        在执行 xp_cmdshell 的过程中出错。调用 'CreateProcess' 失败,错误代码: '5'。

        https://zhidao.baidu.com/question/536452084.html?qbl=relate_question_0&word=sql%20server%202005%20sa%B5%C7%C2%BC%BA%F3%D4%DA%D6%B4%D0%D0%20xp_cmdshell%20%B5%C4%B9%FD%B3%CC%D6%D0%B3%F6%B4%ED.%B5%F7%D3%C3%20%27CreateProcess

        https://blog.csdn.net/weixin_34259559/article/details/86183217?utm_medium=distribute.pc_relevant_bbs_down.none-task-blog-baidujs-1.nonecase&depth_1-utm_source=distribute.pc_relevant_bbs_down.none-task-blog-baidujs-1.nonecase

    SQL2005 BCP使用心得

        https://blog.csdn.net/ninetowns2008/article/details/8924387?utm_medium=distribute.pc_relevant.none-task-blog-title-6&spm=1001.2101.3001.4242

    SQL Server 2012 xp_cmdshell不能执行的解决办法(说是杀毒软件和防火墙惹的祸)

        https://lselect.blog.csdn.net/article/details/80982872?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control

        通过SqlCmd调用执行包含master..xp_cmdshell扩展存储过调用执行Bcp的.sql文件:

        若遇以下提示:也是因为Sql Server服务的启动账户无权调用操作系统资源管理器的写入权限引起的:

        报错:
        SQLState = 08001, NativeError = 53
        Error = [Microsoft][SQL Native Client]命名管道提供程序: 无法打开与 SQL Server 的
        连接 [53].
        SQLState = 08001, NativeError = 53
        Error = [Microsoft][SQL Native Client]建立到服务器的连接时发生错误。连接到 SQL S
        erver 2005 时,默认设置 SQL Server 不允许远程连接这个事实可能会导致失败。
        SQLState = S1T00, NativeError = 0
        Error = [Microsoft][SQL Native Client]登录超时已过期

        换个思维,不就好了吗?!如下:

    5.2、打开Windows PowerShell程序或运行Cmd.exe进入命令行工具执行Bcp.exe


        PS C:\Users\Administrator> bcp CarveoutSiliaoShouyaoERP.dbo.View_Bcp_Test out E:\牵引软件\饲料兽药演示\武当动物药业\est.xls -m -c -t -S"172_17_100_12" -U"sa" -P"kingdee"

        运行结果:

        开始复制...
        1000 行成功地成批复制到主文件。总计接收: 1000

        已复制 1045 行。
        网络数据包大小(字节): 4096
        总时钟时间(毫秒)     : 1      平均值: (每秒 1045000.00 行。超快!!!!!!!哪里是传统的ComObj方法能够比拟的)


        PS C:\Users\Administrator> bcp CarveoutSiliaoShouyaoERP.dbo.SDd02020 out c:\currency.txt -S"172_17_100_12" -U"sa" -P"kingdee"-c

        运行结果:

        开始复制...
        1000 行成功地成批复制到主文件。总计接收: 1000

        已复制 1044 行。
        网络数据包大小(字节): 4096
        总时钟时间(毫秒)     : 15     平均值: (每秒 69600.00 行。超快!!!!!!!哪里是传统的ComObj方法能够比拟的)

6、delphi中如何实现用Cmd.exe命令行工具调用执行Bcp指令:

    6.1、delphi中如何用Cmd.exe命令行工具调用执行Bcp指令调用数据库系统表或视图对象

          参考:1.4、附带说一下OLE DB调用数据库存储过程、query、系统表或视图对象



procedure ShellMe(const AFormHandle:TForm; APath,AShellexeName,AShellCommand:string);
var LShellCommand :string;
begin
  ShellExecute(AFormHandle.Handle,'open',PChar(APath+'\'+AShellexeName),PChar(AShellCommand),nil,SW_SHOWNORMAL);
    //:cmd.exe  //:Null:HWND:可强制转化HWND(Null)
  //ShellExecute(AFormHandle.Handle,'open',PChar(APath+'\'+AShellexeName),PChar(AShellCommand),nil,SW_SHOWNORMAL);
  //ShellExecute(Null, 'open', PChar(APath+'\'+AShellexeName), PChar(AShellCommand), nil, SW_HIDE);
    //:用SW_HIDE使cmd窗口静默执行:SW_SHOWNORMAL正常打开状态


    //:AShellCommand不支持直接使用..\,要使用相对路径可以用tempdir来实现
      //:假设text.txt在test.exe的上一层目录中:
      //ShellExecute(NULL, 'open', PChar('cmd.exe'), "/c D:\\test\\test.exe "tempdir\\..\\..\\text.txt >D:\\testResult.txt", NULL, SW_HIDE);
    //:用变量传命令的时候,要在命令字符AShellCommand开始加上"/c",不然命令不会执行。

    //:如果命令中的路径包含空格比如te st,要把路径去掉头尾用双引号包含起来:
      //APath = " "D:\\\"te st\\\"test.exe;(用“\”进行字符转义)
        //:比如目标:"C:\Program Files (x86)\Microsoft SQL Server\100\SDK\Lib\x86\sqlncli10.lib"
        //:其起止位置:"C:\Program Files (x86)\Microsoft SQL Server\100\SDK\Lib\x86"
          //:这样你才能在Windows下打开和这个文件夹及其文件:Windows常识
    //:shellexecute(handle,nil,'cmd.exe',pchar('/c ping 192.168.1.1  & pause'),nil,sw_normal);
      //:注意,cmd的命令需要是用/c表示带参数运行,然后后面跟上具体命令,这样就可以执行ping命令了。
      //:上面的命令的确可以成功运行,但是有个缺点,那就是cmd在执行完毕后就会自动关闭窗口,如果有时候命令执行的过快,我们根本看不清结果,那么,有什么办法可以让cmd在执行完毕后不关闭窗口呢?答案是肯定的,我们需要一个额外的命令pause
    //:WinExec(PChar( 'cmd.exe /c  ping 192.168.1.1 & pause'),SW_SHOWNORMAL);
      //:我们注意上面:使用一个&符号和pause,pause是批处理中的暂停命令,
        //:批处理中&符号则表示可以执行多个命令,
        //:上面的例子就表明,在执行完ping命令后再执行pause命令,
        //:这样,窗口就不会关闭了。shellexecute同样支持该方法。
end;

var LFileFullPath: string;
    LShellCommand :string;
begin

    //LShellCommand:=' /c bcp TestTemp.dbo.SDd02020 out D:\SDd02020.txt -S"(local)" -U"sa" -P"admin81919048" -c';
      //: 表
      //: /k命令前导符(保留显示命令行控制台),若用: /c(不保留显示控制台)
      //:注意Bcp命令中Sql Server服务器名必须用命名管道名或IP不能用其内部标识符.比如这样是错误的表达服务器-S".",正确的是-S"YLMF-1710171449"或-S"127.0.0.1":
    //ShellMe(Self,'c:\windows\system32','cmd.exe',LShellCommand);
    LShellCommand:=' /c bcp TestTemp.dbo.View_Bcp_Test out D:\SDd02020.xls -S"192.168.3.101" -U"sa" -P"admin81919048" -m -c -t';
      //: 视图
      //: /k命令前导符(保留显示命令行控制台),若用: /c(不保留显示控制台)
      //:注意Bcp命令中Sql Server服务器名必须用命名管道名或IP不能用其内部标识符.比如这样是错误的表达服务器-S".",正确的是-S"YLMF-1710171449"或-S"127.0.0.1":
    ShellMe(Self,'c:\windows\system32','cmd.exe',LShellCommand);

    LShellCommand:=' /k bcp TestTemp.dbo.View_Bcp_Test out D:\sp_Test_Bcp.xls -S"192.168.3.101" -U"sa" -P"admin81919048" -m -c -t';

      //:存储过程和Select语句:Bcp不能直接显式的支持存储过程和Select语句,因为它们并非数据库的实体对象,只能通过表对象dbTable或视图query对象来间接的实现对存储过程的调用存储过程产生的结果集(因为可以是多个数据集/行集)
    ShellMe(Self,'c:\windows\system32','cmd.exe',LShellCommand);

end;

          如上所述,存储过程和Select语句:Bcp不能直接显式的支持存储过程和Select语句,因为它们并非数据库的实体对象,只能通过表对象dbTable或视图query对象来间接的实现对存储过程的调用存储过程产生的结果集(因为可以是多个数据集/行集)。

    6.2、delphi中如何用Cmd.exe命令行工具调用执行Bcp指令调用数据库query视图对象

           bcp TestTemp.dbo.View_Bcp_Test out D:\SDd02020.xls -S"192.168.3.101" -U"sa" -P"admin81919048" -m -c -t

           bcp TestTemp.dbo.View_Bcp_Test out D:\SDd02020.xls -S"192.168.3.101" -U"sa" -P"admin81919048" -m -c 

           那么放到Delphi中怎么调用?如上代码框中的代码,不再赘述。

    6.3、delphi中如何用Cmd.exe命令行工具调用执行Bcp指令调用数据库存储过程

           Bcp这样执行存储过程sp_Test_Bcp 是错误的,控制台会输出操作提示“[存储过程]对象名无效”

           bcp TestTemp.dbo.sp_Test_Bcp out D:\SDd02020.xls -S"192.168.3.101" -U"sa" -P"admin81919048" -m -c         

           正确的步骤:

        6.3.1、书写好你的“存储过程”并让其在Sql Server中实际存在。

           假如你已经书写并创建了存储过程:sp_Test_Bcp

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_Test_Bcp]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[sp_Test_Bcp]
GO

SET QUOTED_IDENTIFIER ON 
GO
SET ANSI_NULLS ON 
GO

CREATE  PROCEDURE sp_Test_Bcp  --为了简要说明,我们创建了1个没有任何输入和输出参数的存储过程,只让他查询后返回1个结果集
AS
BEGIN TRANSACTION MyTran_sp_Test_Bcp

  select * from sdf00504 where com_id='001'  --在存储过程中我们查询了“客户资料表”

IF @@ERROR <> 0 
begin
  ROLLBACK TRANSACTION MyTran_sp_Test_Bcp
end else
begin
  COMMIT TRANSACTION MyTran_sp_Test_Bcp
end

GO
SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO

        6.3.2、创建1个Sql Server能识别的数据库实体对象Query视图,在视图中使用OpenRowSet实现视图对存储过程的查询结果集。

            6.3.2.1、你可以用Sql Server的视图设计器去产生

            6.3.2.2、你也可以用代码去动态产生

                比如你可以在delphi等语言开发工具中用代码 数据集.ExecSQL的方法调用MS OLE DB数据引擎动态创建这样的一个视图,而不必用Sql Server的视图设计器去产生,因为你很可能需要为存储过程传入参数并可能因需若干个存储过程输出参数:

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[View_TestOpenOpenRowSet_Procedure]') and OBJECTPROPERTY(id, N'IsView') = 1)
drop view [dbo].[View_TestOpenOpenRowSet_Procedure]
GO

SET QUOTED_IDENTIFIER ON 
GO
SET ANSI_NULLS ON 
GO

CREATE VIEW dbo.View_TestOpenOpenRowSet_Procedure
AS
select * from OpenRowSet('SQLOLEDB','127.0.0.1';'sa';'admin81919048','TestTemp.dbo.sp_Test_Bcp') as derivedtbl_1  --在视图中,我们使用了SQLOLEDB的OpenRowSet返回了存储过程的执行结果

GO
SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO

            6.3.2.3、你可能会跨数据库甚至跨域(跨服务器)设计输入:

            6.3.2.4、特别需要注意的事项

                 那么此时,你必须开启Sql Server的MSDTC服务。它对应的操作系统的服务是DTC (Distributed Transaction Coordinator)分布式事务协调器

                 否则,在低版本的Sql Server 2000中,你用Sql Server的视图设计器去产生内置OpenRowset或OpenDataSource代码的视图中时,会产生如上图的报错,当然,你也可以用代码去动态产生。

                 在高版本的Sql Server 2005/2008/2012等中,你需要用代码启动Ad Hoc Distributed Queries(即席分布式查询),它能否被启动,取决于你的Sql Server安装时所用的操作系统账号及启动Sql Server服务的账户权限:

declare @LVersion sysname
--启动Ad Hoc Distributed Queries(即席分布式查询):
exec sp_configure 'show advanced options',1
set @LVersion=@@Version  --select @LVersion--select substring(@LVersion,22,4) --select ltrim(rtrim(substring(@LVersion,22,5)))
if ltrim(rtrim(substring(@LVersion,22,5))) >'2000'
begin
  exec sp_configure 'Ad Hoc Distributed Queries',1  --:这一句:Sql2000无效也无需,只需启动MSDTC
  RECONFIGURE WITH OVERRIDE
  --重置对Sql Server服务器的配置改变,让其生效
end else
begin
  RECONFIGURE  --即席更新Sql Server服务器的配置改变,让其生效
end
select * from 
  OpenRowSet('SQLOLEDB','127.0.0.1';'sa';'admin81919048','TestTemp.dbo.sp_Test_Bcp') 

        6.3.3、Bcp调用上一步你创建的数据库query视图对象

            6.3.3.1、在Cmd命令行工具下先用Bcp指令验证一下我们的Excel文件中存储过程的输出结果

                 bcp TestTemp.dbo.View_TestOpenOpenRowSet_Procedure out D:\View_TestOpenOpenRowSet_Procedure.xls -S"127.0.0.1" -U"sa" -P"admin81919048" -m -c

                 超级快的输出速度,40列字段、1056行记录,哪里是传统的ComObject技术能够比拟的! 

            6.3.3.2、然后我们把Bcp指令放到Delphi来调用且实现其输出结果

                 具体请参考:6.1、delphi中如何用Cmd.exe命令行工具调用执行Bcp指令调用数据库系统表或视图对象


    LShellCommand:=' /k bcp TestTemp.dbo.View_TestOpenOpenRowSet_Procedure out D:\View_TestOpenOpenRowSet_Procedure.xls -S"127.0.0.1" -U"sa" -P"admin81919048" -m -c';
      //:存储过程和Select语句:Bcp不能直接显式的支持存储过程和Select语句,
        //:因为它们并非数据库的实体对象,只能通过表对象dbTable或视图query对象来间接的实现对存储过程的调用存储过程产生的结果集(因为可以是多个数据集/行集)
    ShellMe(Self,'c:\windows\system32','cmd.exe',LShellCommand);

 

本博客相关:

        1、《delphi使用MS Sql Server数据库的分布式查询

        2、《delphi导入导出Excel与Sql Server

        3、RAD Studio 10.4.1的TEdgeBrowser与javascript交互-基于Chromium的Edge浏览器控件用法之二

喜欢的话,就在下面点个赞、收藏就好了,方便看下次的分享:

猜你喜欢

转载自blog.csdn.net/pulledup/article/details/110673518