Strange behavior with ISNULL() in MSSQL - sql

I have a select statement that is combining multiple segments of a persons name. This isn't anything new.
SELECT FirstName + ' ' + LastName AS FullName FROM MyTable
I then tried to add the middle initial to this and I came up with the following
SELECT FirstName + ' ' + ISNULL(MiddingInitial + ' ', '') + LastName AS FullName FROM MyTable
This appears to work, but during my testing of ISNULL(), I came across an odd behavior. I'm aware that NULL + 'any string' resolves to NULL. However this was just plain odd...
Here's my code, and what I get out as a result...
print '''' + isnull(null + 'Any String','Results in null') + ''''
print '''' + isnull(null + 'Any','Results in null') + ''''
print '''' + isnull(null + 'A','Results in null') + ''''
print '''' + isnull(null + '','Results in null') + ''''
/*
'Results in '
'Resu'
'Re'
'Re'
*/
Any idea of why this behavior occurs? Does it do the same for you?

It comes down to the datatypes you're working with and the behavior of the ISNULL function. Let's look at one example:
null + 'Any String'
The above fits perfectly into a varchar(11) datatype. The NULL (which is really just the result of char(0) and has length 1) and a regular 10-character string concatenated together makes for 11 characters total. The replacement string -- the second parameter to your ISNULL function -- is going to be forced to fit into a varchar(11), so it is truncated to 11 characters.
The pattern repeats for the remaining items, with a special case for the empty string.
If you don't want this to happen, use COALESCE, which instead of taking the datatype of the first item in the list, it uses data type precedence. A varchar(15) takes precedence over a varchar(11), so you will get the full replacement string:
print '''' + coalesce(null + 'Any String','Results in null') + ''''
print '''' + coalesce(null + 'Any','Results in null') + ''''
print '''' + coalesce(null + 'A','Results in null') + ''''
print '''' + coalesce(null + '','Results in null') + ''''
/*
'Results in null'
'Results in null'
'Results in null'
'Results in null'
*/

Related

sql-query, find and replace with this particular code

I need to understand the following SQL query and would like to ask if anybody could explain it to me a bit more in detail (like the xml path) as well as update it with a replace element?
So I want to find all values with BlaBlaBlaBla and replace them with HaHaHaHa instead. At the moment the query is finding all values of BlaBlaBlaBla only.
DECLARE #searchstring NVARCHAR(255)
SET #searchstring = '%BlaBlaBlaBla%'
DECLARE #sql NVARCHAR(max)
SELECT #sql = STUFF((
SELECT
' UNION ALL SELECT ''' + TABLE_SCHEMA + '.' + TABLE_NAME + ''' AS tbl, ''' + COLUMN_NAME + ''' AS col, [' + COLUMN_NAME + '] AS val' +
' FROM [' + TABLE_SCHEMA + '].[' + TABLE_NAME + '] WHERE [' + COLUMN_NAME + '] LIKE ''' + #searchstring + ''''
FROM INFORMATION_SCHEMA.COLUMNS
WHERE DATA_TYPE in ('nvarchar', 'varchar', 'char', 'ntext') FOR XML PATH('')) ,1, 11, '')
Exec (#sql)
I believe that the XML PATH is a trick to get the strings to all concatenate together.
You could change it to REPLACE with something like this:
SELECT #sql =
SELECT
' UPDATE ' + QUOTENAME(TABLE_SCHEMA) + '.' + QUOTENAME(TABLE_NAME) + '
SET ' + QUOTENAME(COLUMN_NAME) + ' = REPLACE(' + QUOTENAME(COLUMN_NAME) + ', ''' + #search_string + ''', ' + ', ''' + #replace_string + '''
WHERE ' + QUOTENAME(COLUMN_NAME) + ' LIKE ''' + #searchstring + ''''
FROM INFORMATION_SCHEMA.COLUMNS
WHERE
DATA_TYPE in ('nvarchar', 'varchar', 'char', 'ntext') FOR XML PATH('')
EXEC(#sql)
Some caveats:
I haven't tested this. When you're generating code like this it's very easy to make minor errors with all of the start and end quotes, etc. I would print out the SQL and check it, repeating as necessary until you get the output SQL correct.
Also, this is generally not a good idea. If your database is large and/or has a large number of tables then performance is going to be miserable. You should usually do the analysis of where you think this sort of data is going to appear and write code that will correct it as necessary. The fact that data elements are buried in strings throughout your data is concerning.
Finally, be aware that this might easily update additional data that you didn't intend to update. If you try to update "123" with "456" and there's a string out there that is "My ID is 1234" it's going to become "My ID is 4564".
BTW, the QUOTENAME function is a way of enclosing your table and column names in [ and ], but if the quote character is changed in a DB implementation it should still work.

not able to retrieve if the table column has null value

I have stored procedure to find customer, its working fine. If Customer_City_Name is null in the table then I am not able to retrieve the row. SP fails . how to do this
I have to get result even if Customer_City_Name or Customer_Country_Code IS NULL
EXEC findCustomer null,'%',null,null,
SP code:
CREATE PROCEDURE findCustomer
#customerNumber NVARCHAR(100),
#customerNamePattern NVARCHAR(35),
#customerCityNamePattern NVARCHAR(35),
#customerCountryCode NVARCHAR(5)
AS
BEGIN
DECLARE #SQL NVARCHAR(4000)
SET #SQL =
'
SELECT
c.Customer_Number,
c.Customer_Name,
c.Postal_Address_Identifier,
c.Customer_Street_Or_Road_Name,
c.Customer_City_Name,
c.Customer_Territory_Code,
c.Customer_Postal_Code,
c.Customer_Country_Code,
c.Telephone_Number,
c.Mobile_Telephone_Number,
c.Fax_Number,
c.Email_Address
FROM Customer c
WHERE c.Customer_Number LIKE ' +
CASE WHEN #customerNumber IS NOT NULL
THEN '''' + #customerNumber + ''''
ELSE 'c.Customer_Number'
END + '
AND c.Customer_Name LIKE ' +
CASE WHEN #customerNamePattern IS NOT NULL
THEN '''' + #customerNamePattern + ''''
ELSE 'c.Customer_Name'
END + '
AND c.Customer_City_Name LIKE ' +
CASE WHEN #customerCityNamePattern IS NOT NULL
THEN '''' +#customerCityNamePattern + ''''
ELSE 'c.Customer_City_Name'
END + '
AND c.Customer_Country_Code LIKE ' +
CASE WHEN #customerCountryCode IS NOT NULL
THEN '''' +#customerCountryCode + ''''
ELSE 'c.Customer_Country_Code'
END
EXEC sp_executesql #SQL
#user2218371 you should reconsider alternatives instead of dynamic SQL. anyway, this is an alternative code.
CREATE PROCEDURE findCustomer
#customerNumber NVARCHAR(100),
#customerNamePattern NVARCHAR(35),
#customerCityNamePattern NVARCHAR(35),
#customerCountryCode NVARCHAR(5)
AS
BEGIN
DECLARE #SQL NVARCHAR(4000)
SET #SQL =
'
SELECT
c.Customer_Number,
c.Customer_Name,
c.Postal_Address_Identifier,
c.Customer_Street_Or_Road_Name,
c.Customer_City_Name,
c.Customer_Territory_Code,
c.Customer_Postal_Code,
c.Customer_Country_Code,
c.Telephone_Number,
c.Mobile_Telephone_Number,
c.Fax_Number,
c.Email_Address
FROM Customer c
WHERE ' +
CASE WHEN #customerNumber IS NOT NULL
THEN ' c.Customer_Number LIKE ''' + #customerNumber + '''' +
CASE WHEN #customerNamePattern IS NOT NULL THEN ' AND ' ELSE '' END
ELSE ''
END +
CASE WHEN #customerNamePattern IS NOT NULL
THEN ' c.Customer_Name LIKE ''' + #customerNamePattern + '''' +
CASE WHEN #customerCityNamePattern IS NOT NULL THEN ' AND ' ELSE '' END
ELSE ''
END +
CASE WHEN #customerCityNamePattern IS NOT NULL
THEN ' c.Customer_City_Name LIKE ''' + #customerCityNamePattern + '''' +
CASE WHEN +#customerCountryCode IS NOT NULL THEN ' AND ' ELSE '' END
ELSE ''
END +
CASE WHEN #customerCountryCode IS NOT NULL
THEN ' c.Customer_Country_Code LIKE ''' + #customerCountryCode + ''''
ELSE ''
END
EXEC sp_executesql #SQL
When one of the fields is NULL, your WHERE clause looks like this:
WHERE c.Customer_Number LIKE c.Customer_Number
This is clearly intentional, since it is part of the CASE statements.
However, LIKE doesn't return true for a pair of NULL values. (NULL is not LIKE NULL.) This is pretty typical behavior for NULL. = does the same thing.
See this very simple SQL Fiddle for a demonstration: http://sqlfiddle.com/#!3/82a23/3/0.
Assuming I'm reading this correctly, you want to simply ignore the column's value when the procedure argument is NULL. In that case, you will need to adjust your query to leave out the clause entirely or put some always true expression in its place when the argument is NULL.
If you insist on sticking with your dynamic SQL building function (which you should not), try this for your CASE statements:
WHERE ' +
CASE WHEN #customerNumber IS NOT NULL
THEN 'c.Customer_Number LIKE ''' + #customerNumber + ''''
ELSE '0=0'
END + '
Null values cant be compared with any other value, that means you don't get a True or a False.
To work around this issue I use the ISNULL built in function that checks if the value is null, and if it is returns your default value.
You should apply this function to each column where a null value is an option.
http://msdn.microsoft.com/en-us/library/ms184325.aspx
brother, so your query mainly is to perform select function to search for existing customer info
try to modify your where condition as sample below
Select ....
From .....
WHERE (#Customer_Number is null or c.Customer_Number like #Customer_Number + '%')
AND (#customerNamePattern is null or c.Customer_Name like #customerNamePattern + '%' )
AND (#customerCityNamePatternis null or c.Customer_City_Name like #customerCityNamePattern+ '%' )
AND (#customerCountryCode is null or c.c.Customer_Country_Code like #customerCountryCode + '%' )
I have fixed this issue by doing this
AND ISnull(c.Customer_City_Name,'''') LIKE ' +
CASE WHEN #customerCityNamePattern IS NOT NULL
THEN '''' +#customerCityNamePattern + ''''
ELSE 'isnull (c.Customer_City_Name,'''')'
END + '
AND Isnull(c.Customer_Country_Code,'''') LIKE ' +
CASE WHEN #customerCountryCode IS NOT NULL
THEN '''' +#customerCountryCode + ''''
ELSE 'isnull(c.Customer_Country_Code , '''')'
END
1.Thanks to asafrob i have accepted your approach.
2. Thanks to devio In 'LIKE' operator needs to select 'NULL'(DB) values I got idea from
3.Thanks to jpmc26 -- > I like your answer .
Finally thanks to every one answered to my question

Stored Procedure with Dynamic Query

I am writing a stored procedure using dynamic SQL.
In my procedure, I have say some 10 tables of similar columns.
For example if I consider Designation & Department tables, Designation table has these columns:
Designation, Code, EntryBy, EntryOn, ModifiedBy, ModifiedOn
and Department table has these columns:
Department, Code, EntryBy, EntryOn, ModifiedBy, ModifiedOn
and similarly I have some eight other tables.
In my stored procedure, I need to update and insert data into all the tables. So, instead of writing update & insert statements for each table, I am using a stored procedure which accepts the table name as a parameter and checks if the row already exists in that table.
If that row is present, then that record will be updated otherwise that record will be inserted into the respective table.
ALTER PROC UpdateMasterItems
(
#MasterTypeTmp varchar(50),
#NameTmp varchar(50),
#CodeTmp varchar(10))
AS
BEGIN
DECLARE #CntTmp numeric(2,0)
EXEC('select count(*)' + #CntTmp + ' from ' + #MasterTypeTmp + ' where ' + #MasterTypeTmp + ' = ' + #NameTmp)
IF(#CntTmp > 1)
BEGIN
EXEC('UPDATE ' + #MasterTypeTmp + ' SET ' + 'Code = ' + #CodeTmp + ', ModifiedBy = CURRENT_USER, MOdifiedOn = CURRENT_TIMESTAMP WHERE' + #MasterTypeTmp + ' = ' + #NameTmp)
RETURN 10
END
ELSE
BEGIN
EXEC('INSERT INTO ' + #MasterTypeTmp + '(' + #MasterTypeTmp + ', Code, EntryBy, EntryOn, ModifiedBy, ModifiedOn ) VALUES (' + #NameTmp + ',' + #CodeTmp + ',' + 'CURRENT_USER, CURRENT_TIMESTAMP, CURRENT_USER, CURRENT_TIMESTAMP )')
RETURN 11
END
END
where #MasterTypeTmp is the table name(can be Department/Designation.....)
I am getting an error while executing the procedure:
Exec Statement:
EXEC UpdateMasterItems 'Designation', 'TestName', 'TestCode'
Error Statements:
Invalid column name 'TestName'.
Invalid column name 'TestCode'.
But TestName & TestCode are not column names. Those are the values for the columns. Please let me know if my dynamic queries are wrong or where the problem is!
Thanks in advance
Mounika
I think the error message is because you have wrapped column names with ' (ie; 'Designation' is wrong). Should be Designation
But there are other issue as well.
I think you cannot define variables out side dynamic sql and assign them inside. Because dynamic sql runs in a different session and therefore variables defined outside the scope are unknown. (ie; #CntTmp)
Even thought you are checking #CntTmp > 1 you are not really assigning count(*) value to it (won't work anyway due to reason 1)
Your #CntTmp will overflow if the record count > 99 (not really an issue if you don't have bad data)
If you need to get this work the way you have described, you have to declare variables, check the existance of records and then update/insert all within the same dynamic query. You could do without a variable using if exists (select ....) update ... else insert...
Your string concatenation are missing some ' and the db interpret the values as column names:
EXEC('INSERT INTO ' + #MasterTypeTmp + '(' + #MasterTypeTmp + ', Code, EntryBy, EntryOn, ModifiedBy, ModifiedOn )
VALUES (''' + #NameTmp + ''',''' + #CodeTmp + ''',' + 'CURRENT_USER, CURRENT_TIMESTAMP, CURRENT_USER, CURRENT_TIMESTAMP )')
First, this part will give you an error: Error converting data type varchar to numeric.
DECLARE #CntTmp numeric(2,0)
EXEC('select count(*)' + #CntTmp + ' from ' + #MasterTypeTmp + ' where ' + #MasterTypeTmp + ' = ' + #NameTmp)
Because the CntTmp is an numeric and can't directly used in that expression.
If you change to this:
EXEC('select count(*)' + cast(#CntTmp as varchar(30)) + ' from ' + #MasterTypeTmp + ' where ' + #MasterTypeTmp + ' = ' + #NameTmp)
It will give you an error because you can't use variable directly in dynamic SQL.
Also, it will not give you the output, because CntTmp is null.
So, you can create another variable to store the result from cast the numeric to varchar and then perform ISNULL function to give the variable a value if it's null.
Second, you're missing ' for your column value.
And here is the working stored procedures:
ALTER PROC UPDATEMASTERITEMS
( #MASTERTYPETMP VARCHAR(50), #NAMETMP VARCHAR(50), #CODETMP VARCHAR(10))
AS
BEGIN
DECLARE #CNTTMP NUMERIC(2,0)
DECLARE #CNTTMPVAL VARCHAR(30) = ISNULL(CAST(#CNTTMP AS VARCHAR(30)) , '')
EXEC ('SELECT COUNT(*) ' + #CNTTMPVAL + ' FROM ' + #MASTERTYPETMP + ' WHERE ' + #MASTERTYPETMP + ' = ''' + #NAMETMP + '''')
IF(#CNTTMP > 1)
BEGIN
EXEC('UPDATE ' + #MASTERTYPETMP + ' SET ' + 'CODE = ''' + #CODETMP + ''', MODIFIEDBY = CURRENT_USER, MODIFIEDON = CURRENT_TIMESTAMP WHERE' + #MASTERTYPETMP + ' = ''' + #NAMETMP + '')
RETURN 10
END
ELSE
BEGIN
EXEC('INSERT INTO ' + #MASTERTYPETMP + '(' + #MASTERTYPETMP + ', CODE, ENTRYBY, ENTRYON, MODIFIEDBY, MODIFIEDON ) VALUES (''' + #NAMETMP + ''',''' + #CODETMP + ''',' + 'CURRENT_USER, CURRENT_TIMESTAMP, CURRENT_USER, CURRENT_TIMESTAMP )')
RETURN 11
END
END
Anyway, you can format your SQL using this

Using special characters in SQL as string

Using SQL Server 2008
DECLARE #myVariable nvarchar (500)
SET #myVariable = 'select distinct b.*,v.vertrag_id,v.VersicherungsscheinNummer
from CRM_Wifo_GmbH.dbo.vertrag_168 v,temp_universa b
where v.VersicherungsscheinNummer like '%' + b.vsnr + '%
and v.gesellschaft_id in('59','66')'
I have to set the value of this type in a variable. How could I do this? Is it possible? USING ' ' sign in a string?
You just need to escape the single quote ' using 2 single quotes instead ''
DECLARE #myVariable nvarchar (500)
SET #myVariable =
N'select distinct b.*,v.vertrag_id,v.VersicherungsscheinNummer
from CRM_Wifo_GmbH.dbo.vertrag_168 v,temp_universa b
where v.VersicherungsscheinNummer like ''%'' + b.vsnr + ''%
and v.gesellschaft_id in(''59'',''66'')'
I am also using N', so that I can span the string on multiple lines
Alternative solutions :
DECLARE #myVariable nvarchar (500)
SET #myVariable = 'select distinct b.*,v.vertrag_id,v.VersicherungsscheinNummer from CRM_Wifo_GmbH.dbo.vertrag_168 v,temp_universa b where v.VersicherungsscheinNummer like ' + char(39) + '%' + char(39) + ' + b.vsnr + ' + char(39) + '% and v.gesellschaft_id in(' + char(39) + '59' + char(39) + ',' + char(39) + '66' + char(39) + ')'
But i suggesst you, using 2 single quotes.

How Do I Escape Apostrophes in Field Valued in SQL Server?

I asked a question a couple days ago about creating INSERTs by running a SELECT to move data to another server. That worked great until I ran into a table that has full on HTML and apostrophes in it. What's the best way to deal with this? Lucking there aren't too many rows so it is feasible as a last resort to 'copy and paste'. But, eventually I will need to do this and the table by that time will probably be way too big to copy and paste these HTML fields.
This is what I have now:
select 'Insert into userwidget ([Type],[UserName],[Title],[Description],[Data],[HtmlOutput],[DisplayOrder],[RealTime],[SubDisplayOrder]) VALUES ('
+ ISNULL('N'''+Convert(varchar(8000),Type)+'''','NULL') + ','
+ ISNULL('N'''+Convert(varchar(8000),Username)+'''','NULL') + ','
+ ISNULL('N'''+Convert(varchar(8000),Title)+'''','NULL') + ','
+ ISNULL('N'''+Convert(varchar(8000),Description)+'''','NULL') + ','
+ ISNULL('N'''+Convert(varchar(8000),Data)+'''','NULL') + ','
+ ISNULL('N'''+Convert(varchar(8000),HTMLOutput)+'''','NULL') + ','
+ ISNULL('N'''+Convert(varchar(8000),DisplayOrder)+'''','NULL') + ','
+ ISNULL('N'''+Convert(varchar(8000),RealTime)+'''','NULL') + ','
+ ISNULL('N'''+Convert(varchar(8000),SubDisplayOrder)+'''','NULL') + ')'
from userwidget
Which is works fine except those pesky apostrophes in the HTMLOutput field. Can I escape them by having the query double up on the apostrophes or is there a way of encoding the field result so it won't matter?
You can replace the single apostrophe with double apostrophes.
ISNULL('N'''+ REPLACE(Convert(varchar(8000),Type), '''', '''''') + '''','NULL') + ','
You should use parameters instead of injecting values into your SQL query.
Use QUOTENAME function
declare #valueAsNull as varchar(10)
set #valueAsNull = quotename('NULL','''')
SELECT 'Insert into userwidget ([Type],[UserName],[Title],[Description],[Data],[HtmlOutput],[DisplayOrder],[RealTime],[SubDisplayOrder]) VALUES (' +
REPLACE(QUOTENAME(Convert(varchar(8000),ISNULL(Type,'NULL'),''''), #valueAsNull,'NULL') + ', '
REPLACE(QUOTENAME(Convert(varchar(8000),ISNULL(Username,'NULL'),''''), #valueAsNull,'NULL') + ', '
REPLACE(QUOTENAME(Convert(varchar(8000),ISNULL(Title,'NULL'),''''), #valueAsNull,'NULL') + ', '
REPLACE(QUOTENAME(Convert(varchar(8000),ISNULL(Description,'NULL'),''''), #valueAsNull,'NULL') + ', '
REPLACE(QUOTENAME(Convert(varchar(8000),ISNULL(Data,'NULL'),''''), #valueAsNull,'NULL') + ', '
REPLACE(QUOTENAME(Convert(varchar(8000),ISNULL(HTMLOutput,'NULL'),''''), #valueAsNull,'NULL') + ', '
REPLACE(QUOTENAME(Convert(varchar(8000),ISNULL(DisplayOrder,'NULL'),''''), #valueAsNull,'NULL') + ', '
REPLACE(QUOTENAME(Convert(varchar(8000),ISNULL(RealTime,'NULL'),''''), #valueAsNull,'NULL') + ', '
REPLACE(QUOTENAME(Convert(varchar(8000),ISNULL(SubDisplayOrder,'NULL'),''''), #valueAsNull,'NULL') + ')'
FROM userwidget