使用OPENROWSET、Microsoft.ACE.OLEDB实现大数据量的高效导入

首先说明使用的环境是: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();比较方便。

时间: 2024-11-10 05:58:02

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

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

前几天拿到了一个400多M的.csv文件,在电脑上打开要好长时间,打开后里面的数据都是乱码.因此,做了一个先转码再导入数据库的程序.100多万条的数据转码+导入花了4分钟,感觉效率还可以.各位网友有更好的方法,请在留言中指点下,一起学习学习,多谢了. static void Main(string[] args) { int count = 0; string readerPath=@"C:\Users\Administrator\Desktop\readerDemo.csv"; st

Microsoft ACE OLEDB 12.0概念及用法

首先需要清楚几个概念: Database engine(数据引擎):一些预先存储于数据库中的组件: Microsoft JET (Joint Engine Technology):Microsoft Jet 数据引擎,1992年发行初版,主要运用于 Microsoft Accesss的数据连接,Jet引擎,仅能访问 Office97-2003: Microsoft ACE(Office Access Connectivity Engine ):随着Access 2007发布之后, Microso

未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序解决办法 错误信息:未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序。

解决办法: 去http://download.microsoft.com/download/7/0/3/703ffbcb-dc0c-4e19-b0da-1463960fdcdb/AccessDatabaseEngine.exe下载.然后安装就行了. 本错误是由于你使用了ACCESS2007版本建立的数据库,但服务器中没有相配合使用的程序,所以出错. 未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序. 说明: 执行当前 Web 请求期间,出现未处理的异常.请检查堆栈

System.InvalidOperationException: 未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序。

最近用MVC3 做了一个项目,发布时项目的中的数据导入功能(Excel格式,有固定的导入数据模板)居然不能用,查看报错日志,发现是“System.InvalidOperationException: 未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序............”. 在网上找一些资料,把问题解决了.如下图所示. 后台功能代码:导入与导出实现代码. 1 #region -使用IO写入Excel- 2 /// <summary> 3 /// 使用IO写入E

未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0

从Excel中导入数据时,提示"未在本地计算机上注册"Microsoft.ACE.OLEDB.12.0"提供程序"的解决办法 操作系统:使用的是64位的Windows Server 2008 解决办法: 这是由于该计算机上没有安装Microsoft Access Database Engine组件,该组件帮助在现有的Microsoft Office文件与其他数据源之间传输数据. 在向软件中导入数据时,如果数据源选用Excel时,连接字符串中使用的是"Mic

c#操作Excel时,抛出异常:“未在本地计算机上注册“Microsoft.ACE.OLEDB.12.0”提供程序”

我们开发环境下,使用excel导入数据到数据库中,编译的软件起初是x86 方式,起初并未发现什么问题,一切很正常: 程序该进的过程: 后来导入文件一次就要读取几百G的数据导入数据库中,使用编译的X86程序就不正常了,尽管我解析文件(.xml)方式从数据流改用XmlTextReader,但还是会跑出内存溢出(“System.OutofMemoryException”多么痛苦,悲剧,无奈呀),后来同事建议我将程序编译x64后问题真正的解决了(我们开发机时64G内存,呵呵.) 程序改进后问题内存溢出问

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

在win7 操作系统中SQL2008导入excel2007 出现: 未在本地计算机上注册"microsoft.ACE.oledb.12.0"提供程序 的解决方法: 出现这个原因是office 2007没有安装64位的数据驱动,下载: http://download.microsoft.com/download/7/0/3/703ffbcb-dc0c-4e19-b0da-1463960fdcdb/AccessDatabaseEngine.exe 数据引擎安装后即可.

未在本地计算机上注册&quot;microsoft.ACE.oledb.12.0&quot;提供程序解决办法

错误信息:未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序. 解决办法: 去http://download.microsoft.com/download/7/0/3/703ffbcb-dc0c-4e19-b0da-1463960fdcdb/AccessDatabaseEngine.exe下载.然后安装就行了. 本错误是由于你使用了ACCESS2007版本建立的数据库,但服务器中没有相配合使用的程序,所以出错. 未在本地计算机上注册“microsoft.ACE.ol

未在本地计算机上注册&quot;Microsoft.ACE.OLEDB.12.0&quot;提供程序

开发SharePoint,需要导入Excel表数据,结果问题来了: 解决方法一:下载64位的Microsoft Access 2010 数据库引擎. http://www.microsoft.com/zh-cn/download/details.aspx?id=13255 解决方法二:在Visual Studio的项目属性里,将build的platform target由Any CPU改为x86,这样build出来的就是32位的程序. SharePoint程序不可以为32位,所以方法二,不可以.