SQL-Server Dynamic SQL failing - sql-server

I have a stored procedure that dynamically generates a table to hold staging data for imports. This routine was running fine until Wednesday. I have tracked this down to one particular area. Code that's causing an issue is:
DECLARE #strSQL NVARCHAR(MAX) = N'';
SELECT #strSQL = #strSQL + N',[' + CustomerField + N'] NVARCHAR(MAX)' + CHAR(10)
FROM dbo.WebServices
WHERE CallType = 'customer'
AND IsPrimaryTable = 1
AND Source = 'clientName'
ORDER BY TagOrder;
SET #strSQL = STUFF(#strSQL, 1, 1, 'CREATE TABLE ' + 'TableName' + CHAR(10) + '(')+ N')';
PRINT #strSQL
NB. Table its pulling from holds a list of columns and the data hasn't changed either. There are about 34 rows of data with no special characters, and it isn't exposed to the clients.
This gives me an output of:
CREATE TABLE TableName
([CUSACNR2] NVARCHAR(MAX)
)
Running this on exactly the same data, even in the same window on SSMS, with an additional TOP filter gives me the results I was expecting i.e.
DECLARE #strSQL NVARCHAR(MAX) = N'';
SELECT TOP (99999) #strSQL = #strSQL + N',[' + CustomerField + N'] NVARCHAR(MAX)' + CHAR(10)
FROM dbo.WebServices
WHERE CallType = 'customer'
AND IsPrimaryTable = 1
AND Source = 'clientName'
ORDER BY TagOrder;
SET #strSQL = STUFF(#strSQL, 1, 1, 'CREATE TABLE ' + 'TableName' + CHAR(10) + '(')+ N')';
PRINT #strSQL
Generates the dynamic SQL:
CREATE TABLE TableName
([TAG] NVARCHAR(MAX)
,[CUSACNR] NVARCHAR(MAX)
.
.
.
,[SHIPPING_POSTAL_CODE] NVARCHAR(MAX)
,[CUSACNR2] NVARCHAR(MAX)
)
Specific column names removed for security
While I have tracked this down in one specific stored proc, we use this technique for building dynamic SQL a lot and I am seeing errors popping up in multiple places. I'm assuming that something has changed on the server as this has started happening in multiple databases at one time.
Does anyone know of anything that may have caused this issue? Its a legacy system that has been running fine for a long time and re-coding every instance of this type of activity is not an option.

I have found the answer. Sort of. I now know what had changed, so I've been able to correct it, but it should not have made any difference.
There was an index on the table that was causing the issue. Before you say that shouldn't make any difference, and you're right it shouldn't, I've managed to reliably solve and reproduce the problem by dropping and recreating the index.
So this isn't a fragmentation issue, it's not a clustered index, and the execution plan doesn't search by this index anyway.
CREATE NONCLUSTERED INDEX [idx_ntindex] ON [dbo].[WebServices] ([Source], [CallType], [SecurityKey])
GO
DECLARE #strSQL NVARCHAR(MAX) = N'';
SELECT #strSQL = #strSQL + N',[' + ISNULL(CustomerField,'') + N'] NVARCHAR(MAX)' + CHAR(10)
FROM dbo.WebServices
WHERE CallType = 'customer'
AND IsPrimaryTable = 1
AND Source = 'clientName'
ORDER BY TagOrder;
SET #strSQL = STUFF(#strSQL, 1, 1, 'CREATE TABLE ' + 'TableName' + CHAR(10) + '(') + N')';
PRINT #strSQL;
GO
DROP INDEX idx_ntindex ON dbo.WebServices
DECLARE #strSQL NVARCHAR(MAX) = N'';
SELECT #strSQL = #strSQL + N',[' + ISNULL(CustomerField,'') + N'] NVARCHAR(MAX)' + CHAR(10)
FROM dbo.WebServices
WHERE CallType = 'customer'
AND IsPrimaryTable = 1
AND Source = 'clientName'
ORDER BY TagOrder;
SET #strSQL = STUFF(#strSQL, 1, 1, 'CREATE TABLE ' + 'TableName' + CHAR(10) + '(') + N')';
PRINT #strSQL;
Gives me the results
CREATE TABLE TableName
([CUSACNR2] NVARCHAR(MAX)
)
CREATE TABLE TableName
([TAG] NVARCHAR(MAX)
,[CUSACNR] NVARCHAR(MAX)
,[DDAYS] NVARCHAR(MAX)
.
.
.
,[SHIPPING_POSTAL_CODE] NVARCHAR(MAX)
,[CUSACNR2] NVARCHAR(MAX)
)
If anyone knows WHY this is the case then please let me know. I found it but it doesn't make any sense to me.
Thanks for the help all. +1's for all the support, each suggestion got me a little closer.

Related

How do i insert or update a record based on data whicha has a apostohphe in a dynamic sql?

Here the #Data has a value with apostophe(')s . how do i update or insert a data based on the data value which is having apostophe in a dynamic sql
suppose #data has one value abc and another value abc's it throwing error for the second one
SET #SQL = ' Update '+ #ProcessCode + '_abc SET IS_IGNORING = 1 where Column_Name = '''+ #Column_Name +''' and [DATA] = ''' + #Data + ''' and Table_name = '''+ #Table_Name + ''''
Generally what i found is a manual process of adding one more apostophe but i am not really sure how to use that in a dynamic sql where not all data in the table is same, few of the data records has got this type of apostophe(')
Parameterized your query using sp_executesql
Example:
SET #SQL = 'Update ' + #ProcessCode + '_abc '
+ 'SET IS_IGNORING = 1 '
+ 'where Column_Name = #Column_Name '
+ 'and [DATA] = #Data '
+ 'and Table_name = #Table_Name '
EXEC sp_executesql #SQL,
N'#Column_Name varchar(100), #Data varchar(100), #Table_Name varchar(100)',
#Column_Name, #Data, #Table_Name
Do read up more on dynamic query and SQL Injection
You might find convenient to use parameterized queries, so you can replace static values with placeholders and then bind values to those placeholders before executing the query. It has certain advantages like better performance and helps to avoid SQL-injection attacks.
More info here: https://techcommunity.microsoft.com/t5/sql-server-blog/how-and-why-to-use-parameterized-queries/ba-p/383483

Dynamically insert data into a temp table with like as variable

I am trying to dynamically insert data into a temp table passing like data as a variable:
DECLARE #data NVARCHAR(MAX)
SET #data = 'INSERT INTO #coco ' + '([' + #val + '])' + ' SELECT [USER_ID] FROM [dbo].[Sheet1$] WHERE [Standard_Name] LIKE ' + #val
EXEC sp_executesql #data
#val is a column name selected from table Sheet1$ and few column name has space between them. While executing, I am getting error, like for column name "Acrobat Reader":
Incorrect syntax near 'Acrobat'.
Also if I am adding data using hardcoded one by one in a column its adding data to one column while other column its adding NULL.
Any suggestion how I can overcome this?
Parametrise your SQL, and this problem "goes away":
DECLARE #data;
SET #data = N'INSERT INTO #Coco (' QUOTENAME(#val) + N')' + NCHAR(10) +
N'SELECT [USER_ID]' + NCHAR(10) +
N'FROM dbo.[Sheet1$]' + NCHAR(10) +
N'WHERE [Standard_Name] = #val;'; --As this doesn't contain a %, there's no need for LIKE
EXEC sp_executesql #data,
N'#val = sysname', --guessed datatype
#val = #val;
Note the comments I made in the SQL though.
Afraid, I've no idea what your second statement means. You'll need to explain further.

SQL Server Concatenate JSON For Dynamic Queries

I have a dynamic (I am passing parameters in a stored procedure) query in a stored procedure which results in a JSON string. Similar to this:
#PropertyPK uniqueidentifier (Stored Procedure Parameter)
#search_term Varchar(50)
#limit int
#offset int
Declare #test Varchar(1000)
SELECT #test = '
SELECT Cast((
SELECT *
FROM Contacts
Where Address like ''%' + #search_term + '%''' + ' Order By '
+ #sort_by + ' ' + #sort_order + ' OFFSET '
+ Cast(#offset as varchar) +
' ROWS
FETCH NEXT '
+ Cast(#limit as varchar) +
' ROWS ONLY
For JSON Path, INCLUDE_NULL_VALUES )
as varchar(max))'
EXEC(#test)
I have been asked to return results from 2 queries in a JSON format but in one string. Basically run one query into a variable and the second into another and concatenate them together and then deliver the results.. Can someone help me putting the result JSON from the above query into a variable so I can do the same with my second query and concatenate them? Can I do anything after the Exec(#test) to get the result into a variable?
Thank you..
The latest query you've posted isn't dynamic either, so I'm unsure why you're using EXEC. As i mentioned in the comments, therefore, this is as simple as using SET:
DECLARE #PropertyPK uniqueidentifier; --SP parameter
DECLARE #JSON nvarchar(MAX);
SET #JSON = (SELECT *
FROM Contacts
WHERE PropertyPK = #PropertyPK
FOR JSON PATH, INCLUDE_NULL_VALUES);
There's no need for #PropertyPK to be cast as a varchar to a dynamic SQL statement; just use proper parametrised SQL.
This is based on guesswork and the OP's latest (but clearly incomplete) question. If this isn't correct, this should get you on the right path, however, having information drip fed makes the question difficult to answer properly.
DECLARE #PropertyPK uniqueidentifier,
#SearchTerm varchar(50),
#Limit int,
#Offset int,
#SortBy sysname, --objects have the data type sysname, aka nvarchar(128)
#SortOrder nvarchar(4); --Guess datatype as it was missing in your sample
DECLARE #JSON nvarchar(MAX);
DECLARE #SQL nvarchar(MAX);
SET #SQL = N'SET #JSON = (SELECT {Columns}' + NCHAR(10) + --Replace {Columns} with an actual list of the required columns (NOT *)
N' FROM dbo.Contacts' + NCHAR(10) +
N' WHERE Address LIKE #Search' + NCHAR(10) +
N' AND PropertyPK = #PropertyPK' + NCHAR(10) + --I ASSUME that WHERE is still needed
N' ORDER BY ' + QUOTENAME(#SortBy) + N' ' + CASE #SortOrder WHEN N'ASC' THEN N'ASC' WHEN N'DESC' THEN 'DESC' END + NCHAR(10) + --The CASE stops invalid sort orders
N' OFFSET #Offset FEETCH NEXT #Limit ROWS ONLY' + NCHAR(10) +
N' FOR JSON PATH, INCLUDE_NULL_VALUES);';
PRINT #SQL; --Your best friend
EXEC sp_executesql #SQL,
N'#JSON nvarchar(MAX) OUTPUT, #Search varchar(50), #PropertyPK uniqueidentifier, #Offset int, #Limit int',
#JSON = #JSON OUTPUT,
#Search = #SearchTerm,
#PropertyPK = #PropertyPK,
#Offset = #Offset,
#Limit = #Limit;
One of the biggest things you need to note here is I have made the SQL SAFE. Your SQL was wide open to SQL injection, which is a huge security flaw. If you don't know/understand SQL injection, I suggest reading about it now. SQL like you have above is a huge problem, and raw string concatenation is an awful idea waiting to be exploited.

I want to write one common stored procedure to insert data in any table of my database

I have 6 table with different fields. I want to access table name dynamically. is there any idea to do it?
My code is below this is simple procedure which I want to make dynamic to use in c#. how to do it?
Create procedure [dbo].[Insert_Data] (#Id int,#FeesHead nchar(20),#Fees int,#Remarks nchar(20))
as
begin
Insert into FeesHead(ID,FeesHead,Fees,Remarks)values(#Id,#FeesHead,#Fees,#Remarks)
End
Don't go there.
It's a bad idea since you will end up with a long, inefficient stored procedure that will be vulnerable to SQL injection attacks and have performance issues.
Writing an insert stored procedure for each table is the way to go.
You wrote you have six different tables with different columns, so writing a stored procedure to handle inserts for all of them will require you to send all the parameters for all columns as well as a parameter for the table name, and a nested if...else with 6 possible paths, one for each table.
This will end up as a long, messy, poorly written code at best, bad in each parameter: security, performance, code readability and maintainability.
The only way that makes some sense to achieve such a goal is to write individual insert stored procedures for each table, and then write a stored procedure that will take all of the possible parameters and the table name and inside of it decide what insert stored procedure to execute based on the value of the table name parameter. However, you will be better off leaving conditions like these to the SQL client (your c# code in this case) then to SQL Server.
Its very easy to do.........
Just call the sql query using Data Adapter.
select TABLE_NAME from INFORMATION_SCHEMA.TABLES
As you said you need dynamic SQL like this:
Create procedure [dbo].[Insert_Data]
(
#TableName nvarchar(512),
#Values nvarchar(max)
)
BEGIN
DECLARE #SQL nvarchar(max)
SELECT #SQL = 'INSERT INTO ' + #TableName + ' VALUES (' + #Values + ')'
EXEC(#SQL)
END
Note that #Values will be like this '1, ''name'', 10.2' and with the same order of columns.
or
Create procedure [dbo].[Insert_Data]
(
#TableName nvarchar(512),
#Fields nvarchar(max),
#Values nvarchar(max)
)
BEGIN
DECLARE #SQL nvarchar(max)
SELECT #SQL = 'INSERT INTO ' + #TableName + ' (' + #Fields + ') VALUES (' + #Values + ')'
EXEC(#SQL)
END
To more ability to handle column order and remove identity columns.
As Robert Harvey mentioned it is a bad idea, anyway if you want to you can do something like....
CREATE PROCEDURE Insert_Data
#TableName SYSNAME
,#Column1 SYSNAME = NULL
,#Column2 SYSNAME = NULL
,#Column3 SYSNAME = NULL
,#Value1 NVARCHAR(100) = NULL
,#Value2 NVARCHAR(100) = NULL
,#Value3 NVARCHAR(100) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Sql NVARCHAR(MAX);
SET #Sql = N' INSERT INTO ' + QUOTENAME(#TableName)
+ N' ( '
+ STUFF(
CASE WHEN #Column1 IS NOT NULL
THEN N',' + QUOTENAME(#Column1) ELSE N'' END
+ CASE WHEN #Column2 IS NOT NULL
THEN N',' + QUOTENAME(#Column2) ELSE N'' END
+ CASE WHEN #Column3 IS NOT NULL
THEN N',' + QUOTENAME(#Column3) ELSE N'' END
,1,1,'')
+ N' ) '
+ N' VALUES ( '
+ STUFF(
CASE WHEN #Value1 IS NOT NULL
THEN N', #Value1' ELSE N'' END
+ CASE WHEN #Value2 IS NOT NULL
THEN N', #Value2' ELSE N'' END
+ CASE WHEN #Value3 IS NOT NULL
THEN N', #Value3' ELSE N'' END
,1,1,'')
+ N' ) '
Exec sp_executesql #Sql
,N'#Value1 NVARCHAR(100),#Value2 NVARCHAR(100),#Value3 NVARCHAR(100)'
,#Value1
,#Value2
,#Value3
END

Change columnname with variable in WHILE loop SQL

Say i have columns PlanSalesMt1, PlanSalesMt2 and so on for the whole year until Mt12 in a table.
Before i created a view where i unioned all the months togheter but that query is pretty slow. Now i want to try the query with a Loop.
My Problem is i can't use the variable in my column names because they get invalid. Is that a way to solve this?
What i mean is i want to loop my question 12 times and change the name of the columns also after what month it is. But i cant make a #i variable and say select PlanSalesMt#1 or PlanSalesMt+#1 or PlanSalesMt+CAST(#1 as varchar) becuase it says invalid column Name.
Here is an example of dynamic SQL
DECLARE #ColumnName1 VarChar(20)
DECLARE #ColumnName2 VarChar(20)
DECLARE #sql Varchar(1000)
DECLARE #MyWhere VarChar(20)
SET #ColumnName1 = 'PlanSalesMt1'
SET #ColumnName2 = 'PlanSalesMt2'
SET #MyWhere = 'Books'
SET #sql = 'SELECT ' + #ColumnName1 + ', ' + #ColumnName2 + ' FROM TABLE WHERE ' + #ColumnName1 + ' = ''' + #MyWhere + ''''
EXEC(#sql)
If you change EXEC(#sql) to SELECT #sql copy into SSMS and run it you will see the query this produces.

Resources