(4.17)SQL server 2008一些常见配置之三(读取Excel的数据)

SQL server 2008一些常见配置之三(读取Excel的数据)

一、实操环境

(一)SQL server:Microsoft SQL Server 2008 R2 (RTM) 

(二)操作系统:Windows Server 2008 R2 Standard(64位)

(三)电脑硬件:

1.CPU:Interl(R) Xeon(R)E5-2630 v2 @ 2.60GHz 2.60GHz

2.内存:16.0GB

(四)AccessDatabaseEngine:

AccessDatabaseEngine_64.exe(64位)

二、读取Excel表格单个工作表的数据
扫描二维码关注公众号,回复: 3687800 查看本文章

      Microsoft Excel是微软公司的优秀的电子表格软件,是目前最流行的个人计算机数据处理软件,应用相当官方,同样是微软公司产品的SQL server数据库对Execl的支持非常好。

(一)配置访问Execl的接口

1.基于OLE DB访问的相关基础知识

2.配置访问接口

      读取Excel有两种接口参数可以选择:

        Microsoft.Jet.OLEDB.4.0

        Microsoft.ACE.OLEDB.12.0

      Microsoft.Jet.OLEDB.4.0接口对某些版本的Excel支持不是很好,建议使用Microsoft.ACE.OLEDB.12.0接口,下面演示一下配置过程。

      全新安装的SQL server数据库是没有Microsoft.ACE.OLEDB.12.0接口的,需要安装引擎:

      AccessDatabaseEngine

 

注:Microsoft Office Access database Engine是微软的access数据库访问引擎,可通过官网下载相关软件:

https://www.microsoft.com/en-us/download/details.aspx?id=13255

      如果电脑操作系统是64位但安装了32位office会出现以下提示:

      此时,可以通过命令行加参数(passive)来运行AccessDatabaseEngine。

开始->cmd->

d:\ldw\AccessDatabaseEngine_x64.exe /passive

      安装完AccessDatabaseEngine之后,SQL server【访问接口】就会出现Microsoft.ACE.OLEDB.12.0这个接口了。

3.修改参数配置

      通过以下脚本修改相关参数配置,允许使用该接口。

--修改高级参数
sp_configure 'show advanced options',1
go

--允许即席分布式查询
sp_configure 'Ad Hoc Distributed Queries',1
go

--如果配置的值不在合理范围(在最小值最大值范围内),那么可以强制覆盖
reconfigure with override  
go

(二)访问某个Execl指定工作表

      访问某个Execl指定工作表需要知道具体文件路径和工作表表名,以下表为例。

SQL server访问Excel脚本:

select *
  from openrowset('microsoft.ace.oledb.12.0',
                  'Excel 12.0;database=d:\www\test.xlsx',
                  'select * from [sheet1$]');

其中:

1.所调用的访问接口:microsoft.ace.oledb.12.0
2.Excel文件具体路径(完整的路径):d:\www\test.xlsx
3.工作表表名(不区分大小写):sheet1
4.如果Execl表第一行的单元格有具体值,则将以该值做为该字段名,如果没有值或为合并单元格,将默认以F1、F2、F3。。。为字段名。这个Oracle就做不到,这也是SQL server比较适合新手的主要原因之一。

注意:

1.这里需要特别注意的一点:被访问的Excel表格中,同一列单元格必须是同一格式(要么全部是数值型、要么全部是文本类型),同一列格式不同极有可能把实际有数据的单元格错读成空值(Null),从而影响了数据的正常入库。(初学者要特别留意)

2.最常见的报错:

(1)Excel文件在打开状态(报错代码:7303)

链接服务器"(null)"的 OLE DB 访问接口 "microsoft.ace.oledb.12.0" 返回了消息 "The Microsoft Access database engine cannot open or write to the file ''. It is already opened exclusively by another user, or you need permission to view and write its data."。
消息 7303,级别 16,状态 1,第 1 行
无法初始化链接服务器 "(null)" 的 OLE DB 访问接口 "microsoft.ace.oledb.12.0" 的数据源对象。

(2)文件不存在(报错代码:7350)

链接服务器"(null)"的 OLE DB 访问接口 "microsoft.ace.oledb.12.0" 返回了消息 "The Microsoft Access database engine could not find the object 'sheet1$'. Make sure the object exists and that you spell its name and the path name correctly. If 'sheet1$' is not a local object, check your network connection or contact the server administrator."。

消息 7350,级别 16,状态 2,第 1 行

无法从链接服务器 "(null)" 的 OLE DB 访问接口"microsoft.ace.oledb.12.0"获取列信息。

(3)指定的工作表不存在(报错代码:7357)

消息 7357,级别 16,状态 2,第 1 行
无法处理对象 "select * from [sheet11$]"。链接服务器 "(null)" 的 OLE DB 访问接口 "microsoft.ace.oledb.12.0" 指示该对象没有列,或当前用户没有访问该对象的权限。

二、自动读取单个Excel里所有工作表的数据

(一)Execl文件基本信息

1.文件路径:d:\ldw\ftp_serv\日常\数据导出.xlsx
2.“信息-导出1”:第一个工作表名,有11行数据
3.“信息-导出2”:第二个工作表名,有22行数据

--代码
select *
  from openrowset('microsoft.ace.oledb.12.0',
                  'Excel 12.0;database=d:\ldw\ftp_serv\日常\数据导出.xlsx',
                  'select * from [信息-导出1$]')

 在实际应用中,导出的execl文件会根据数量大小分解成N个工作表,比如“信息-导出1” “信息-导出2”…..,如上表11行在工作表“信息-导出1”,22行数据在“信息-导出2”。如果单纯通过上述办法去读取,那需要每次都打开execl文件人眼去查看具体工作表名,然后修改脚本,费时费力。为解决这个问题,今天我们一起尝试一种自动获取工作表名,并按工作表名逐一导入到数据库表的办法。

(二)第一步:读取execl工作表名

--代码
--文件名(含路径)
declare @i_excelFileUrl nvarchar(512);
set @i_excelFileUrl = 'd:\ldw\ftp_serv\日常\数据导出.xlsx'
--读取开关:--默认为0,全部读所有工作表。如为N,则仅读取对应第N个工作表的数据。
declare @i_flag int;
if @i_flag is null set @i_flag = 0;
-- 设置变量
declare @linkedServerName sysname = 'TempExcelSpreadsheet';
-- 删除链接服务(如果它已经存在)
if exists(select null from sys.servers where name = @linkedServerName) begin  
  exec sp_dropserver @server = @linkedServerName, @droplogins = 'droplogins'  
end
-- 添加服务对象
-- ACE 12.0 可以很好地读取.xls 和.xlsx,你也可以用Jet ,但只能访问*.xls文件
exec sp_addlinkedserver
  @server = @linkedServerName,
  @srvproduct = 'ACE 12.0',
  @provider = 'Microsoft.ACE.OLEDB.12.0',
  @datasrc = @i_excelFileUrl,
  @provstr = 'Excel 12.0;HDR=Yes'
-- 获取当前用户
declare @suser_sname nvarchar(256) = suser_sname() 
-- 添加当前用户作为登陆这个链接服务
exec sp_addlinkedsrvlogin
  @rmtsrvname = @linkedServerName,
  @useself = 'false',
  @locallogin = @suser_sname,
  @rmtuser = null,
  @rmtpassword = null
--查看是否获取到工作表名
exec sp_tables_ex @linkedServerName;

成功获取到execl表的工作表名。

 

注意:Excel中存在隐藏的命名区域,某个版本的Excel会自动产生一个后缀为_FilterDatabase,它代表了自动筛选的区域,这个区域是隐藏的,所以经常被忽略,表面看不出来,但在通过Microsoft.ACE.OLEDB.12.0去读取的时候也会被读进来,这样容易造成数据重复录入,需要做躲坑处理,在脚本中不统计隐藏的工作表。

  --躲坑:不统计无效工作表
  delete [dbo].[#p_ldw_read_execl_1] where [TABLE_NAME] like '%_FilterDatabase';

(三)第二步:采用游标方法逐一读取每个工作表数据

--代码
if object_id(N'.[tempdb].[dbo].[##p_ldw_read_execl_out]',N'U') is not null drop table [dbo].[##p_ldw_read_execl_out];

if object_id(N'.[tempdb].[dbo].[#p_ldw_read_execl_1]',N'U') is not null drop table [dbo].[#p_ldw_read_execl_1];
-- 返回 sheet 和 各个 sheet中的列
create table [dbo].[#p_ldw_read_execl_1]([ID] int identity(1,1),[TABLE_CAT] varchar(512),[TABLE_SCHEM] varchar(512),[TABLE_NAME] varchar(512),[TABLE_TYPE] varchar(512),[REMARKS] varchar(512))
insert into [dbo].[#p_ldw_read_execl_1]([TABLE_CAT],[TABLE_SCHEM],[TABLE_NAME],[TABLE_TYPE],[REMARKS]) exec sp_tables_ex @linkedServerName;

     if   0 in (cursor_status('global','#p_ldw_read_execl_cur'),cursor_status('local','#p_ldw_read_execl_cur')) deallocate #p_ldw_read_execl_cur
else if  -1 in (cursor_status('global','#p_ldw_read_execl_cur'),cursor_status('local','#p_ldw_read_execl_cur')) deallocate #p_ldw_read_execl_cur 
else if   1 in (cursor_status('global','#p_ldw_read_execl_cur'),cursor_status('local','#p_ldw_read_execl_cur')) close #p_ldw_read_execl_cur;
declare @v_sql nvarchar(2000);
declare @i int;set @i = 1;
declare @v_execl_table_name nvarchar(512);
declare #p_ldw_read_execl_cur cursor for 
  select [TABLE_NAME] from [dbo].[#p_ldw_read_execl_1] where @i_flag in ([ID],0);
open #p_ldw_read_execl_cur;
fetch next from #p_ldw_read_execl_cur into @v_execl_table_name;
while @@fetch_status = 0
begin
  set @v_execl_table_name = replace(@v_execl_table_name,'''','');
  if @i = 1 
    set @v_sql = 'select * into [dbo].[##p_ldw_read_execl_out] from openrowset(''microsoft.ace.oledb.12.0'',''Excel 12.0;database='+@i_excelFileUrl+''',''select * from ['+@v_execl_table_name+']'')' ;
  else
    set @v_sql = 'insert into [dbo].[##p_ldw_read_execl_out] select * from openrowset(''microsoft.ace.oledb.12.0'',''Excel 12.0;database='+@i_excelFileUrl+''',''select * from ['+@v_execl_table_name+']'')' ;
  exec (@v_sql);
  set @i = @i + 1;
  fetch next from #p_ldw_read_execl_cur into @v_execl_table_name;
end
     if   0 in (cursor_status('global','#p_ldw_read_execl_cur'),cursor_status('local','#p_ldw_read_execl_cur')) deallocate #p_ldw_read_execl_cur
else if  -1 in (cursor_status('global','#p_ldw_read_execl_cur'),cursor_status('local','#p_ldw_read_execl_cur')) deallocate #p_ldw_read_execl_cur 
else if   1 in (cursor_status('global','#p_ldw_read_execl_cur'),cursor_status('local','#p_ldw_read_execl_cur')) close #p_ldw_read_execl_cur;

if object_id(N'.[tempdb].[dbo].[#p_ldw_read_execl_1]',N'U') is not null drop table [dbo].[#p_ldw_read_execl_1];

      最终将数据导入到临时表[dbo].[##p_ldw_read_execl_out]里面。结果验证:成功将2个工作表的数据读入到指定的临时表

[dbo].[##p_ldw_read_execl_out]

select * from [dbo].[##p_ldw_read_execl_out]

注意:必须要高度注意的是,这些每个工作表的格式(列)格式、列数必须是一致的。

(四)第三步:将整个过程封装成存储过程。

CREATE PROCEDURE [dbo].[p_ldw_read_execl2](
@i_excelFileUrl nvarchar(1000),
@i_flag         int
)

AS 
BEGIN 

  SET NOCOUNT ON;

  if @i_flag is null set @i_flag = 0;
  if object_id(N'.[tempdb].[dbo].[#fileexists]',N'U') is not null drop table [dbo].[#fileexists];
  create table [dbo].[#fileexists]([文件] int,[文件夹] int,[父目录] int)
  insert into [dbo].[#fileexists] EXEC [MASTER]..XP_FILEEXIST @i_excelFileUrl

  --文件不存在则直接退出
  if not exists (select 1 from [dbo].[#fileexists] where [文件] = 1)
  begin
    print '错误提示:文件【 ' + @i_excelFileUrl + ' 】不存在。'
    return;
  end;

  -- 设置变量
  declare @linkedServerName sysname = 'TempExcelSpreadsheet';
  -- /SET

  -- 删除链接服务(如果它已经存在)
  if exists(select null from sys.servers where name = @linkedServerName) begin  
    exec sp_dropserver @server = @linkedServerName, @droplogins = 'droplogins'  
  end  

  -- 添加服务对象
  -- ACE 12.0 可以很好地读取*.xls 和*.xlsx,你也可以用Jet ,但只能访问*.xls文件
  exec sp_addlinkedserver
    @server = @linkedServerName,
    @srvproduct = 'ACE 12.0',
    @provider = 'Microsoft.ACE.OLEDB.12.0',
    @datasrc = @i_excelFileUrl,
    @provstr = 'Excel 12.0;HDR=Yes'  

  -- 获取当前用户
  declare @suser_sname nvarchar(256) = suser_sname() 
  -- 添加当前用户作为登陆这个链接服务
  exec sp_addlinkedsrvlogin
    @rmtsrvname = @linkedServerName,
    @useself = 'false',
    @locallogin = @suser_sname,
    @rmtuser = null,
    @rmtpassword = null  

  if object_id(N'.[tempdb].[dbo].[##p_ldw_read_execl_out]',N'U') is not null drop table [dbo].[##p_ldw_read_execl_out];

  if object_id(N'.[tempdb].[dbo].[#p_ldw_read_execl_1]',N'U') is not null drop table [dbo].[#p_ldw_read_execl_1];
  -- 返回 sheet 和 各个 sheet中的列
  create table [dbo].[#p_ldw_read_execl_1]([ID] int identity(1,1),[TABLE_CAT] varchar(512),[TABLE_SCHEM] varchar(512),[TABLE_NAME] varchar(512),[TABLE_TYPE] varchar(512),[REMARKS] varchar(512))
  insert into [dbo].[#p_ldw_read_execl_1]([TABLE_CAT],[TABLE_SCHEM],[TABLE_NAME],[TABLE_TYPE],[REMARKS]) exec sp_tables_ex @linkedServerName;

  --躲坑:不统计无效工作表
  delete [dbo].[#p_ldw_read_execl_1] where [TABLE_NAME] like '%_FilterDatabase'

  --如果没有对应的工作表,返回提示并退出
  declare @v_max_id int;
  select @v_max_id = max([ID]) from [dbo].[#p_ldw_read_execl_1];
  if @v_max_id is null set @v_max_id = 0;
  if not exists (select 1 from [dbo].[#p_ldw_read_execl_1] where @i_flag in ([ID],0))
  begin
    print '错误提示:【 ' + @i_excelFileUrl + ' 】有【 '+ convert(varchar(32),@v_max_id) +' 】个工作表,第【 '+ convert(varchar(32),@i_flag) +' 】个工作表不存在。'
    return;
  end;

       if   0 in (cursor_status('global','#p_ldw_read_execl_cur'),cursor_status('local','#p_ldw_read_execl_cur')) deallocate #p_ldw_read_execl_cur
  else if  -1 in (cursor_status('global','#p_ldw_read_execl_cur'),cursor_status('local','#p_ldw_read_execl_cur')) deallocate #p_ldw_read_execl_cur 
  else if   1 in (cursor_status('global','#p_ldw_read_execl_cur'),cursor_status('local','#p_ldw_read_execl_cur')) close #p_ldw_read_execl_cur;
  declare @v_sql nvarchar(2000);
  declare @i int;set @i = 1;
  declare @v_execl_table_name nvarchar(512);
  declare #p_ldw_read_execl_cur cursor for 
    select [TABLE_NAME] from [dbo].[#p_ldw_read_execl_1] where @i_flag in ([ID],0);
  open #p_ldw_read_execl_cur;
  fetch next from #p_ldw_read_execl_cur into @v_execl_table_name;
  while @@fetch_status = 0
  begin
    set @v_execl_table_name = replace(@v_execl_table_name,'''','');
    if @i = 1 
      set @v_sql = 'select * into [dbo].[##p_ldw_read_execl_out] from openrowset(''microsoft.ace.oledb.12.0'',''Excel 12.0;database='+@i_excelFileUrl+''',''select * from ['+@v_execl_table_name+']'')' ;
    else
      set @v_sql = 'insert into [dbo].[##p_ldw_read_execl_out] select * from openrowset(''microsoft.ace.oledb.12.0'',''Excel 12.0;database='+@i_excelFileUrl+''',''select * from ['+@v_execl_table_name+']'')' ;
    exec (@v_sql);
    set @i = @i + 1;
    fetch next from #p_ldw_read_execl_cur into @v_execl_table_name;
  end
       if   0 in (cursor_status('global','#p_ldw_read_execl_cur'),cursor_status('local','#p_ldw_read_execl_cur')) deallocate #p_ldw_read_execl_cur
  else if  -1 in (cursor_status('global','#p_ldw_read_execl_cur'),cursor_status('local','#p_ldw_read_execl_cur')) deallocate #p_ldw_read_execl_cur 
  else if   1 in (cursor_status('global','#p_ldw_read_execl_cur'),cursor_status('local','#p_ldw_read_execl_cur')) close #p_ldw_read_execl_cur;

  if object_id(N'.[tempdb].[dbo].[#p_ldw_read_execl_1]',N'U') is not null drop table [dbo].[#p_ldw_read_execl_1];

  --删除链接服务对象  
  if exists(select null from sys.servers where name = @linkedServerName) 
  begin  
    exec sp_dropserver @server = @linkedServerName, @droplogins = 'droplogins'  
  end

end;

注意:

1.输入参数 @i_flag:0为读入所有工作表的数据,非 0 值则读取第N个工作表的数据。
2.如果文件不存在,提示错误并退出。
3.如果工作表不存在,提示错误并退出。

(五)验证脚本

1.全量读取(@i_flag 参数为 0)

 --代码
 exec [dbo].[p_ldw_read_execl2] 'd:\ldw\ftp_serv\日常\数据导出.xlsx',0
 GO
 select * from [dbo].[##p_ldw_read_execl_out]

2.读取第 1 个工作表(@i_flag 参数为 1)

 --代码
 exec [dbo].[p_ldw_read_execl2] 'd:\ldw\ftp_serv\日常\数据导出.xlsx',1
 GO
 select * from [dbo].[##p_ldw_read_execl_out]

3.读取第 2 个工作表(@i_flag 参数为 2)

 --代码
 exec [dbo].[p_ldw_read_execl2] 'd:\ldw\ftp_serv\日常\数据导出.xlsx',2
 GO
 select * from [dbo].[##p_ldw_read_execl_out]

4.(演示错误提示)读取第 5 个工作表(@i_flag 参数为 5)

 --代码
 exec [dbo].[p_ldw_read_execl2] 'd:\ldw\ftp_serv\日常\数据导出.xlsx',5
 GO
 select * from [dbo].[##p_ldw_read_execl_out]

错误提示:【 d:\ldw\ftp_serv\日常\数据导出.xlsx 】有【 2 】个工作表,第【 5 】个工作表不存在。

      校验成功。

      将数据入库是数据分析的第一步,我们下一步接着学习导入CSV(TXT)数据方法和注意事项,其效率要比读取Excel表的效率要高很多,这也是很多数据迁移时常用手段之一,欲知后事如何请听下回分解。

猜你喜欢

转载自www.cnblogs.com/gered/p/9835307.html