Handling IS NULL inside string in SQL - sql-server

I have a SQL query which is written in string and then executed using command Exec(string) like the following :
Declare #TestId bigint = null
Declare #Query nvarchar(max)
set #Query = 'SELECT * from Registrations where RegistrationId = 15 AND (' + CAST(#TestId AS NVARCHAR) + ' IS NULL OR TestId = ' + CAST(#TestId AS NVARCHAR) + ') '
EXEC(#Query)
The problem now that the IS NULL is not parsed correctly inside the string but when i remove the IS NULL from the string it works correctly and when the #TestId takes a value other than null it works correctly where the problem now is in the casting of the IS NULL inside the #Query string.
Note : #TestId is a procedure parameter
I need to know how to make the SQL feel with the IS NULL and parse it correctly
Thanks in advance

If you really do need to use dynamic sql for this, use sp_executesql like this:
Declare #TestId bigint = null
Declare #Query nvarchar(max)
set #Query = 'SELECT * from Registrations where RegistrationId = 15 AND (#TestId IS NULL OR TestId = #TestId)'
EXECUTE sp_executesql #Query, N'#TestId BIG INT', #TestId

You don't need dynamic SQL for this. Also if you were to generate dynamic SQL one of the benefits of doing so is so that your queries do not need to have this sort of WHERE TestId =#TestId OR #TestId IS NULL construct which causes problems with plan caching and unnecessary table scans.
Declare #TestId bigint = null
Declare #Query nvarchar(max)
IF #TestId IS NULL
SELECT * from Registrations where RegistrationId = 15
ELSE
SELECT * from Registrations where RegistrationId = 15 AND TestId =#TestId
Edit
Following comment if you do need dynamic SQL then use sp_executesql and generate different strings for the case where #TestId is null. Don't shoehorn both cases into the same query.

The other answers provide the solutions. Here is why your solution didn't work. When #TestId is null, you are concatenating a null to your #Query string, and that assigns null to #Query. If you print your #Query in place of the the exec, you will see the query that would run.
Declare #TestId bigint = 10--null
Declare #Query nvarchar(max)
set #Query = 'SELECT * from Registrations where RegistrationId = 15 AND (' + CAST(#TestId AS NVARCHAR) + ' IS NULL OR TestId = ' + CAST(#TestId AS NVARCHAR) + ') '
--EXEC(#Query)
print #Query

Building dynamic queries is bad, m'kay...that said, you can fix it by moving the null check outside of the dynamic query and handling it there:
set #Query = 'select * from registrations where registrationId = 15 and
CAST(TestId AS NVARCHAR) = ' + ISNULL(CAST(#TestId AS NVARCHAR), '')
On another note, why are you using a dynamically built query for something as simple as this? It could be a regular SELECT statement which would make things much less complicated.
select * from registrations where registrationId = 15 and TestId = #TestId

From your code I suspect what you want to is to filter the results from Registrations by the TestId, given that it is not null. Why not do this programatically instead of in the SQL query? Something along those lines (syntax my be wrong):
Declare #TestId bigint = null
Declare #Query nvarchar(max)
if(#TestId IS NULL)
set #Query = 'SELECT * from Registrations where RegistrationId = 15'
else
set #Query = 'SELECT * from Registrations where RegistrationId = 15 AND (TestId = ' + CAST(#TestId AS NVARCHAR) + ') '
EXEC(#Query)
Also, Justin Niessner is right, dynamic SQL is to be avoided if possible (this could be done without dynamic SQL).

Related

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.

Querying a column that contains comma separated values

I'm trying to figure out why my where clause is returning all rows.
I'm querying a column that contains csv's using a variable that also contains csv's. I've built a stored function to split the variable on csv and return a table with one row that contains what I'd like to have on the right side of the LIKE operator.
Example:
The stored function:
ALTER Function [dbo].[storedFunction]
(#Fields VARCHAR(MAX),
#Field_Name VARCHAR(MAX) = '')
RETURN #Tbl_Fields Table (FIELD Varchar(max))
AS
BEGIN
DECLARE #FIELD varchar(max) = REPLACE(#Fields, ',', '%''' + ' AND ' +
#Field_Name + ' Like ' + '''%');
INSERT INTO #Tbl_Fields
SELECT '''%' + #FIELD + '%'''
RETURN
END
Using the stored function:
BEGIN
DECLARE #variable varchar(max) = 'variable1, variable3';
END
SELECT field
FROM storedFunction(#variable, 'main_csv_field');
returns '%variable1%' AND main_csv_field Like '%variable3%'
My simplified query:
BEGIN
DECLARE #variable varchar(max) = 'variable1, variable3';
END
SELECT main_csv_field
FROM table
WHERE (main_csv_field LIKE (SELECT field
FROM storedFunction(#variable, 'main_csv_field');
returns
variable1,variable2,variable3,variable4,...
variable2,variable4,...
variable1,variable3,...
My problem is this last query returns all of the rows in the table regardless of value matching. Were I to copy and paste the value returned from the stored function I would get the data that I need.
How/what is the difference here?
Thanks to #Obie and #AllanS.Hansen I knew where to start looking to fix this. Its pretty rough, but I wanted to post a solution before I got too far down the rabbit hole:
DECLARE variable1 varchar(max) = '' --around 9 passed from code
DECLARE #query nvarchar(max);
DECLARE #column_list varchar(max) = 'column1, column2, etc'
--one of each of the tests per variable passed from code
DECLARE #variable1_test nvarchar(max) = (SELECT CASE WHEN #variable = '' THEN '%' ELSE (SELECT * from dbo.stored_function(#variable, 'column_name')) END);
END;
SET #query = ' SELECT ' + #column_list + '
FROM table_name
WHERE variable LIKE ''' + #variable_test + ''' '
EXECUTE sp_executesql #query;
print(#query); --This is just to see the resulting query, which helped me a ton
Exciting! Now I have to test it.

Use variable names in select clause, need them to act like column name - SQL Server

I have a table:
T (Mon varchar(3));
INSERT INTO T VALUES ('Y');
Case 1:
DECLARE #day varchar(3) = 'Mon'
SELECT Mon
FROM T; --result : Y (no problem)
Case 2:
DECLARE #day VARCHAR(3) = 'Mon'
SELECT #day
FROM T; --result : Mon
If I want to get result Y in second case, how can I do that without re-designing the solution completely.
exec('select '+#day+' from #T;')
You can assign this #day variable using this command.
and dont forget to use "top 1" to limit only 1 row data to get.
SELECT top 1 #day = Mon FROM T;
then You can use #day in other queries.
Thanks for updating your question, I appreciate the simplicity.
EXECUTE/EXEC is a simple way of creating your script, allowing you to piece-meal the strings in variables and can be used with VARCHAR. Keep in mind that both EXEC and sp_executesql are not executed until runtime, so it will not be able to utilize cached query plans effectively or at all.
CREATE TABLE #Example (
Class_Type VARCHAR(2) NOT NULL
, Mary INT NULL
, Harry VARCHAR(100) NULL
, Larry XML NULL
, Sorry NVARCHAR(100) NULL )
INSERT INTO #Example (Class_Type, Mary, Harry, Larry, Sorry)
VALUES ('A', 250, 'Apples', CAST('<List><Class Professor="Harry">1</Class><Books>3</Books></List>' AS XML), N'Angelina')
, ('A', 300, 'Pineapples', CAST('<List><Class Professor="Larry">1</Class><Books>3</Books></List>' AS XML), N'Prince Charles')
, ('B', 15, 'Cucumbers', CAST('<List><Class Professor="Sarry">2</Class><Books>5</Books></List>' AS XML), N'Larry Cable')
GO
Here we use the EXECUTE/EXEC Method:
CREATE PROCEDURE dbo.USP_MyQuery (#Column NVARCHAR(255) )
AS
BEGIN
DECLARE #SQL VARCHAR(MAX)
SET #SQL = ' FROM #Example WHERE Class_Type = ''' + 'A' + ''''
SET #Column = RTRIM(LTRIM(#Column));
IF #Column IN (N'Mary', N'Harry', N'Larry', N'Sorry')
BEGIN
SET #SQL = 'SELECT ' + QUOTENAME(#Column, '[]') + #SQL;
EXEC (#SQL)
END
ELSE PRINT 'Check your spelling. Input does not match any existing columns'
END
GO
EXEC dbo.USP_MyQuery #Column = 'Mary'
sp_executesql is arguably the better method. Because the system procedure can paramertizes the entire statement (free from SQL Injection), SQL Server can reuse cached plans on the second query! For performance, safety, reability reasons and more, it is the preferred method of Dynamic SQL statements (when possible). However, note that all strings passed through must be in UNICODE format (NVARCHAR)
Here we use the sp_executesql system procedure
ALTER PROCEDURE dbo.USP_MyQuery (#Column NVARCHAR(255) )
AS
BEGIN
DECLARE #SQL NVARCHAR(100)
SET #SQL = N'SELECT ' + QUOTENAME(#Column) +
' FROM #Example WHERE Class_Type = ''' + 'A' + ''''
SET #Column = RTRIM(LTRIM(#Column))
IF #Column IN (N'Mary', N'Harry', N'Larry', N'Sorry')
BEGIN
EXEC sp_executesql #statement = #SQL
END
ELSE PRINT 'Check your spelling. Input does not match any existing columns'
END
EXEC dbo.USP_MyQuery #Column = 'Mary'
Not so hard, no? Likely you may want to use more testing and take advantage of functions like REPLACE() for making your query robust from human error.
Lastly, remember that QUOTENAME() removes system names identifiers, and instead replaces the string with a deliminator ('[]' is the default setting)

Column name not working when placed inside a variable in SQL Server [duplicate]

create procedure sp_First
#columnname varchar
AS
begin
select #columnname from Table_1
end
exec sp_First 'sname'
My requirement is to pass column names as input parameters.
I tried like that but it gave wrong output.
So Help me
You can do this in a couple of ways.
One, is to build up the query yourself and execute it.
SET #sql = 'SELECT ' + #columnName + ' FROM yourTable'
sp_executesql #sql
If you opt for that method, be very certain to santise your input. Even if you know your application will only give 'real' column names, what if some-one finds a crack in your security and is able to execute the SP directly? Then they can execute just about anything they like. With dynamic SQL, always, always, validate the parameters.
Alternatively, you can write a CASE statement...
SELECT
CASE #columnName
WHEN 'Col1' THEN Col1
WHEN 'Col2' THEN Col2
ELSE NULL
END as selectedColumn
FROM
yourTable
This is a bit more long winded, but a whole lot more secure.
No. That would just select the parameter value. You would need to use dynamic sql.
In your procedure you would have the following:
DECLARE #sql nvarchar(max) = 'SELECT ' + #columnname + ' FROM Table_1';
exec sp_executesql #sql, N''
Try using dynamic SQL:
create procedure sp_First #columnname varchar
AS
begin
declare #sql nvarchar(4000);
set #sql='select ['+#columnname+'] from Table_1';
exec sp_executesql #sql
end
go
exec sp_First 'sname'
go
This is not possible. Either use dynamic SQL (dangerous) or a gigantic case expression (slow).
Create PROCEDURE USP_S_NameAvilability
(#Value VARCHAR(50)=null,
#TableName VARCHAR(50)=null,
#ColumnName VARCHAR(50)=null)
AS
BEGIN
DECLARE #cmd AS NVARCHAR(max)
SET #Value = ''''+#Value+ ''''
SET #cmd = N'SELECT * FROM ' + #TableName + ' WHERE ' + #ColumnName + ' = ' + #Value
EXEC(#cmd)
END
As i have tried one the answer, it is getting executed successfully but while running its not giving correct output, the above works well
You can pass the column name but you cannot use it in a sql statemnt like
Select #Columnname From Table
One could build a dynamic sql string and execute it like EXEC (#SQL)
For more information see this answer on dynamic sql.
Dynamic SQL Pros and Cons
As mentioned by MatBailie
This is much more safe since it is not a dynamic query and ther are lesser chances of sql injection . I Added one situation where you even want the where clause to be dynamic . XX YY are Columns names
CREATE PROCEDURE [dbo].[DASH_getTP_under_TP]
(
#fromColumnName varchar(10) ,
#toColumnName varchar(10) ,
#ID varchar(10)
)
as
begin
-- this is the column required for where clause
declare #colname varchar(50)
set #colname=case #fromUserType
when 'XX' then 'XX'
when 'YY' then 'YY'
end
select SelectedColumnId from (
select
case #toColumnName
when 'XX' then tablename.XX
when 'YY' then tablename.YY
end as SelectedColumnId,
From tablename
where
(case #fromUserType
when 'XX' then XX
when 'YY' then YY
end)= ISNULL(#ID , #colname)
) as tbl1 group by SelectedColumnId
end
First Run;
CREATE PROCEDURE sp_First #columnname NVARCHAR(128)--128 = SQL Server Maximum Column Name Length
AS
BEGIN
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT ' + #columnname + ' FROM Table_1'
EXEC(#query)
END
Second Run;
EXEC sp_First 'COLUMN_Name'
Please Try with this.
I hope it will work for you.
Create Procedure Test
(
#Table VARCHAR(500),
#Column VARCHAR(100),
#Value VARCHAR(300)
)
AS
BEGIN
DECLARE #sql nvarchar(1000)
SET #sql = 'SELECT * FROM ' + #Table + ' WHERE ' + #Column + ' = ' + #Value
--SELECT #sql
exec (#sql)
END
-----execution----
/** Exec Test Products,IsDeposit,1 **/

sql server 2008 create columns using a while

I need to create about 300 columns for a table and I don't want to to it manually.
How can I do this?
I want to have columns like
Bigint1..to..Bigint60
Text1 ..to..Text60
and so on.
IF (NOT EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_SCHEMA = 'dbo'
AND TABLE_NAME = 'mytbl'))
begin
create table OBJ_AttributeValues(
ObjectID numeric(18,0) not null
);
end
else
begin
DECLARE #A INT
set #A = 1;
WHILE(#A <=60)
BEGIN
alter table OBJ_AttributeValues
add ...............................
set #A = #A+1;
END
end
What should I write instead of "..."?
You will need to use dynamic SQL for that, something like
DECLARE #SSQL VARCHAR(1000)
DECLARE #A INT
set #A = 1;
WHILE(#A <=60)
BEGIN
SET #SSQL = 'alter table OBJ_AttributeValues add Bigint' + CAST(#A as varchar) + ' bigint'
EXEC (#SSQL)
set #A = #A+1;
END
This isn't really a good idea, you should take the time to write the sql or just copy-paste the columns from Excel or something like that. You also shouldn't be using the TEXT data type, is deprecated and filled with restriction (use VARCHAR(MAX) instead if you need). That said, here is a way using dynamic SQL:
DECLARE #BigintCols NVARCHAR(MAX), #TextCols NVARCHAR(MAX)
DECLARE #Query NVARCHAR(MAX)
SET #BigintCols = ''
SET #TextCols = ''
SELECT #BigintCols = #BigintCols + N'Bigint' + CAST(number AS NVARCHAR(2)) + N' BIGINT,',
#TextCols = #TextCols + N'Text' + CAST(number AS NVARCHAR(2)) + N' TEXT,'
FROM master..spt_values
WHERE type = 'P'
AND number BETWEEN 1 AND 60
ORDER BY number
SET #Query = '
CREATE TABLE OBJ_AttributeValues(ObjectID numeric(18,0) not null,'+#BigintCols+
LEFT(#TextCols,LEN(#TextCols)-1)+')'
EXECUTE sp_executesql #Query
Oh, you should probably read about dynamic sql first.

Resources