Dynamic SELECT checking for records with apostrophe [duplicate] - sql

I was trying to execute the below statement to escape single quotes (i.e. using two single quotes):
declare #year varchar(max)
set #year = '111,11';
exec ('SELECT * FROM SplitValues(' + #year + ','','')');
I even tried to use char(39) instead of quotes:
declare #year varchar(max)
set #year = '111,11';
exec ('SELECT * FROM SplitValues(' + #year + ',' + char(39) + ',' + char(39) + ')');
But it didn't help. These are the only two solutions that I found on this site. Any help?
This is the simplified query to clear up all your questions:
declare #year varchar(max)
set #year = '111,11';
SELECT * FROM SplitValues(#year , ',')
I want to achieve this, but using a dynamic query.

A word of advice. When testing a dynamic script, first just display it instead of executing it. That way you will be able to see it exactly as it would be seen by the EXEC statement.
Now to the issue. You should keep in mind that you are not passing the variable to SplitValues but are instead concatenating the variable's value into the script. Since the value is varchar, it should be concatenated with quotation marks around it. The absence of them is the only problem really.
The quotes around the second argument, the comma, are escaped correctly in both cases. So, just use either of the methods to add the quotes around the first argument:
repetition of the quotation mark:
DECLARE #year varchar(max), #sql varchar(max);
SET #year = '111,11';
SET #sql = 'SELECT * FROM SplitValues(''' + #year + ''','','')';
SELECT #sql;
using CHAR(39):
DECLARE #year varchar(max), #sql varchar(max);
SET #year = '111,11';
SET #sql = 'SELECT * FROM SplitValues(' + CHAR(39) + #year + CHAR(39) + ',' + CHAR(39) + ',' + CHAR(39) + ')';
SELECT #sql;
Obviously, the first method is more compact, but, like I said, both work well, as this SQL Fiddle demo clearly shows.
Note, however, that you could easily escape this issue in the first place, if you pardon the pun. Instead of EXEC (), you could use EXEC sp_executesql, which allows you to use parameters. Here's the same script rewritten to use sp_executesql:
DECLARE #year varchar(max), #delim char(1);
SET #year = '111,11';
SET #delim = ',';
EXEC sp_executesql
N'SELECT * FROM SplitValues(#year_param,#delim_param)',
N'#year_param varchar(max), #delim_param char(1)',
#year,#delim;
As you can see, no need to worry about escaping the quotes: SQL Server takes the trouble of substituting the values correctly, not you.

Just type single quote two times:
select 'that''s it'

Ok... you want to take this string:
SELECT * FROM SplitValues(#year , ',')
And make it a string like this:
'SELECT * FROM SplitValues('111,11' , '','')'
So, your final code would be:
declare #year varchar(max), #sql varchar(max)
set #year = '111,11';
set #sql = 'SELECT * FROM SplitValues(''' + #year + ''' , '''','''')'
select #sql
Actually, finally select you would use exec() instead. However you really should probably use sp_sqlexecute for stuff like this since you can use paramaterized queries.

declare #var1 varchar(100)
declare #var3 varchar(100)
declare #var4 varchar(100)
declare #var2 nvarchar(MAX)
set #var1 = ‘anil’
set #var4 = ‘1019518594’
set #var2 = N’select
a.*
from card b
join log a on a.Cust = b.ID
where a.c = ”’ + #var1 + ”’ and b.s =”’+ #var4 +””
print(#var2)
exec sp_executesql #var2

Related

What is the purpose and how this SQL query functions?

I have unfortunately executed this query and my website is not displaying some values properly from SQL database now. Could anyone explain what this query actually do?
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = CAST(REPLICATE(N'A',3000) AS NVARCHAR(MAX)) + CAST(REPLICATE(N'B',3000) AS NVARCHAR(MAX))
SELECT LEN(#SQL)
Let's break your code:
-- string variable declaration #SQL at this moment it is NULL
DECLARE #SQL NVARCHAR(MAX)
-- assign to variable #SQL concatentation of AAA...AAABBB...BBB
-- 3000 3000
SET #SQL = CAST(REPLICATE(N'A',3000) AS NVARCHAR(MAX)) + CAST(REPLICATE(N'B',3000) AS NVARCHAR(MAX))
--get length of string (6000 = 3000 + 3000)
SELECT LEN(#SQL)
DBFiddle Demo

Building insert statement with quotes in SQL

I am trying to understand how the single quotes work in SQL.
All I want to achieve is
INSERT INTO LOGTABLE
(ID,
ROLLNO)
VALUES ('E8645F55-A18C-43EA-9D68-1F9068F8A9FB',
28)
Here ID is a uniqueidentifier field and rollNo is an int.
So I have this sample test code:
set #query = '
insert into fileLog
(
id,
rollNo
)
values
('+
'''' + NEWID() + '''' + ',' + 28 +
')'
print #query
I have tried several combination of single quotes left and right but nothing works. I would really appreciate if someone could solve this. But in particular I wanted to know how many single quotes are required on both sides of a string to get something like 'SQL'.
Thanks
My question is: Why are you using dynamic SQL? It's one of those techniques that is useful in some situations, but can be abused easily.
As for the answer to your question, I use a technique to help minimize the flipping in and out of SQL construction:
DECLARE #query VARCHAR(MAX)
SET #query = '
insert into fileLog
(
id,
rollNo
)
values
(''|NEWID|'', |INT|)'
SET #query = REPLACE(#query, '|NEWID|', NEWID())
SET #query = REPLACE(#query, '|INT|', 28)
PRINT #query
(I'm going to assume you need dynamic SQL for reasons not obvious in the question, since this doesn't seem to require dynamic SQL at all.)
As #Gidil suggested, the problem here is trying to treat a uniqueidentifier as a string. In this case, there really isn't any reason to declare NEWID() in the outer scope, since you can simply say:
SET #query = 'INSERT ... VALUES(NEWID(), 28);';
PRINT #query;
Now, you should be using NVARCHAR(MAX) as your parameter, because ultimately you should be executing this using sp_executesql, not EXEC().
If you need to have a literal you can double up the quotes:
DECLARE #string VARCHAR(32);
SET #string = 'foo';
SET #query = N'INSERT ... VALUES(''' + #string + ''', 28);';
However I find it more readable to use CHAR(39):
SET #query = N'INSERT ... VALUES(' + CHAR(39) + #string + CHAR(39) + ', 28);';
And even better is to not append these variables to a string anyway. You should be using properly typed parameters where possible.
DECLARE #query NVARCHAR(MAX);
DECLARE #string VARCHAR(32), #newid UNIQUEIDENTIFIER, #id INT;
SELECT #string = 'foo', #newid = NEWID(), #id = 28;
SET #query = N'INSERT ... VALUES(#string, #newid, #id);';
EXEC sp_executesql #query,
N'#string VARCHAR(32), #newid UNIQUEIDENTIFIER, #id INT',
#string, #newid, #id;
It's bulkier, sure, but it's much safer from SQL injection and it lets you stop trying to figure out and deal with the hassle of embedding single quotes into the string...
Try this:
DECLARE #query VARCHAR(MAX)
SET #query = ' insert into fileLog ( id, rollNo ) values (' + '''' + Cast(Newid() AS VARCHAR(100)) + ''''
+ ',28)'
PRINT #query
The problem isn't the quotes, but the data types.
NEWID isn't a string and neither is the number 28.
Good luck!
Unless you need dynamic SQL for some reason, you can probably just do this:
insert into fileLog
(
id,
rollNo
)
values
(
NEWID(),
28
)

Compare date in stored procedure in an exec statement

I have a stored procedure that looks like this:
create stored procedure aaa
#columnName nvarchar(10),
#comparisonParam nvarchar(10),
#val nvarchar(100)
as
declare #date date
set #date = convert(#val, date)
exec('select * from Sheep where ' + #columnName + #comparisonParam + #date )
When actually the query is supposed to be like this:
select * from Sheep where birth_date = 12-12-2000
When I run the procedure it doesn't work with date value, but with string and int it works.
The date value must be quoted.
On a side note, I'd warn against doing this. If you need to build up dynamic sql you need to consider the risks such as: sql injection attacks, bad syntax, invalid semantics etc.
Consider using an existing component to build the query. A few examples:
.NET LINQ (to SQL/Entities) http://msdn.microsoft.com/en-us/library/bb397926.aspx
.NET SqlCommandBuilder http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommandbuilder.aspx
See Best way of constructing dynamic sql queries in C#/.NET3.5?
Your date literal needs to be surrounded in single quotes (I use CHAR(39) usually since it is easier to read and doesn't require escaping). Otherwise you are saying:
WHERE birth_date = (12) - (12) - (2000)
Which resolves to:
WHERE birth_date = -2000
Which resolves to DATEADD(DAY, -2000, '1900-01-01') or:
WHERE birth_date = '1894-07-11'
This is probably not going to yield the results you want.
With typical SQL injection warnings in place of course, and assuming that #columnName is always a string or date/time column, here is how I would re-write your stored procedure (though I would probably try to avoid the dynamic SQL altogether if I could).
ALTER PROCEDURE dbo.aaa
#columnName NVARCHAR(10),
#comparisonParam NVARCHAR(10),
#val NVARCHAR(100)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'SELECT * FROM dbo.Sheep WHERE '
+ QUOTENAME(#columnName) + #comparisonParam + CHAR(39)
+ REPLACE(#val, CHAR(39), CHAR(39) + CHAR(39))
+ CHAR(39);
EXEC sp_executesql #sql;
END
GO
In order to thwart potential issues you may want to add validation for columns and data types, and ensure that the operation is one you expect. e.g.
CREATE PROCEDURE dbo.bbb
#columnName NVARCHAR(10),
#comparisonParam NVARCHAR(10),
#val NVARCHAR(100)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #delimiter CHAR(1);
SELECT #delimiter = CASE
WHEN [system_type_id] IN
(104,48,52,56,127,59,60,62,106,108,122) THEN '' -- numeric
WHEN [system_type_id] IN
(35,40,41,42,43,58,61,99,167,175,231,239) THEN CHAR(39) -- string
END FROM sys.columns WHERE [object_id] = OBJECT_ID(N'dbo.Sheep')
AND name = #columnName;
IF #delimiter IS NULL
BEGIN
RAISERROR('Column ''%s'' was not found or an unexpected data type.', 11, 1,
#columnName);
RETURN;
END
IF #comparisonParam NOT IN (N'=', N'>=', N'<=', N'<', N'>', N'LIKE')
BEGIN
RAISERROR('Comparison param ''%s'' was not valid.', 11, 1, #comparisonParam);
RETURN;
END
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'SELECT * FROM dbo.Sheep WHERE '
+ QUOTENAME(#columnName) + ' ' + #comparisonParam + ' '
+ #delimiter + REPLACE(#val, CHAR(39), CHAR(39) + CHAR(39))
+ #delimiter;
EXEC sp_executesql #sql;
END
GO
Now make sure you use an unambiguous date format for your string literals. 12-12-2000 is not a good choice. 20001212 is much better.
There are possibly some ways to do this without dynamic SQL - I gave a very simplified answer here. This may be feasible depending on the data types, the number of potential columns, and the number of operations you want to support.
create stored procedure aaa
#columnName nvarchar(10),
#comparisonParam nvarchar(10),
#val nvarchar(100)
as
declare #date date
set #date = convert(#val, date)
exec('select * from Sheep where ' + #columnName + #comparisonParam + #date )
Build your dynamic SQL using a typed date parameter. Use sp_executesql which allows to pass parameter definitions and parameter values to the embedded SQL:
create procedure aaa
#columnName nvarchar(10),
#comparisonParam nvarchar(10),
#val nvarchar(100)
as
declare #date date, #sql nvarchar(max);
set #date = convert(#val, date);
-- Note how #date is a *variable* in the generated SQL:
set #sql =N'select * from Sheep where ' +
quotename(#columnName) + #comparisonParam + N'#date';
-- Use sp_executesql and define the type and value of the variable
exec sp_executesql #sql, N'#date date', #date;
You need to create table valued function for this rather than creating a stored procedure.
You can use any table valued function like
SELECT * from dbo.CallMyFunction(parameter1, parameter2
eg.
CREATE FUNCTION Sales.ufn_SalesByStore (#storeid int)
RETURNS TABLE
AS
RETURN
(
SELECT P.ProductID, P.Name, SUM(SD.LineTotal) AS 'Total'
FROM Production.Product AS P
JOIN Sales.SalesOrderDetail AS SD ON SD.ProductID = P.ProductID
JOIN Sales.SalesOrderHeader AS SH ON SH.SalesOrderID = SD.SalesOrderID
JOIN Sales.Customer AS C ON SH.CustomerID = C.CustomerID
WHERE C.StoreID = #storeid
GROUP BY P.ProductID, P.Name
);
GO
See this for reference http://msdn.microsoft.com/en-us/library/ms191165(v=sql.105).aspx
EDIT
Instead of using dynamic sql try giving a thought on
SELECT * FROM
FROM [dbo].[Person]
WHERE ([PersonID] = #PersonID
OR #AreaID IS NULL
)
AND (([Code] BETWEEN #Code AND CHAR(255))
OR #Code IS NULL
)
AND (([Name] BETWEEN #Name AND CHAR(255))
OR #Name IS NULL
)
AND (([Notes] BETWEEN #Notes AND CHAR(255))
OR #Notes IS NULL
)

SQL Linked Server query with Parameters

I need to select value from SQL Linked Server & get it to loacal variable
This is what I've written so far:
DECLARE #SQLQUERY AS VARCHAR(1000)
DECLARE #FINALQUERY AS VARCHAR(1000)
DECLARE #OutVal AS VARCHAR(10)
SET #SQLQUERY = 'SELECT Field1 FROM Table1 WHERE Field2=' + CAST(#var1 AS VARCHAR)
SET #FINALQUERY = 'SELECT #OutVal=Field1 FROM OPENQUERY(LINKEDSERVER,' + '''' + #SQLQUERY + '''' + ')'
EXEC(#finalQuery)
but this is wrong as it does not set the local variable(#OutVal).
Instead of exec, use sp_execute_sql with an output parameter:
exec sp_executesql #FinalQuery, N'#OutVal output', #OutVal = #OutVal out
Since sp_executesql expects nvarchar parameters, be sure to change the definition of #FinalQuery to nvarchar(max).
#OutVal in query string does not recognized as a variable. use a function or return table statement.

Executing dynamic SQL in a SQLServer 2005 function

I will preface this question by saying, I do not think it is solvable. I also have a workaround, I can create a stored procedure with an OUTPUT to accomplish this, it is just easier to code the sections where I need this checksum using a function.
This code will not work because of the Exec SP_ExecuteSQL #SQL calls. Anyone know how to execute dynamic SQL in a function? (and once again, I do not think it is possible. If it is though, I'd love to know how to get around it!)
Create Function Get_Checksum
(
#DatabaseName varchar(100),
#TableName varchar(100)
)
RETURNS FLOAT
AS
BEGIN
Declare #SQL nvarchar(4000)
Declare #ColumnName varchar(100)
Declare #i int
Declare #Checksum float
Declare #intColumns table (idRecord int identity(1,1), ColumnName varchar(255))
Declare #CS table (MyCheckSum bigint)
Set #SQL =
'Insert Into #IntColumns(ColumnName)' + Char(13) +
'Select Column_Name' + Char(13) +
'From ' + #DatabaseName + '.Information_Schema.Columns (NOLOCK)' + Char(13) +
'Where Table_Name = ''' + #TableName + '''' + Char(13) +
' and Data_Type = ''int'''
-- print #SQL
exec sp_executeSql #SQL
Set #SQL =
'Insert Into #CS(MyChecksum)' + Char(13) +
'Select '
Set #i = 1
While Exists(
Select 1
From #IntColumns
Where IdRecord = #i)
begin
Select #ColumnName = ColumnName
From #IntColumns
Where IdRecord = #i
Set #SQL = #SQL + Char(13) +
CASE WHEN #i = 1 THEN
' Sum(Cast(IsNull(' + #ColumnName + ',0) as bigint))'
ELSE
' + Sum(Cast(IsNull(' + #ColumnName + ',0) as bigint))'
END
Set #i = #i + 1
end
Set #SQL = #SQL + Char(13) +
'From ' + #DatabaseName + '..' + #TableName + ' (NOLOCK)'
-- print #SQL
exec sp_executeSql #SQL
Set #Checksum = (Select Top 1 MyChecksum From #CS)
Return isnull(#Checksum,0)
END
GO
It "ordinarily" can't be done as SQL Server treats functions as deterministic, which means that for a given set of inputs, it should always return the same outputs. A stored procedure or dynamic sql can be non-deterministic because it can change external state, such as a table, which is relied on.
Given that in SQL server functions are always deterministic, it would be a bad idea from a future maintenance perspective to attempt to circumvent this as it could cause fairly major confusion for anyone who has to support the code in future.
Here is the solution
Solution 1:
Return the dynamic string from Function then
Declare #SQLStr varchar(max)
DECLARE #tmptable table (<columns>)
set #SQLStr=dbo.function(<parameters>)
insert into #tmptable
Exec (#SQLStr)
select * from #tmptable
Solution 2:
call nested functions by passing parameters.
You can get around this by calling an extended stored procedure, with all the attendant hassle and security problems.
http://decipherinfosys.wordpress.com/2008/07/16/udf-limitations-in-sql-server/
http://decipherinfosys.wordpress.com/2007/02/27/using-getdate-in-a-udf/
Because functions have to play nicely with the query optimiser there are quite a few restrictions on them. This link refers to an article that discusses the limitations of UDF's in depth.
Thank you all for the replies.
Ron: FYI, Using that will throw an error.
I agree that not doing what I originally intended is the best solution, I decided to go a different route. My two choices were to use sum(cast(BINARY_CHECKSUM(*) as float)) or an output parameter in a stored procedure. After unit testing speed of each, I decided to go with sum(cast(BINARY_CHECKSUM(*) as float)) to get a comparable checksum value for each table's data.