首先说明使用的环境是:java和Sqlserver。

最近公司需要进行大数据量的导入操作。原来使用的是Apache POI,虽然可以实现功能,但是因为逻辑处理中需要进行许多校验,处理速度太慢,使用多线程之后也不尽如人意。在网上搜索之后,找到了OPENROWSET和OPENDATASOURCE,发现使用OPENROWSET,可以非常快速的把Excel导入到数据库中。之后的各种校验,我可以通过编写sql来实现。最终结果是6w条数据可以在10秒内完成。当然数据量增加之后,完成时间并不会明显增加。这需要编写的sql比较高效,是另一方面的问题了。

首先可能需要下载一个小的程序AccessDatabaseEngine_X64.exe。

之后需要开启配置

启用:

exec sp_configure 'show advanced options',1
reconfigure
exec sp_configure 'Ad Hoc Distributed Queries',1
reconfigure

关闭:

exec sp_configure 'Ad Hoc Distributed Queries',0
reconfigure
exec sp_configure 'show advanced options',0
reconfigure

之后可以通过OPENROWSET来查询Excel文件的内容。当然也可以改为SELECT INTO存到数据库中。

SELECT * FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0',
'Excel 12.0;HDR=YES;Database=E:\DataBack\Copy of SD Expired Contracts.xlsx', ['Copy of SD Expired Contracts$'])

这样使用的前提是你知道需要导入的Sheet的名称。而如果不知道的话就需要使用下面的方法,查询出所有的Sheet名称,再由用户选择导入哪一个。

EXEC sp_addlinkedserver 'ExcelSource', '',
'Microsoft.ACE.OLEDB.12.0',
'E:\DataBack\Copy of SD Expired Contracts.xlsx',
NULL,
'Excel 8.0'
EXEC sp_addlinkedsrvlogin 'ExcelSource', 'false'
GO
EXECUTE SP_TABLES_EX 'ExcelSource'

下面是我的Java代码:

这个方法用户获得Sheet Name List。

	public static List<String> getSheetNameList(String filePath,BaseDao baseDao){
String excelSource = "ExcelSource_"+StringUtils.getUUIDString();
String addSourceSql = "{CALL SP_ADDLINKEDSERVER(?,'','Microsoft.ACE.OLEDB.12.0',?,NULL,'Excel 8.0')}";
SQLQuery query = baseDao.getSQLQuery(addSourceSql);
query.setParameter(0, excelSource);
query.setParameter(1, filePath);
query.executeUpdate();
String loginSourceSqql = "{CALL SP_ADDLINKEDSRVLOGIN(?,'false')}";
query = baseDao.getSQLQuery(loginSourceSqql);
query.setParameter(0, excelSource);
query.executeUpdate(); String sheetNameSql = "{CALL SP_TABLES_EX(?)}";
query = baseDao.getSQLQuery(sheetNameSql);
query.setParameter(0, excelSource);
query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
List<Map<String, Object>> list = query.list();
List<String> sheetList = new ArrayList<String>();
for(int i=0;i<list.size();i++){
String sheetName = (String) list.get(i).get("TABLE_NAME");
if(sheetName.endsWith("_xlnm#_FilterDatabase")){ }else{
sheetList.add(sheetName);
}
}
return sheetList;
}

这个方法用户创建一个临时表,存储Excel文件内容。临时表的字段名是根据Excel表头来创建的。

	public static String uploadAndCreateTable(String filePath,String sheetName,BaseDao baseDao) {
String importTableName = "tbl_zz_"+StringUtils.getUUIDString();
String uploadFileSql = "SELECT IDENTITY(int, 1, 1) as %s,t.*,CAST(NEWID() AS VARCHAR(36)) AS %s into %s FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0','Excel 12.0;HDR=YES;Database=%s', [%s]) as t";
uploadFileSql = String.format(uploadFileSql,ImportVisitorUtil.importIndex,ImportVisitorUtil.importUUID,importTableName,filePath,sheetName);
SQLQuery query = baseDao.getSQLQuery(uploadFileSql);
query.executeUpdate();
replaceSpecialCharacter(baseDao, importTableName);
changeColumnCollation(baseDao, importTableName);
return importTableName;
}

代码编写过程中发现一个问题,如果Excel表头中含有":",在编写sql过程中会跟:name这种占位符冲突,我冒号替换成了空格。

	private static void replaceSpecialCharacter(BaseDao baseDao,String tableName){
String cha = ":";
String cha_ = "%:%";
String sql = "SELECT COLUMN_NAME columnName FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ? AND COLUMN_NAME like ? ";
SQLQuery query = baseDao.getSQLQuery(sql);
query.setParameter(0, tableName);
query.setParameter(1, cha_);
query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
List<Map<String,Object>> list = query.list();
for(Map<String,Object> map : list){
String columnName = (String) map.get("columnName");
String newColumnName = columnName.replace(cha, " ");
String tableColumnName = String.format("%s.[%s]", tableName, columnName);
String changeSql = "{CALL SP_RENAME(?,?,'column')}";
query = baseDao.getSQLQuery(changeSql);
query.setParameter(0, tableColumnName);
query.setParameter(1, newColumnName);
query.executeUpdate();
}
}

另一个问题是,本地数据库安装的时候使用的排序规则与服务器不一致,导致编写的sql运行时出现错误,提前修改排序规则。  

	private static void changeColumnCollation(BaseDao baseDao,String tableName){
String defaultCollation = "SQL_Latin1_General_CP1_CI_AS";
String dataType = "nvarchar";
String sql = "SELECT COLUMN_NAME AS columnName,CHARACTER_MAXIMUM_LENGTH AS length FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = ? AND COLLATION_NAME <> ? AND DATA_TYPE = ? ";
SQLQuery query = baseDao.getSQLQuery(sql);
query.setParameter(0, tableName);
query.setParameter(1, defaultCollation);
query.setParameter(2, dataType);
query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
List<Map<String,Object>> list = query.list();
for(Map<String,Object> map : list){
String columnName = (String) map.get("columnName");
Integer length = (Integer) map.get("length");
String changeSql = String.format("ALTER TABLE [%s] ALTER COLUMN [%s] %s(%d) COLLATE %s",tableName,columnName,dataType,length,defaultCollation);
query = baseDao.getSQLQuery(changeSql);
query.executeUpdate();
}
}

可以使用下面sql查询出表中的所有列。

SELECT * FROM INFORMATION_SCHEMA.COLUMNS

前台可以让用户现在Excel中每一列对应的真实表的列。对应关系组织好之后,根据需要给临时表添加字段、修改数据等等,最后使用INSERT SELECT插入数据或修改数据。

编写sql的时候可能因为列名的不规范,导致sql语法错误。这时候需要在列名或表名前后添加中括号“[]”。Jaya使用String.format();比较方便。

使用OPENROWSET、Microsoft.ACE.OLEDB实现大数据量的高效导入的更多相关文章

  1. 大数据量.csv文件导入SQLServer数据库

    前几天拿到了一个400多M的.csv文件,在电脑上打开要好长时间,打开后里面的数据都是乱码.因此,做了一个先转码再导入数据库的程序.100多万条的数据转码+导入在本地电脑上花了4分钟,感觉效率还可以. ...

  2. .NET读取Excel数据,提示错误:未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序

    解决.NET读取Excel数据时,提示错误:未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序的操作: 1. 检查本机是否安装Office Access,如果未安装去去h ...

  3. 从Excel中导入数据时,提示“未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序”的解决办法

    注意,64位系统,用64位的补丁文件; https://www.cnblogs.com/A2008A/articles/2438962.html 操作系统:使用的是64位的Windows Server ...

  4. 在使用Access连接后获取数据--出现此类问题如何解决---未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序解决办法

    转载:https://blog.csdn.net/yyzzhc999/article/details/79367114 using System; using System.Collections.G ...

  5. C#通过OLEDB导出大数据到Excel

    C#导出数据到Excel,基本有两种方法,一种方法是通过Microsoft.Office.Interop.Excel.Application,一行一列的写入Excel中:另一种方法是通过OLEDB,利 ...

  6. Microsoft.ACE.OLEDB.12.0 及其在 MSSQL中的使用

    1.Microsoft.ACE.OLEDB.12.0 简介 就是一个数据访问接口,用于在office文件和非office应用程序间传输数据.例如 Microsoft Office Access 201 ...

  7. Microsoft ACE OLEDB 12.0 数据库连接字符串

    Excel 97-2003 Provider=Microsoft.ACE.OLEDB.12.0;Data Source=c:\myFolder\myOldExcelFile.xls;Extended ...

  8. 未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序

    这种错误的可能性有几种,比如: 1.没有安装数据访问组件,需要安装相应版本的数据访问组件: 2.没有安装相应版本的Office客户端,需要安装相应版本的Office客户端: 3.Microsoft.J ...

  9. 未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序 解决方法

    最近在搞asp.net.今天在做数据库操作的时候,老发生错误,还以为是自己代码有问题,检查了好久都发现错误. 错误提示: 未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程 ...

随机推荐

  1. centos7 下修改时区

    #timedatectl status //显示当前设置 #timedatectl list-timezones //列出时区 #timedatectl set-timezone Asia/Shang ...

  2. 一次Oracle 11g+FluentNHibernate AutoMapping组合尝试

    本文的上下文环境 操作系统:Win7 x64 Ultimate开发工具:Visual Studio 2013 一.前言 在以前开发的大多数场景下,使用的开发语言是C#,使用的数据库是Sql Serve ...

  3. Java集合类简单总结(重学)

    java集合类简介(重学) 一.Collection(集合).Map接口两者应该是平行关系吧. 1.Map介绍 Map是以键值(key-value)对来存放的,2个值.通过key来找到value(例: ...

  4. java SSH框架详解(面试和学习都是最好的收藏资料)

    Java—SSH(MVC)1. 谈谈你mvc的理解MVC是Model—View—Controler的简称.即模型—视图—控制器.MVC是一种设计模式,它强制性的把应用程序的输入.处理和输出分开.MVC ...

  5. Spark Streaming源码解读之State管理之UpdataStateByKey和MapWithState解密

    本期内容 : UpdateStateByKey解密 MapWithState解密 Spark Streaming是实现State状态管理因素: 01. Spark Streaming是按照整个Bach ...

  6. bootstrapValidator.js,最好用的bootstrap表单验证插件

    前言:做Web开发的我们,表单验证是再常见不过的需求了.友好的错误提示能增加用户体验.博主搜索bootstrap表单验证,搜到的结果大部分都是文中的主题:bootstrapvalidator.今天就来 ...

  7. 第一天 :学习node.js

    ① node.js环境配置 我学过的语言最简单的一门 直接百度就可以配置 ② 每个入门 的程序都是从helloworld开始 代码如下 : var http=require('http'); http ...

  8. 阅读{django-restframework}源码[generics.py]学习笔记

    首先django-restframework是基于django的一个框架.   mixins.py中开头是这样写的: Basic building blocks for generic class b ...

  9. Android IOS WebRTC 音视频开发总结(七七)-- WebRTC的架构和协议栈

    本文主要介绍WebRTC的架构和协议栈(我们翻译和整理的,译者:litie),最早发表在[编风网] 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID:blackerteam ...

  10. .NET (五)委托第五讲:内置委托Predicate

    // 摘要: // 表示定义一组条件并确定指定对象是否符合这些条件的方法. // // 参数: // obj: // 要按照由此委托表示的方法中定义的条件进行比较的对象. // // 类型参数: // ...