Parameterize Bulk Insert for Microsoft SQL Server 2014 - tsql

I am trying to parameterize the LASTROW argument of a BULK INSERT query.
The last row of data in the textfile I am importing in to SQL Server contains information about the number of rows in the textfile. And I plan to use that number to populate the value of the LASTROW argument of the BULK INSERT query.
An example of the textfile:
20161003|3504|1360|
20161003|3540|1441|
EOF|2
The query I am working on:
DECLARE #FilePath VARCHAR(500)
SELECT #FilePath = 'C:\path\textfile.txt'
DECLARE #file VARCHAR(MAX)
SELECT #file = (SELECT * FROM OPENROWSET(BULK N'C:\path\textfile.txt', SINGLE_CLOB) AS Content)
DECLARE #LastRow INT
DECLARE #LastRow_String VARCHAR(10)
SELECT #LastRow_String = (SELECT SUBSTRING(#file, CHARINDEX('EOF|', #file)+4, LEN(#file)))
SELECT #LastRow = (SELECT CAST(#LastRow_String AS INT))
DECLARE #Query VARCHAR(MAX)
SELECT #Query = ('BULK INSERT [Database].[dbo].[Table] FROM ''' + #FilePath +
''' WITH ( FIELDTERMINATOR = ''|'', ROWTERMINATOR = ''|\n'', LASTROW = ' + #LastRow + ')')
When I execute the above query I get this error message:
Msg 245, Level 16, State 1, Line 11
Conversion failed when converting the varchar value 'BULK INSERT [Database].[dbo].[Table] FROM 'C:\path\textfile.txt' WITH ( FIELDTERMINATOR = '|', ROWTERMINATOR = '|\n', LASTROW = ' to data type int.
Any help is appreciated!

The problem is that LastRow is an integer; you may need to use LastRow_String for the concatenation into Query; if you are concerned about the white spaces, you can do ltrim(rtrim(#LastRow_String))

Related

The name "xxx" is not permitted in this context. Valid expressions are constants, constant expressions, and (in some contexts) variables

I want to separate a table's columns into two set (1st set = bottom 50%, 2nd set = top 50%, there is a reason why I am not using a median formula in this case and I know that there will be a case when the count([ORDINAL_POSITION]) will be an odd number, then I won't get accurate result.) to achieve this I am trying to use INFORMATION_SCHEMA.COLUMNS, but I can't figure it out why I got the following error message:
The name "sometable" is not permitted in this context. Valid expressions are constants, constant expressions, and (in some contexts) variables. Column names are not permitted.
DECLARE #table2 NVARCHAR(MAX)
DECLARE #table_op_mid INT
SET #table2 = 'sometable'
SELECT #table_op_mid = 'SELECT ROUND(MAX([ORDINAL_POSITION])/2,0) AS OP FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '+#table2+''
PRINT (table_op_mid)
EXEC (#table_op_mid)
1st problem is that #table_op_mid is declared as INT instead of VARCHAR
2nd problem is that #table2 need extra quotes when used in TABLE_NAME comparision
3rd problem is that table_op_mid is missing # symbol, should be PRINT(#table_op_mid)
DECLARE #table2 NVARCHAR(MAX)
DECLARE #table_op_mid NVARCHAR(MAX)
SET #table2 = 'sometable'
SELECT #table_op_mid = 'SELECT ROUND(MAX([ORDINAL_POSITION])/2,0) AS OP FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '''+#table2+''''
PRINT (#table_op_mid)
EXEC (#table_op_mid)
EDIT
After your comment.. It is the same problem as before.. #SQL_columnnull_part_2 should be VARCHAR instead of INT
declare #db2 varchar(max) = 'MyDb'
declare #table2 varchar(max) = 'sometable'
declare #SQL_columnnull_part_2 varchar(max) = ''
Also, your new query will not work because STRING_AGG doesn't add last separator, so you should move the comparision term in the 1st parameter and keep in separator only the ';'
SELECT #SQL_columnnull_part_2 = STRING_AGG(
'UPDATE ' + #db2 + '.[dbo].' + #table2 + ' WITH (TABLOCK) SET ' + QUOTENAME(COLUMN_NAME,'['']') + ' = NULL WHERE ' + QUOTENAME(COLUMN_NAME,'['']') + ' = ''''',
'; '
)
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = #table2
AND [ORDINAL_POSITION] > #table_op_mid

Access pass-through query passing comma separated list as a parameter to SQL stored procedure

The SQL server is 2008. I have an Access 2016 front-end for reporting purposes. One report requires that one or more Product Classes from a list be chosen to report on. I have the VBA that creates the pass-through query with the appropriate single line:
exec dbo.uspINVDAYS 'A3,A4,A6,AA,AB'
I have this SQL code that should take the list as hard-coded here:
DECLARE #parProductClasses NVARCHAR(200) = 'A3,A4,A6,AA,AB';
DECLARE #ProductClasses NVARCHAR(200),#delimiter NVARCHAR(1) = ',';
SET #ProductClasses = #parProductClasses;
DECLARE #DAYS INT,#numDAYS int;
SET #DAYS = 395;
SET #numDAYS = #DAYS;
SELECT UPINVENTORY.StockCode, UPINVENTORY.[Description], UPINVENTORY.Supplier, UPINVENTORY.ProductClass
, UPINVENTORY.WarehouseToUse
, CAST(UPINVENTORY.Ebq AS INT)Ebq
, cast(UPINVENTORY.QtyOnHand AS INT)QtyOnHand
, cast(UPINVENTORY.PrevYearQtySold AS INT)PrevYearQtySold
, cast(UPINVENTORY.YtdQtyIssued AS INT)YtdQtyIssued
,#numDAYS as numDAYS
,CAST(ROUND((PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS,0) AS INT)TOTAL
,CASE WHEN (PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS
= 0 THEN 0
ELSE CAST(ROUND(QTYONHAND/((PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS),0)AS INT)
END FINAL
,CASE WHEN (PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS
= 0 THEN 0
ELSE CAST(ROUND(QTYONHAND/((PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS),0)AS INT)
END FINAL1
FROM
TablesCoE.dbo.vwRPUpInventory UPINVENTORY
WHERE UPINVENTORY.ProductClass IN (Select val From TablesCoE.dbo.split(#ProductClasses,','));
When I run this I get:
Msg 468, Level 16, State 9, Line 9
Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and "Latin1_General_BIN" in the equal to operation.
I cannot determine where
COLLATE SQL_Latin1_General_CP1_CI_AS
should go. Where am I equating or comparing? The SQL IN clause cannot handle the comma-separated list since it is not a strict SQL table.
Here's the code used to create the dbo.split() function:
CREATE FUNCTION dbo.split(
#delimited NVARCHAR(MAX),
#delimiter NVARCHAR(100)
) RETURNS #t TABLE (id INT IDENTITY(1,1), val NVARCHAR(MAX))
AS
BEGIN
DECLARE #xml XML
SET #xml = N'<t>' + REPLACE(#delimited,#delimiter,'</t><t>') + '</t>'
INSERT INTO #t(val)
SELECT r.value('.','varchar(MAX)') as item
FROM #xml.nodes('/t') as records(r)
RETURN
END
Thanks to Sandeep Mittal and I am sure others have very similar split functions. Run separately this function does operate as expected and provides a table of the comma-separated list objects.
DECLARE #parProductClasses NVARCHAR(200) = 'A3,A4,A6,AA,AB';
DECLARE #ProductClasses NVARCHAR(200),#delimiter NVARCHAR(1) = ',';
SET #ProductClasses = #parProductClasses;
Select val From TablesCoE.dbo.split(#ProductClasses,',')
Returns
val
A3
A4
A6
AA
AB
try this.
WHERE concat(',',#ProductClasses,',') like concat('%',UPINVENTORY.ProductClass,'%')
it's a silly way of checking if your productClass is within the #productClasses list.
After attempting to use a prefabricated table-valued variable versus on the fly in the WHERE clause, neither worked, I then started to try different placements of the COLLATE statement. I was complacent in applying COLLATE to the right-side with the collation listed on the left in the SQL error message. I tried the collation listed on the right of the SQL error message to the left side of the WHERE clause and the SQL code works to spec now. Here it is:
DECLARE #parProductClasses NVARCHAR(200) = 'A3,A4,A6,AA,AB';
DECLARE #ProductClasses NVARCHAR(200),#delimiter NVARCHAR(1) = ',';
SET #ProductClasses = #parProductClasses;
DECLARE #DAYS INT,#numDAYS int;
SET #DAYS = 395;
SET #numDAYS = #DAYS;
SELECT UPINVENTORY.StockCode, UPINVENTORY.[Description], UPINVENTORY.Supplier, UPINVENTORY.ProductClass
, UPINVENTORY.WarehouseToUse
, CAST(UPINVENTORY.Ebq AS INT)Ebq
, cast(UPINVENTORY.QtyOnHand AS INT)QtyOnHand
, cast(UPINVENTORY.PrevYearQtySold AS INT)PrevYearQtySold
, cast(UPINVENTORY.YtdQtyIssued AS INT)YtdQtyIssued
,#numDAYS as numDAYS
,CAST(ROUND((PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS,0) AS INT)TOTAL
,CASE WHEN (PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS
= 0 THEN 0
ELSE CAST(ROUND(QTYONHAND/((PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS),0)AS INT)
END FINAL
,CASE WHEN (PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS
= 0 THEN 0
ELSE CAST(ROUND(QTYONHAND/((PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS),0)AS INT)
END FINAL1
FROM
TablesCoE.dbo.vwRPUpInventory UPINVENTORY
WHERE UPINVENTORY.ProductClass COLLATE Latin1_General_BIN IN (SELECT val FROM TablesCoE.dbo.split(#ProductClasses,','));
Thanks for your suggestions #Krish and #Isaac.
Tim

Getting Error With GROUP BY when Converting Query to Dynamic SQL

I'm trying to convert the following query to dynamic SQL to allow for variations:
UPDATE T
SET SumCount = J.SUM
FROM #temp T
JOIN (SELECT Count_99221 + COUNT_99222 + Count_99223 [SUM], t2.userID
FROM #temp t2
GROUP BY t2.userID, Count_99221 + COUNT_99222 + Count_99223
) J ON T.userID = J.UserID
This is what I have for the Dynamic SQL:
DECLARE #sql3 nvarchar(2000) =
'UPDATE T ' +
'SET SumCount = J.SumOfColumns ' +
'FROM #temp T ' +
'JOIN (SELECT ' + #columnSumString + ' [SumOfColumns], t2.userID ' +
'FROM #temp t2 ' +
'GROUP BY t2.userID, ' + #columnSumString +
' ) J ON T.userID = J.UserID'
EXEC sp_executesql #sql3
I am receiving the following error only when I run the query as Dynamic SQL:
Each GROUP BY expression must contain at least one column that is not
an outer reference.
Can somebody help explain why this is happening? I am new to Dynamic SQL so I'm not privy to any limitations for running queries this way.
Thank you in advance.
EDIT:
The variable #columnString is a string made by concatenating several other column names, created in the following way:
DECLARE #Cursor Cursor
DECLARE #code varchar(20)
DECLARE #ID INT
SET #cptCursor = CURSOR FOR
SELECT * FROM dbo.Split(#UserInput,CHAR(44))
OPEN #cptCursor
FETCH NEXT FROM #cptCursor INTO #ID, #code
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #colName varchar(50) = 'Count_' + cast(#code as varchar(10))
DECLARE #sql nvarchar(50) = 'ALTER TABLE #temp ADD ' + #colName + ' int'
EXEC sp_executesql #sql
--Code that adds values to each column that is created.....
SET #columnSumString = #colName + ' + ' + #columnSumString
--SET #columnSumString = #code + ' + ' + #columnSumString
FETCH NEXT FROM #cptCursor INTO #ID, #code
END
CLOSE #Cursor
DEALLOCATE #Cursor
SET #columnSumString = SUBSTRING(#columnSumString,1,LEN(#columnSumString)-2)
SELECT #columnSumString
The user input is a comma separated string. "Count_99221 + COUNT_99222 + Count_99223" is just one example of columns created from the user input "99221, 99222, 99223".
I also realized I was concatenating the #code variable into #columnSumString instead of #colName. Now when I run the query I don't get the error (even though I don't understand how the above error message relates to that mistake) but every value of SumCount is NULL.
IMHO you must re-write your query as follow:
UPDATE #temp
SET SumCount =
(SELECT Count_99221 + COUNT_99222 + Count_99223
FROM #temp t2
WHERE t2.userID = #temp.userID)
So the dynamic SQL will become:
DECLARE #columnString varchar(200)
SET #columnString = Count_99221 + COUNT_99222 + Count_99223
DECLARE #sql3 nvarchar(2000) =
N'UPDATE #temp ' +
'SET SumCount = (SELECT ' + #columnString +
' FROM #temp t2 WHERE t2.userID = #temp.userID)'
EXEC sp_executesql #sql3

two table input parameters in stored procedure

I am working on C# project which needs a stored procedure which will take two table names as inputs.
First table will copy data to a temp table which has two columns URL & channelID. This URL column is then matched with other input table's URL column & if match is found then it will update channel id from temp table to other tables channel ID.
I have written stored procedure as
CREATE PROCEDURE [dbo].[UpdateTables]
#excelTable NVARCHAR(128) ,
#TableName NVARCHAR(128)
AS
Declare #channel_Id nvarchar(50)
Declare #url varchar(400)
BEGIN
Select *
Into #Temp
From QUOTENAME(#excelTable)
END
While EXISTS(SELECT * From #Temp ) > 0
Begin
Select Top 1
#channel_Id = channel_Id, #url = url
From #Temp
update QUOTENAME(#TableName)
set channelid = #channelid
where pagefullurl like '%'+ #url + '%'
Delete #Temp
Where channelid = #channelid
End
I don't have much knowledge in TSQL and my above code has errors.
Incorrect syntax near '>'.
Msg 137, Level 15, State 2, Procedure UpdateTables, Line 20
Must declare the scalar variable "#channelid".
Msg 137, Level 15, State 2, Procedure UpdateTables, Line 22
Must declare the scalar variable "#channelid".
Please suggest what changes needs to done
I don't have MS SQL server handy to test it, but you declare your variable as #channel_Id, and later try to use it as #channelid (without the underscore) so you get errors about the undeclared variable.
I've corrected your SP and this is how it should look
CREATE PROCEDURE [dbo].[UpdateTables]
#excelTable NVARCHAR(128) ,
#TableName NVARCHAR(128)
AS
Declare #channel_Id nvarchar(50)
Declare #url varchar(400)
BEGIN
Select *
Into #Temp
From QUOTENAME(#excelTable)
While EXISTS(SELECT * From #Temp )
Begin
Select Top 1
#channel_Id = channel_Id, #url = url
From #Temp
update QUOTENAME(#TableName)
set channelid = #channel_Id
where pagefullurl like '%'+ #url + '%'
Delete #Temp
Where channelid = #channel_Id
End
END

Update Sql db with a NULL value passed from Parameter

I need to store a value with Null which is passed from a parameterized query using VB.net into SSEE 2008 R2.
The value maybe either 'Null' or a blank string "". How can I test for this and properly UPDATE the field in my Stored Procedure?
EDIT: Added declarations.
#ID int,
#currTable varchar(150),
#prev_LangString nvarchar(max),
#brief_Descrip nvarchar(max)
BEGIN
IF #brief_Descrip IS NULL OR #brief_Descrip = 'Null'
SET #brief_Descrip = 'Null';
END
BEGIN
SET #sql = 'UPDATE ' + #currTable + ' SET [date_Changed] = ''' + convert(varchar(20), #submitDate1) + ''', [prev_LangString] = ''' + #prev_LangString + ''', [brief_Descrip] = ''' + #brief_Descrip + '''
WHERE (ID = ' + CAST(#ID as nvarchar(10)) + '); '
EXECUTE(#sql);
END
Thanks for any help on this.
The problem is that you are converting #brief_Descript to a string. This will also fix your injection vulnerability.
BEGIN
IF #brief_Descrip = 'Null'
SET #brief_Descrip = NULL;
END
UPDATE TABLE table_name
SET
date_Changed = convert(varchar(20), #submitDate1),
prev_LangString = #prev_LangString,
brief_Descrip = #brief_Descrip,
WHERE
ID = CAST(#ID as nvarchar(10))
EDIT
The best way to fix this is to convert the string null to DBNull in vb.net, and use a parameterized query with an update statement.
You should not convert the date to a string. Change the column type to a date time.
Another option would be to use a parameterized query. It would even make your problem go away. Like this:
DECLARE #sql NVARCHAR(4000), #params NVARCHAR(4000)
SET #sql = 'UPDATE ' + #currTable + ' SET date_changed = #p0, prev_langstring = #p1, brief_descrip = #p2 WHERE id = #p3'
SET #params = '#p0 VARCHAR(20), #p1 VARCHAR(???), #p2 VARCHAR(???), #p3 NVARCHAR(10)'
DECLARE #sd VARCHAR(20), #sid NVARCHAR(10)
SET #sd = CONVERT(VARCHAR(20), #submiteDate1)
SET #sid = CAST(#ID AS NVARCHAR(10))
EXEC sp_executesql #sql, #params, #p0 = #sd, #p1 = #prev_langstring, #p2 = #brief_descrip, #p3 = #sid
I don't know the datatype of #prev_langstring and of #brief_descrip, hence the VARCHAR(???) you see above; replace it by the real datatype.
You can read about sp_executesql here.