Bulk insert fails as dynamic sql - sql-server

I have this code in an SP that sets up a bulk insert:
begin try
declare #sentFile nvarchar(255)
declare #bulk_cmd nvarchar(1000) = ''
declare #loadDate nvarchar(8) = Convert(nvarchar(8),#p_loadDate) -- #p_loadDate is char(8)
set #StrImportFolder = N'D:\EMVImports\'
set #sentFile = #StrImportFolder + N'etl_rnli_sent_'+ #loadDate + N'.txt'
SET #bulk_cmd = N'BULK INSERT loadSent
FROM ''' + #sentFile + N'''
WITH (
FIRSTROW = 2
,formatfile=''D:\EMVScripts\Sent_Format.xml''
)'
Print #bulk_cmd
EXECUTE sp_executesql #bulk_cmd
-- more stuff happens here
end try
Inside my stored procedure, this fails with this error:
Cannot fetch a row from OLE DB provider "BULK" for linked server "(null)".
But the code printed out:
BULK INSERT loadSent
FROM 'D:\EMVImports\etl_sent_20130529.txt'
WITH (
FIRSTROW = 2
,formatfile='D:\EMVScripts\Sent_Format.xml'
)
works like a charm. I've no idea why it fails under sp_executesql.

I am using much similar query. And it is working.
DECLARE #filepath nvarchar(500)
SET #filepath = N'e:\5-digit Commercial.csv'
DECLARE #bulkinsert NVARCHAR(2000)
SET #bulkinsert =
N'BULK INSERT ZIPCodes FROM ''' +
#filepath +
N''' WITH (FIRSTROW = 2, FIELDTERMINATOR = '','', ROWTERMINATOR = ''\n'')'
EXEC sp_executesql #bulkinsert
How do you set the value of #sentFile?

We can not set the 'From' file path dynamically for BULK INSERT.
You are generating the path dynamically
set #sentFile = #StrImportFolder + N'etl_rnli_sent_'+ #loadDate + N'.txt'
Here the #loadDate is a variable component of the file name.
The working example given above uses fixed path event with a variable:
SET #filepath = N'e:\5-digit Commercial.csv',
Here the variable has a fix path for every case.
So, try to use a pre-define file path.

Related

Amount of single quotes when running dynamic SQL with variable

I am having a real hard time sorting out the number of ' I should have within the following SQL statement:
declare #sql varchar(max)
declare #LetterID varchar(max) = 'c01as1'
set #sql =
'SELECT fltr.tency_seq_no FROM OPENQUERY(loopback,
''SET FMTONLY OFF; EXEC BST.[LET].[LETTERBUILD] #LetterCode =
''''\\SVR-QL4APPLIVE\QLSHAREPOINT\LETTERS\DATAFILES\
'''+#LetterID+'''
.csv''''
WITH RESULT SETS (tency_seq_no VARCHAR(255))''
) AS fltr'
exec (#sql)
Current error:
Msg 102, Level 15, State 1, Line 3
Incorrect syntax near 'c01as1'
Just to clarify, the error is referring to the #LetterID variable within the dynamic SQL , not when declaring the parameter
Print of # SQL
SELECT fltr.tency_seq_no
FROM OPENQUERY(loopback, 'SET FMTONLY OFF; EXEC BST.[LET].[LETTERBUILD] #LetterCode = ''\\SVR-QL4APPLIVE\QLSHAREPOINT\LETTERS\DATAFILES\'c01as1'.csv''
WITH RESULT SETS (tency_seq_no VARCHAR(255));'
) AS fltr
Any help would be appreciated!
I think the error has got something to do with unwanted line breaks etc in the original code. Try this instead:
DECLARE #sql VARCHAR(MAX);
DECLARE #LetterID VARCHAR(MAX) = 'c01as1';
SET #sql = 'SELECT fltr.tency_seq_no FROM OPENQUERY(loopback,
''SET FMTONLY OFF; EXEC BST.[LET].[LETTERBUILD] #LetterCode = ''''\\SVR-QL4APPLIVE\QLSHAREPOINT\LETTERS\DATAFILES\'
+ #LetterID + '.csv''''
WITH RESULT SETS (tency_seq_no VARCHAR(255))''
) AS fltr';
EXEC (#sql);
When I ran into this difficulty a few months ago, I created a function that would perform a double-up on the quotes for dynamic SQL. Instead of searching the string manually each time for quotes needing to be doubled, this scalar function can perform this task. This can prevent potentially cluttering up the script when future modifications are performed, such as adding additional variables, as well as improving readability.
Function as follows:
CREATE FUNCTION dbo.fn_duplicateQuotes
(#string varchar(max),
#level int)
RETURNS varchar(max)
AS
BEGIN
/*Doubles-up quotation marks for nested dynamic SQL
level can be set greater than 1 to add additional doubled-up quotes
for further nested dynamic SQL*/
/*Double up quotes*/
set #string = REPLACE(#string, '''', REPLICATE('''', (#level) * 2))
/*Return Value*/
return #string
END
Dynamic SQL as follows:
declare #SQL nvarchar(max)
declare #LetterID varchar(max) = 'c01as1'
set #SQL = 'SET FMTONLY OFF; EXEC BST.[LET].[LETTERBUILD] #LetterCode =
''\\SVR-QL4APPLIVE\QLSHAREPOINT\LETTERS\DATAFILES\' + #LetterID + '.csv''
WITH RESULT SETS (tency_seq_no VARCHAR(255));'
set #SQL = 'SELECT fltr.tency_seq_no FROM OPENQUERY(loopback,
''' + dbo.fn_duplicateQuotes(#SQL, 1) + '''
) AS fltr'
print #SQL
exec (#SQL)
Print of #SQL returns:
SELECT fltr.tency_seq_no FROM OPENQUERY(loopback,
'SET FMTONLY OFF; EXEC BST.[LET].[LETTERBUILD] #LetterCode =
''\\SVR-QL4APPLIVE\QLSHAREPOINT\LETTERS\DATAFILES\c01as1.csv''
WITH RESULT SETS (tency_seq_no VARCHAR(255));'
) AS fltr

Incorrect syntax near 'c:'

this a sample of test SP i was created befor
when i run it "Incorrect syntax near 'c:'."
create PROCEDURE [dbo].[TextImpotedSP2]
AS
set #path = 'c:temp\TextImpoted0.txt'
SET NOCOUNT ON;
DECLARE #bulk_cmd NVARCHAR(max);
SET #bulk_cmd =
'BULK
INSERT TextImpoted FROM '+ #path1 + '; WITH (FIELDTERMINATOR = ' + ';' ',ROWTERMINATOR = ' + '\n'+ ' )';
EXEC sp_executesql #bulk_cmd
--*************************
any one have any idea?
There seem to be several issues. First, the variable path is never declared, and it's also incorrectly referred to as path1 which needs to be changed. Second, some of the values in the bulk_cmd string needs to be quoted.
A corrected version that should work:
CREATE PROCEDURE [dbo].[TextImpotedSP2]
AS
DECLARE #path NVARCHAR(max)
SET #path = 'c:\temp\TextImpoted0.txt'
SET NOCOUNT ON;
DECLARE #bulk_cmd NVARCHAR(max);
SET #bulk_cmd = '
BULK INSERT TextImpoted
FROM '''+ #path + '''
WITH
(
FIELDTERMINATOR = ' + ''';''' + ',
ROWTERMINATOR = ' + '''\n'''+ '
)';
--PRINT #bulk_cmd
EXEC sp_executesql #bulk_cmd
Also, the path string 'c:temp\TextImpoted0.txt' should probably be 'c:\temp\TextImpoted0.txt' as you might get unexpected results if you use a relative path.
Edit: added the solution to the follow-up question that were posted as an answer...
CREATE PROCEDURE [dbo].[TextImpoted01]
AS
SET NOCOUNT ON;
DECLARE #SQL NVARCHAR(100)
SELECT TOP (1) #SQL = total FROM DealCounter
DECLARE #path NVARCHAR(max)
SET #path = 'c:\temp\TextImpoted'+ #SQL + '.txt'
DECLARE #bulk_cmd NVARCHAR(max);
SET #bulk_cmd = '
BULK INSERT TextImpoted
FROM '''+ #path + '''
WITH
(
FIELDTERMINATOR = ' + ''';''' + ',
ROWTERMINATOR = ' + '''\n'''+ '
)';
--PRINT #bulk_cmd
EXEC sp_executesql #bulk_cmd
change
set #path = 'c:temp\TextImpoted0.txt'
to
set #path = 'c:\temp\TextImpoted0.txt'
the \ is important

Can I specify a database name dynamically in a trigger?

I need to get data from a table in a database who's database name will be determined as a variable during a trigger. I then, knowing this variable need to get a seqno from a table in the determined database for a item which was also determined as a variable during a trigger.
I am trying this route as I assume I need to build the SQL statement before I set it to a variable.
This is not working and I need to know the best way on how I can do this:
DECLARE #SU_SEQNO INTEGER, #SU_NAME VARCHAR(50), #SU_OWNER VARCHAR(15), #SUD_SEQNO INTEGER, #SQL NVARCHAR(500)
SET #SU_OWNER = 'XXX'
SET #SU_NAME = '1ABC234'
SET #SQL ='SELECT #SUD_SEQNO=SEQNO FROM ' + (#SU_OWNER) + '.SU_MAIN
WHERE UNITNAME= ' + #SU_NAME
SET #SUD_SEQNO = (EXECUTE (#SQL))
Thanks alot for any help with this
From: Get result from dynamic SQL in stored procedure
SET #SQL = N'SELECT DISTINCT #FiscalYear = FiscalYear FROM ' + #DataSource;
EXEC sp_executesql #SQL, N'#FiscalYear INT OUTPUT', #FiscalYear OUTPUT;
PRINT #FiscalYear;
I'd re-engineer to use the sp_executesql method as shown above. That should do the trick.
I have amended the code, and it works
declare #su_owner varchar(15) = 'DBTEST'
declare #SU_SEQNO INTEGER=1, #SUD_SEQNO INTEGER=0, #SQL NVARCHAR(500)
DECLARE #ParmDefinition NVARCHAR(500), #SU_NAME_INPUT VARCHAR(50)='SU123'
SET #SU_NAME_INPUT = (SELECT UNITNAME FROM SU_MAIN WHERE SEQNO=#SU_SEQNO)
SET #SU_NAME = (SELECT UNITNAME FROM SU_MAIN WHERE SEQNO=#SU_SEQNO)
SET #SQL = N'SELECT #sud_seqnoOUT=MAX(SEQNO) FROM ' + quotename(#su_owner) + '.[dbo].[SU_MAIN] WHERE UNITNAME]=#SU_NAME_INPUT' ;
SET #ParmDefinition = N'#SU_NAME_INPUT VARCHAR(50),#sud_seqnoOUT INT OUTPUT'
EXEC sp_executesql #SQL,#ParmDefinition,#SU_NAME_INPUT = #SU_NAME,
#sud_seqnoOUT = #SUD_SEQNO OUTPUT

MSSQL : Insert value of execute statement together with another string in a table

What I have
I am currently inserting all the SQL files from the current directory in a temporary table :
create table #tmpSQLFiles(SQLFileName varchar(max))
set #SqlStatement = 'master.dbo.xp_cmdshell ''dir /b "' + #DirPath + '*.sql"'''
insert into #tmpSQLFiles
execute (#SqlStatement)
This works well.
What I want
Now I want the table to have an extra varchar column in which I can store a certain desired string :
create table #tmpSQLFiles(SQLFileName varchar(max), MyString varchar(max))
set #SqlStatement = 'master.dbo.xp_cmdshell ''dir /b "' + #DirPath+ '*.sql"'''
What I have tried
How can I do the insert? I have tried :
insert into #tmpSQLFiles (SQLFileName, MyString) values (execute (#SqlStatement), 'aa')
or :
declare #ExecutedStmt varchar(max)
set #ExecutedStmt = execute (#SqlStatement)
-- But the above line is not correct.
Any of these 2 would work for me. I don't want to insert and update afterwards the second column.
Any ideas on how I can achieve this? Thank you !
Try using a table variable as an intermediary - not the prettiest, but does the job:
create table #tmpSQLFiles(SQLFileName varchar(max), MyString varchar(max))
set #SqlStatement = 'master.dbo.xp_cmdshell ''dir /b "' + #DirPath + '*.pdf"'''
DECLARE #TempOutpt TABLE ([output] varchar(500))
insert into #TempOutpt ([output])
execute (#SqlStatement)
insert into #tmpSQLFiles (SQLFileName, MyString)
select [output], 'my string here' from #TempOutpt
select * from #tmpSQLFiles
If you weren't using an extended stored procedure (xp_cmdshell), you might have been able to use OPENROWSET to execute the SP:
SELECT *fields*
FROM OPENROWSET('SQLNCLI',
'Server=*your_server*;Trusted_Connection=yes;Database=*your_database*',
'exec *stored_procedure* *parameters*')
You need to use an output parameter so you can use the results in the next INSERT:
DECLARE #result INT, #sql NVARCHAR(512)
SET #sql = N'SELECT #result = COUNT(*) FROM ' + #dbname + '.INFORMATION_SCHEMA.TABLES'
EXEC sp_executesql
#query = #sql,
#params = N'#result INT OUTPUT',
#result = #result OUTPUT
PRINT #result

How to create and populate a table in a single step as part of a CSV import operation?

I am looking for a quick-and-dirty way to import CSV files into SQL Server without having to create the table beforehand and define its columns.
Each imported CSV would be imported into its own table.
We are not concerned about data-type inferencing. The CSV vary in structure and layout, and all of them have many many columns, yet we are only concerned with a few of them: street addresses and zipcodes. We just want to get the CSV data into the SQL database quickly and extract the relevant columns.
I'd like to supply the FieldTerminator and RowTerminator, point it at the CSV, and have the utility do the rest. Is there any way to create the table and populate it, all in one step, using BULK INSERT and/or OpenRowset(BULK ... ) ?
Referencing SQLServerPedia, I think this will work:
sp_configure 'show advanced options', 1;
RECONFIGURE;
GO
sp_configure 'Ad Hoc Distributed Queries', 1;
RECONFIGURE;
GO
select TerritoryID
,TotalSales
,TotalCost
INTO CSVImportTable
from openrowset('MSDASQL'
,'Driver={Microsoft Access Text Driver (*.txt, *.csv)}'
,'select * from C:\csvtest.CSV')
Annoying, I don't have the rep points yet to just comment, so I'll add an answer based on TyT's (that handle looks terrible in possessive, btw ...)
The worker code needed a double "\" instead of a single for me to avoid a "file not found" error. And you don't have to specify the fields; they will be inferred from the first row of the file:
select *
into CsvImportTable
from openrowset(
'MSDASQL',
'Driver={Microsoft Access Text Driver (*.txt, *.csv)}',
'select * from C:\\csvtestfile.csv')
I had no problems with the Access driver.
UPDATE: If you have trouble with the types being inferred incorrectly, insert a few rows at the top of the file with data of the type you want in the table so you get, say text -> VARCHAR instead of text-> INT and then delete those rows after the import.
As the final icing, add a PK to the table so you can manipulate the data - delete the dummy rows, etc:
alter table CsvImportTable add Id int identity(1, 1)
Updated answer if you're using SQL Server Management Studio 17.
Right click on Database -> Tasks -> Import Flat File...
It will automatically infer the first row of the data as the column names. It should automatically pick up the terminators. You will get the option to set primary keys, allowing nulls, and specify data types for the columns as well.
Putting all .CSV files to a folder and running this is working well for me.
IF OBJECT_ID('dbo.ConfigFile', 'u') IS NOT NULL DROP TABLE [dbo].[ConfigFile];
IF OBJECT_ID('tempdb..#columns', 'u') IS NOT NULL DROP TABLE #columns;
CREATE TABLE ConfigFile(Path VARCHAR(255), FileName VARCHAR(255));
DECLARE #filename VARCHAR(255)
, #path VARCHAR(255)
, #cmd VARCHAR(8000);
SET #path = 'C:\FTP_DATA\Netscout\test\'; --PATH TO YOUR CSV FILES (CHANGE TO YOUR PATH)
SET #cmd = 'dir ' + #path + '*.csv /b';
INSERT INTO ConfigFile(FileName)
EXEC Master..xp_cmdShell #cmd;
DELETE from ConfigFile WHERE FileName IS NULL;
UPDATE ConfigFile SET Path = #path WHERE Path IS NULL;
DECLARE cur CURSOR
FOR SELECT Path
, FileName
FROM ConfigFile
WHERE FileName LIKE '%.csv%'
OPEN cur
FETCH NEXT FROM cur INTO #path
, #filename
WHILE ##fetch_status -1
BEGIN
CREATE TABLE #columns(HeadString NVARCHAR(MAX))
DECLARE #Columns NVARCHAR(MAX) = ''
DECLARE #Query NVARCHAR(MAX) = ''
DECLARE #QUERY2 NVARCHAR(MAX) = ''
DECLARE #HeaderQuery NVARCHAR(MAX) = ''
SELECT #HeaderQuery = 'BULK INSERT #columns FROM ''' + #path + #filename + ''' WITH(firstrow=1,lastrow=1)';
EXEC (#HeaderQuery);
SELECT #Columns = (
SELECT QUOTENAME(value) + ' nvarchar(max)' + ','
FROM #columns
CROSS APPLY STRING_SPLIT(HeadString,',') FOR xml PATH('')
)
IF ISNULL(#Columns,'') ''
BEGIN
SET #Columns = LEFT(#Columns,LEN(#Columns) - 1)
SELECT #Query = 'IF OBJECT_ID(''dbo.['+ REPLACE(#filename,'.csv','') +']'', ''u'') IS NOT NULL DROP TABLE [' + REPLACE(#filename,'.csv','') + ']'
EXEC (#QUERY)
SELECT #Query = 'CREATE TABLE [' + REPLACE(#filename,'.csv','') + '] (' + REPLACE(#Columns,'"','') + ')'
EXEC (#QUERY)
END
SELECT #QUERY2 = 'BULK INSERT [' + REPLACE(#filename,'.csv','') + '] FROM ''' + #path + #filename + '''
WITH(firstrow=2,CODEPAGE = ''65001'',FORMAT=''csv'',FIELDTERMINATOR='','',ROWTERMINATOR=''\n'')';
EXEC (#QUERY2);
DROP TABLE #columns
FETCH NEXT FROM cur INTO #path
, #filename;
END;
CLOSE cur;
DEALLOCATE cur;

Resources