How to check IS NULL in dynamic query in sql server - sql-server

I did following store procedure using dynamic query, see the following blueprint of code
ALTER PROCEDURE [dbo].[usp_Report] (
#LocationId INT = NULL
,#UserId INT = NULL)
DECLARE #miscquery NVARCHAR (MAX);
begin
SET #miscquery='
SELECT
,A.AgreementNumber AS Contract
,A.AgreementId
FROM tblAgreement A
WHERE
AND (A.IsDeleted = 0 or A.IsDeleted is null)
AND (
(
' + convert(NVARCHAR(30), #LocationId) + ' IS NULL
AND (
A.CheckoutLocation IN (
SELECT LocationId
FROM [dbo].[tblUserLocations]
WHERE UserID = ' + convert(VARCHAR(10), #userId) +'
AND IsDeleted = 0
)
OR A.CheckoutLocation IS NULL
)
)
OR A.CheckoutLocation = ' + convert(VARCHAR(10), #LocationId) +'
)'
EXEC (#miscquery)
end
)
here when i execute the query with #Locationid is null, results doesn't return table, it returns like following
(63 rows affected)
(2325 rows affected)
please help me. thank you

The code you have there cannot be your actual code, firstly because right at the start you try to set a variable called #miscquery before you declare it. There's also no reason for this code to be dynamic, so it's clear you're doing some other stuff as well.
I will take it as a given that for some reason you "need" dynamic SQL. I will put in the standard warning about sanitising your inputs. That was it.
OK. Even if #miscquery had been declared, the code as written will not produce any results. It will either throw a syntax error, or do nothing, depending on your setting for concat_null_yields_null.
Let's take the likely case: you have the default setting for this, which means that when you concatenate a varchar to null, the result is null.
Observe the following code:
declare #locationId int = null;
select 'this sentence ends with...' + convert(nvarchar(30), #locationId);
What will be the output?
"This sentence ends with... null"
"This sentence ends with..."
null
The answer is 3. And notice: that's not a string with the value "null". That's just null. When you convert the null value to a string, you don't get a string with the value "null", you get the null value.
OK, so now we try to add null to the end of our string. When you try to concatenate the null value to a string with the + operator, the entire result is null.
Therefore, your entire #miscquery variable is null at the end of the assignment.
So what you are then doing is the same as this:
declare #myquery varchar(max) = null;
exec sp_executesql #myquery
Which is valid, and doesn't throw any error, but does nothing. Then the rest of your code does whatever it does, and produces the results you see.
if concat_null_yields_null was set to off then you would get a syntax error, because the resulting text would not be valid SQL when you handed it to sp_executesql.
As Dan said, the best and safest solution would be to parameterize your inputs. But putting that aside, the solution you need would look something like the following. In the iif() function, I look at the value of #locationId. If it is null, then I want the string "null". If it is not null, then I want the string conversion of whatever value it has.
declare #locationId int = null;
declare #query varchar(max) =
'select * from mytable where '
+ iif(#locationId is null, 'null', convert(nvarchar(30), #locationId))
+ ' is null';
print #query;

Related

SQL Server RIGHT function returns an error with no null or empty values

Following SQL Server function works fine with simple examples but when it is passed a column that has millions of records, it gives the error shown below:
Question: What could be a cause of the problem, and it can be fixed?
Function:
Create FUNCTION MyFunction(#var varchar(300))
RETURNS varchar(300)
AS
BEGIN
IF LEN(ISNULL(#var, '')) = 0
return #var
declare #varLen tinyint = LEN(#var)
SET #var = CASE
WHEN LEFT(#var,4) = 'abc ' THEN RIGHT(#var,#varLen-4)
WHEN LEFT(#var,4) = 'abc.' THEN RIGHT(#var,#varLen-4)
WHEN LEFT(#var,3) = 'rs ' THEN RIGHT(#var,#varLen-3)
WHEN LEFT(#var,4) = 'rs. ' THEN RIGHT(#var,#varLen-4)
ELSE #var
END
RETURN #var
END
Simple query [correctly returns the expected output]:
declare #t varchar(150) = 'abc ewotiu ryire'
select dbo.TestFunction(#t) --returns "ewotiu ryire"
Sample Query that gives an error:
select myColumn MyFunction(myColumn) from myTable
REMARKS:
myColumn has millions of records, has no null values, there are about 10 empty values (blanks ''). But I have taken care of that scenario in the first line of the code above. I have tested that when I pass null or blank string, it correctly returns null or empty string.
I have also tried using DATALENGTH function instead of LEN, but still the exact same error
Using Azure SQL Db: Microsoft SQL Azure (RTM) - 12.0.2000.8 Sep 18 2021 19:01:34 Copyright (C) 2019 Microsoft Corporation
Online search (such as this or this etc.) did not help - probably my issue is a bit different having to do with millions of records or something else.
Error: [When passed to a query with million of records ]
Msg 536, Level 16, State 2
Invalid length parameter passed to the RIGHT function.
You will see this error if you pass 'abc ' or 'rs ' to your function as LEN does not count trailing spaces.
So for 'rs ' the expression RIGHT(#var,#varLen-3) ends up as RIGHT('rs ',2-3) and -1 is an invalid length.
You can use the following instead to just specify the desired start position directly instead of calculating it with LEN
CREATE OR ALTER FUNCTION MyFunction(#var VARCHAR(300))
RETURNS VARCHAR(300)
AS
BEGIN
RETURN CASE WHEN #var LIKE 'abc %' OR #var LIKE 'abc.%' OR #var LIKE 'rs. %' THEN SUBSTRING(#var, 5, 300)
WHEN #var LIKE 'rs %' THEN SUBSTRING(#var, 4, 300)
ELSE #var
END
END
The reason for switching to LIKE is so that trailing spaces also become significant in the string comparison part. Otherwise 'rs' and 'rs ' will also compare equal (which would also give the error in your original function and needs to be guarded against to give the desired results in the rewrite).
to illustrate your problem:
declare #a varchar(300)
set #a = 'abc'
if LEFT(#a,4) = 'abc '
select LEN(#a)-4
-----------
-1
(1 row(s) affected)
To illustrate one more problem in your code
declare #a varchar(300)
set #a = REPLICATE('A',300)
declare #varLen tinyint = LEN(#a)
Msg 220, Level 16, State 2, Line 4
Arithmetic overflow error for data type tinyint, value = 300.

How to hold values in variable in sql server

I need to fetch max length of First_name and put it into #sq i am getting an error.
Declare #sq nvarchar(max)
Set #sq=''
SELECT MAX(LEN(FIRST_NAME)) FROM #table1
Drop table #t
SELECT CASE WHEN LEN(SEQ_NUM) = 0 THEN NULL ELSE SEQ_NUM END AS REC_NUM,
CASE WHEN LEN(FIRST_NAME) = 0 THEN NULL ELSE CONVERT(CHAR(Select #sq)),RTRIM(UPPER(FIRST_NAME))) END AS FIRST_NAME
into #t
from #tabel1
There's a few problem with this SQL. If we start with the first statement:
Set #sq=''SELECT MAX(LEN(FIRST_NAME)) FROM #table1
You have a couple of wayward single quotes here ('); not sure what they're doing. Secondly, if you're assigning a variables value from a dataset, the syntax is SELECT {Variable} = {expression} [,{Variable} = {expression} [,...]] FROM {etc} Thus you get:
SELECT #sq = MAX(LEN(FIRST_NAME))
FROM #table1;
The next statement, well, that's a mess. Firstly, there's also a wayward right parenthesis ()) here: RTRIM(UPPER(FIRST_NAME))) There should only be 2.
The expression CONVERT(CHAR(Select #sq)) is very wrong. CONVERT require 2 parameters, but only has one, and CHAR would return an character for the appropriate number provided. I.e. CHAR(65) returns A. I suspect you mean CONVERT(char,#sq) (you should really be declaring a length here!), however, #sq is already an nvarchar(max) (which is also pointless, as it's being assigned the value of an int). Thus I literally have no idea what you're trying to achieve here.
You need to assign the variable, like this:
Declare #sq int --LEN returns an int, not a varchar!
SELECT #sq = MAX(LEN(FIRST_NAME))
FROM #table1

ISNULL wildcard '%' search returning no records

As one of the criteria of a WHERE clause in a search query in SQL Server, I'm trying to search by courseId using ISNULL.
When the query is executed with all null values except for #currentUserId, as below, the query returns no records.
My SQL:
DECLARE #currentUserId int = '6'
DECLARE #searchString nvarchar(MAX) = NULL
DECLARE #courseId nchar(10) = NULL
DECLARE #dateFrom date = NULL
DECLARE #dateTo date = NULL
SELECT [questionId],[Question].[moduleId],[questionDate],[questionTopic]
FROM [dbo].[Question],[dbo].[Module],[dbo].[ModuleCourse]
WHERE [lecturerId] = #currentUserId AND
([dbo].[Question].[moduleId] = [dbo].[Module].[moduleId] AND
[dbo].[Module].[moduleId] = [dbo].[ModuleCourse].[moduleId]) AND
([dbo].[Question].[questionTopic] LIKE ISNULL('%' + #searchString + '%','%') OR
[dbo].[Question].[questionText] LIKE ISNULL('%' + #searchString + '%','%')) AND
[dbo].[ModuleCourse].[courseId] = ISNULL(#courseId,'%') AND
([dbo].[Question].[questionDate] >= ISNULL(#dateFrom,(SELECT MIN([questionDate])
FROM [dbo].[Question])) AND
[dbo].[Question].[questionDate] <= ISNULL(#dateTo,(SELECT(GETDATE()))))
When the line
[dbo].[ModuleCourse].[courseId] = ISNULL(#courseId,'%') AND
is commented out, results return as expected.
([dbo].[ModuleCourse].[courseId] is PK; nchar(10))
It seems as though the wildcard % does not work in this situation; I have no idea why.
Please help...
If #courseId is null, you will be comparing the courseId to the string '%'. The = operator doesn't use wildcards.
You can use like instead of =, or write the condition as:
(#courseId is null or [dbo].[ModuleCourse].[courseId] = #courseId) AND

Using PATINDEX to remove characters

I am using a function that I found here and else where on the internet to try and strip illegal characters from a field.
Create Function [epacube].[StripSpecs](#myString varchar(500),
#invalidChars varchar(100)) RETURNS varchar(500) AS Begin
While PatIndex(#invalidChars, #myString) > 0
Set #myString = Stuff(#myString, PatIndex(#invalidChars, #myString), 1, '')
Return #myString End
in my table I have set my field value to be: set DATA_NAME = 'Pro$d)uc^t'
If I run this query:
SELECT epacube.StripSpecs (
DATA_NAME
,'%$%') FROM TABLE_DATA
It works and I get a value returned of Prod)uc^t
However, if I add another special character, it no longer works:
SELECT epacube.StripSpecs (
DATA_NAME
,'%$)%') FROM TABLE_DATA
returns my original value Pro$d)uc^t
Does anyone have any suggestion for accomplishing what I need to do?
EDIT
As per the answer below here is the code that worked:
Create Function [epacube].[StripSpecs](#myString varchar(500), #invalidChars varchar(100))
RETURNS varchar(500) AS
Begin
While PatIndex('%[' + #invalidChars + ']%', #myString) > 0
Set #myString = Stuff(#myString, PatIndex('%[' + #invalidChars + ']%', #myString), 1, '')
Return #myString
End
As with LIKE, if you want to specify that one of a set of characters should match, use [] to enclose the set.
SELECT epacube.StripSpecs (
DATA_NAME
,'%[$)]%') FROM TABLE_DATA
Although, personally, given the descriptions of the function and parameters, I'd add the %[ and ]% in StripSpecs, and let the caller just give a list of characters (if you don't want to support any other type of pattern being specified)

Using variable in SQL LIKE statement

I've got a sproc (MSSQL 2k5) that will take a variable for a LIKE claus like so:
DECLARE #SearchLetter2 char(1)
SET #SearchLetter = 't'
SET #SearchLetter2 = #SearchLetter + '%'
SELECT *
FROM BrandNames
WHERE [Name] LIKE #SearchLetter2 and IsVisible = 1
--WHERE [Name] LIKE 't%' and IsVisible = 1
ORDER BY [Name]
Unfortunately, the line currently running throws a syntax error, while the commented where clause runs just fine. Can anyone help me get the un-commented line working?
If you are using a Stored Procedure:
ALTER PROCEDURE <Name>
(
#PartialName VARCHAR(50) = NULL
)
SELECT Name
FROM <table>
WHERE Name LIKE '%' + #PartialName + '%'
Joel is it that #SearchLetter hasn't been declared yet? Also the length of #SearchLetter2 isn't long enough for 't%'. Try a varchar of a longer length.
As Andrew Brower says, but adding a trim
ALTER PROCEDURE <Name>
(
#PartialName VARCHAR(50) = NULL
)
SELECT Name
FROM <table>
WHERE Name LIKE '%' + LTRIM(RTRIM(#PartialName)) + '%'
But in my opinion one important thing.
The "char(number)" it's lenght of variable.
If we've got table with "Names" like for example [Test1..Test200] and we declare char(5) in SELECT like:
DECLARE #variable char(5)
SET #variable = 'Test1%'
SELECT * FROM table WHERE Name like #variable
the result will be only - "Test1"! (char(5) - 5 chars in lenght; Test11 is 6 )
The rest of potential interested data like [Test11..Test200] will not be returned in the result.
It's ok if we want to limit the SELECT by this way.
But if it's not intentional way of doing it could return incorrect results from planned
( Like "all Names begining with Test1..." ).
In my opinion if we don't know the precise lenght of a SELECTed value, a better solution could be something like this one:
DECLARE #variable varchar(max)
SET #variable = 'Test1%'
SELECT * FROM <table> WHERE variable1 like #variable
This returns (Test1 but also Test11..Test19 and Test100..Test199).
This works for me on the Northwind sample DB, note that SearchLetter has 2 characters to it and SearchLetter also has to be declared for this to run:
declare #SearchLetter2 char(2)
declare #SearchLetter char(1)
Set #SearchLetter = 'A'
Set #SearchLetter2 = #SearchLetter+'%'
select * from Customers where ContactName like #SearchLetter2 and Region='WY'
DECLARE #SearchLetter2 char(1)
Set this to a longer char.
We can write directly too...
DECLARE #SearchLetter CHAR(1)
SET #SearchLetter = 'A'
SELECT *
FROM CUSTOMERS
WHERE CONTACTNAME LIKE #SearchLetter + '%'
AND REGION = 'WY'
or the following way as well if we have to append all the search characters then,
DECLARE #SearchLetter CHAR(1)
SET #SearchLetter = 'A' + '%'
SELECT *
FROM CUSTOMERS
WHERE CONTACTNAME LIKE #SearchLetter
AND REGION = 'WY'
Both these will work
I had also problem using local variables in LIKE.
Important is to know: how long is variable.
Below, ORDER_NO is 50 characters long, so You can not use: LIKE #ORDER_NO, because in the end will be spaces.
You need to trim right side of the variable first.
Like this:
DECLARE #ORDER_NO char(50)
SELECT #ORDER_NO = 'OR/201910/0012%'
SELECT * FROM orders WHERE ord_no LIKE RTRIM(#ORDER_NO)
It may be as simple as LIKE '%%[%3]%%' being [%3] the input variable.
This works for me with SAP B1 9.1
I ran into a similar problem. I needed to use just a small piece of a URL saved in my database where the front and ends were irrelevant.
I first attempted to use:
DECLARE #variable VARCHAR(250) = %x%;
SELECT * FROM tblone WHERE column1 LIKE '#variable'
However, this returned the error:
Arithmetic overflow error converting numeric to data type varchar
My working query was formatted:
DECLARE #variable VARCHAR(1000) = x;
SELECT * FROM tblone WHERE column1 LIKE '%'+#variable+'%'

Resources