I’ve had several occasions to use OPENROWSET recently in T-SQL. Although you can use it as an ad hoc alternative to a linked server to query a relational database, I’m finding it useful to get data from other sources. I’ll go into details of how to use it, but first I would like to acknowledge: OPENROWSET is not the answer to everything. Sometimes you should use a linked server or SSIS or some other method. I had some specific cases where I felt OPENROWSET made more sense for my task.
Importing Data from an Excel Spreadsheet
Technical need: I had some data that needed to be validated before importing into SQL Server from Excel as part of an upload process in an application. If it didn’t pass validation, I did not want to import the data. Rather than use SSIS to import the data into a table and then check it, I used OPENROWSET to pull it directly from the Excel file into a temp table, ran all of my validation checks, and then imported the data using SSIS all within a stored procedure that was called by an application. Field and table names have been changed to protect the innocent.
The stored procedure:
CREATE PROCEDURE dbo.MySproc @datafile nvarchar(127) AS BEGIN -- SET NOCOUNT ON added to prevent extra result sets from -- interfering with SELECT statements. SET NOCOUNT ON; DECLARE @sql nvarchar(max) , @rowcount int SELECT @rowcount = 0 DECLARE @FileExists int BEGIN TRY EXECUTE master.dbo.xp_fileExist @datafile, @FileExists out -- Value of 1 = file exists -- Value of 0 = file doesn't IF @FileExists = 0 BEGIN SELECT CONCAT('The file ', @datafile, ' could not be found.') AS reasontext , NULL AS field2 , NULL AS field3; RETURN -1; END END TRY BEGIN CATCH SELECT ERROR_MESSAGE() AS reasontext , NULL AS field2 , NULL AS field3; RETURN -1; END CATCH TRUNCATE TABLE dbo.finaldest; IF OBJECT_ID('tempdb..##tempf') IS NOT NULL BEGIN DROP TABLE ##tempf; END; --Create temp table to hold values from Excel file Create table ##tempf ( Field1 [nvarchar](255), Field2 [nvarchar](255), Field3 [nvarchar](255), Field4 [nvarchar](255), Field5 [nvarchar](255), Field6 [nvarchar](255) ); --dynamic sql to populate temp table with values from Excel file SELECT @sql = N'INSERT INTO ##tempf SELECT [Field1], [Field2], [Field3], [Field4], [Field5], [Field6], FROM OPENROWSET ( ''Microsoft.ACE.OLEDB.12.0'',''Excel 12.0;Database=' + @datafile + ';HDR=YES;IMEX=1'', ''SELECT s.* FROM [Sheet1$] AS S WHERE [Field2] IS NOT NULL AND [Field3] IS NOT NULL'')'; BEGIN TRY EXEC sp_executesql @sql; END TRY BEGIN CATCH SELECT 'File not found or invalid file type' AS reasontext --ERROR_MESSAGE() AS reasontext , NULL AS Field2 , NULL AS Field3; IF OBJECT_ID('tempdb..##tempf', 'U') IS NOT NULL BEGIN DROP TABLE ##tempf END RETURN -1 END CATCH /*Do lots of data validation here ... */ --if data validation tests are passed, write to final destination table Insert INTO dbo.[finaldest] ([Field1], [Field2], [Field3], [Field4], [Field5], [Field6]) Select [Field1], [Field2], [Field3], [Field4], [Field5], [Field6] from ##tempf; Drop table ##tempf; END
To make this work you need to install the Microsoft ACE OLE DB 12.0 provider on your server. You cannot use the Jet OLEDB Provider with 64-bit SQL Server and 64-bit Excel. Once you have this installed you need to change some settings:
EXEC master.dbo.sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0',
N'AllowInProcess', 1
GO
EXEC master.dbo.sp_MSset_oledb_prop N'Microsoft.ACE.OLEDB.12.0',
N'DynamicParameters', 1
GO
sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
sp_configure 'Ad Hoc Distributed Queries', 1;
GO
RECONFIGURE;
GO
Here is a blog post that explains the errors you might get if you don’t have these settings correct. It also notes that you can go check your settings in the registry keys. For SQL Server 2012 the path is HKEY_LOCAL_MACHINE -> SOFTWARE -> Microsoft -> Microsoft SQL Server -> MSSQL11.MSSQLSERVER -> Providers -> Microsoft.ACE.OLEDB.12.0. You can then see that AllowInProcess and Dynamic parameters are set to 1.
This provider seems to be a bit fussy and there are some things that took me a while to troubleshoot once I got started. First, make sure that the location of the Excel file you are reading is accessible to the account that is running the SQL engine and doesn’t require admin access. The Linked Server errors that get returned with this really aren’t that helpful, so you have to use your Google-fu to work your way through it. I used a global temporary table so it would be accessible across connections if it needed it to be (I was having trouble when I used a local temp table). The application that calls this stored procedure queues up the calls and executes them serially so one can’t override the other.
Returning results from an Analysis Services cube in SQL
Technical need: I had some data (several facts with common dimensions) with calculations in a multidimensional cube and an application that needed that data but couldn’t/wouldn’t consume the MDX result set.
The query: For demonstration purposes I’m using a small query that returns the values for a drop-down box for selecting a member of the date dimension.
select a."[Measures].[DateKeys]" as DateKey, a."[Measures].[DateLevel]" as DateLevel, a."[Measures].[DateValues]" as DateValue from OPENROWSET ('MSOLAP','Datasource=localhost; Initial Catalog=MyCubeDB;', 'with member [Measures].[DateValues] as Iif([Date].[Fiscal Calendar].currentmember.level_number = 2, " " + [Date].[Fiscal Calendar].currentmember.member_value, [Date].[Fiscal Calendar].currentmember.member_value) member [Measures].[DateKeys] as [Date].[Fiscal Calendar].currentmember.uniquename member [Measures].[DateLevel] as [Date].[Fiscal Calendar].currentmember.level_number select {[Measures].[DateValues], [Measures].[DateKeys], [Measures].[DateLevel]} on 0 , {Exists(Descendants([Date].[Fiscal Calendar].[FY2014], 1, SELF_AND_BEFORE), , "MyMeasureGroup") } on 1 from [MyCube] ') as a;
You can see that I explicitly named my fields, but you can also do a select * to get all the fields from the OPENROWSET. I had to put the fields from the cube in quotation marks to make it work because of the brackets in the field names. For Analysis Services, MSOLAP is the name of my provider. You must have the correct version of this provider listed under the linked servers on the server where this is being executed.
For SQL Server 2012 SP1, you can download the Microsoft Analysis Services OLE DB Provider for Microsoft SQL Server 2012 SP1 here. There is nothing special about the data source and initial catalog. They are exactly the same connection info you would use when connecting to SSAS through Management Studio. The last part is simply the MDX query. You should be able to copy that part out and run it in Management Studio connected to your SSAS database and get results.
Inserting Images Into a Database
Technical need: I was building a demo for Power BI and I needed to show some images in my Power View reports in Office 365. In order for the images to show in Office 365, you must import the images into SQL Server and then import them into Power Pivot. First I downloaded images of flags for each country from the CIA world factbook. I took the folder of flags and put it in my pictures folder. I already had a table in SQL Server that contained country names and codes. So I just needed add a flag for each country.
The query:
CREATE TABLE [dbo].[countryflagsdemo] ( [id] int, [country] varchar(100), varchar(2), flagimage [varbinary](max) ) --get ID, country, and code from existing table INSERT INTO dbo.countryflagsdemo (id, country, code) SELECT id, country, code FROM dbo.Countryflag1; DECLARE @i INT; DECLARE @rows INT; DECLARE @code VARCHAR(2); SET @i = 1; --get max id value for loop SET @rows = (SELECT COUNT(country) FROM dbo.countryflagsdemo) WHILE (@i < @rows) BEGIN DECLARE @sql VARCHAR(MAX); SET @code = (SELECT code FROM dbo.countryflagsdemo WHERE [id] = @i); SELECT @sql = 'UPDATE dbo.countryflagsdemo SET flagimage = (SELECT BulkColumn FROM OPENROWSET (BULK ''C:\Users\mlongoria\Pictures\flags\large\' + @code + '-lgflag.gif'', Single_Blob) as flagimage) WHERE = ''' + @code + ''';' EXEC (@sql); SET @i = @i + 1; --PRINT @code; END GO
I figured out how to do this based upon this MSDN blog post. I inserted my other data first and then added the images. I’m sure you could find a way to do this all in one select statement. Since this was a one-time thing I didn’t see the need to find another way. It should be noted that the bulk insert must pull from a location on your computer; you can’t use links to the images on the internet. I downloaded the images in a folder. Each image name was the abbreviation for the country. If you are going to import this data into PowerPivot, be sure you change the table behavior properties so it will recognize the varbinary field as an image.
There may be other ways to accomplish my goals, but it’s nice to understand how to use OPENROWSET and have that in my toolbox.