SQL - string comparison ignores space - sql

This query:
SELECT CASE WHEN 'abc ' = 'abc' THEN 1 ELSE 0 END
Returns 1, even though 'abc ' clearly is not equal to 'abc'. Similarly,
SELECT CASE WHEN 'abc ' LIKE '%c' THEN 1 ELSE 0 END
Also returns 1. However, a very similar query:
SELECT * FROM #tempTable WHERE Name LIKE '%c'
Did not return a row where Name = 'abc '.
SQL Server 2008 R2, Windows 7 & 2008 R2, x64.

= ignores trailing space
len ignores training space
like does not ignore trailing space
SELECT CASE WHEN 'abc ' = 'abc' and DATALENGTH('abc ') = DATALENGTH('abc')
THEN 1 ELSE 0 END
You can assert DATALENGTH is not relevant but it is still the solution.

Turns out that the Name column was NVARCHAR (even though it contained ASCII characters only) and NVARCHAR behaves differently than VARCHAR:
SELECT CASE WHEN N'abc ' LIKE 'abc' THEN 1 ELSE 0 END
Returns 0, ditto for column instead of literal. The following does return 1 still:
SELECT CASE WHEN N'abc ' = 'abc' THEN 1 ELSE 0 END
So = and LIKE work differently, another peculiar difference.

If you need to compare things in this way but are restricted because your columns are of data type VARCHAR, something like this will basically fill the trailing space with an 'X' which will accomplish a failed comparison:
DECLARE #1 VARCHAR(5), #2 VARCHAR(5)
SET #1 = 'ABC '
SET #2 = 'ABC'
IF REPLACE(#1,' ','X') = REPLACE(#2,' ','X')
PRINT 'Equal'
ELSE
PRINT 'Not Equal'
Not rocket science, but at least a work around if you encounter a similar situation that you need to deal with :)

TA, I am not sure how you got zero by simply mentioning as unicode(N). I run your query and it giving me 1 only.
You can compare the LEN('abc ') and DATALENGTH('abc ') and can use those as per you requirement or you can replace the trailing space with some character to solve your problem.

I had a similar issue with a nvarchar column and wanted to fix the data so I did the following to find the data
select 'x' + username + 'x' from aspnet_users
where 'x' + username + 'x' <> 'x' + rtrim(username) + 'x'
The where clause compares the username as is with the trimmed version
'x1234 x' <> 'x1234x'
To fix the data I just did an update
update aspnet_Users
set username = rtrim(username)
where 'x' + username + 'x' <> 'x' + rtrim(username) + 'x'

Related

How to select strings in UUID format + case sensitive wildcards

How can I select only strings in the format XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX where X is any number or any UPPERcase (not lowercase) character?
Example dataset:
ed54cb09-b402-4551-912d-b8e0fec69d9e --I do not want to select this one
00029B19-80CC-4FF8-BE11-BDB55FC7FC2A --I do want to select this one
Some are all-caps, some are not, and this is a varchar field. I want to select only these all-caps UUIDs, excluding the rest.
Currently I'm using the _ wildcard to look for the basic UUID format, but looks like UPPER doesn't apply to the _ wildcard. Ex.:
SELECT mycolumn
FROM mytable t
WHERE t.mycolumn like UPPER('________-____-____-____-____________') COLLATE SQL_Latin1_General_Cp1_CS_AS
As mentioned, you can first check its all uppercase by comparing upper of your value with itself using a case sensitive collation. And then use your wildcard pattern (or a more precise one as shown below) to confirm the format is correct.
select
-- Precise check on allowed characters
case when upper(X.Test) = X.Test collate Latin1_General_CS_AI
and X.Test like '[A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9]-[A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9]-[A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9]-[A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9]-[A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9][A-Z0-9]' then 1 else 0 end
-- Loose check on allowed character, but definitely upper case
, case when upper(X.Test) = X.Test collate Latin1_General_CS_AI
and X.Test like '________-____-____-____-____________' then 1 else 0 end
-- Not sure why HABO deleted their answer, but the following works also
, case when upper(X.Test) = X.Test collate Latin1_General_CS_AI
and try_convert(uniqueidentifier,X.Test) is not null then 1 else 0 end
-- And combining Shmiel's and Charleface's suggestions gives
, case when X.Test like replicate('[A-F0-9]', 8) + '-' + replicate('[A-F0-9]', 4) + '-' + replicate('[A-F0-9]', 4) + '-' + replicate('[A-F0-9]', 4) + '-' + replicate('[A-F0-9]', 12) collate Latin1_General_100_BIN2 then 1 else 0 end
from (
values ('ed54cb09-b402-4551-912d-b8e0fec69d9e'), ('00029B19-80CC-4FF8-BE11-BDB55FC7FC2A')
) X (Test);
I have to echo Larnu's comment here, that it seems quite an odd requirement which might be better solved by a system change elsewhere.

Invalid argument for function integer IBM DB2

I need to filter out rows in table where numer_lini column has number in it and it is between 100 and 999, below code works just fine when i comment out line where i cast marsnr to integer. However when i try to use it i get error: Invalid character found in a character string argument of the function "INTEGER". when looking at the list seems like replace and translate filters only numbers just fine and select only contains legit numbers (list of unique values is not long so its easy to scan by eye). So why does it fail to cast something? I also tried using integer(marsnr), but it produces the same error. I need casting because i need numeric range, otherwise i get results like 7,80 and so on. As I mentioned Im using IBM DB2 database.
select numer_lini, war_trasy, id_prz1, id_prz2
from alaska.trasa
where numer_lini in (
select marsnr
from (
select
distinct numer_lini marsnr
from alaska.trasa
where case
when replace(translate(numer_lini, '0','123456789','0'),'0','') = ''
then numer_lini
else 'no'
end <> 'no'
)
where cast(marsnr as integer) between 100 and 999
)
fetch first 300 rows only
If you look at the optimized SQL from the Db2 explain, you will see that Db2 has collapsed your code into a single select.
SELECT DISTINCT Q2.NUMER_LINI AS "NUMER_LINI",
Q2.WAR_TRASY AS "WAR_TRASY",
Q2.ID_PRZ1 AS "ID_PRZ1",
Q2.ID_PRZ2 AS "ID_PRZ2",
Q1.NUMER_LINI
FROM ALASKA.TRASA AS Q1,
ALASKA.TRASA AS Q2
WHERE (Q2.NUMER_LINI = Q1.NUMER_LINI)
AND (100 <= INTEGER(Q1.NUMER_LINI))
AND (INTEGER(Q1.NUMER_LINI) <= 999)
AND (CASE WHEN (REPLACE(TRANSLATE(Q1.NUMER_LINI,
'0',
'123456789',
'0'),
'0',
'') = '') THEN Q1.NUMER_LINI
ELSE 'no' END <> 'no')
Use a CASE to force Db2 to do the "is integer" check first. Also, you don't check for the empty string.
E.g. with this table and data
‪create‬‎ ‪TABLE‬‎ ‪alaska‬‎.‪trasa‬‎ ‪‬‎(‪numer_lini‬‎ ‪VARCHAR‬‎(‪10‬‎)‪‬‎,‪‬‎ ‪war_trasy‬‎ ‪INT‬‎ ‪‬‎,‪‬‎ ‪id_prz1‬‎ ‪INT‬‎,‪‬‎ ‪id_prz2‬‎ ‪INT‬‎)‪;
insert into alaska.trasa values ('',1,1,1),('99',1,1,1),('500',1,1,1),('3000',1,1,1),('00300',1,1,1),('AXS',1,1,1);
This SQL works
select numer_lini, war_trasy, id_prz1, id_prz2
from alaska.trasa
where case when translate(numer_lini, '','0123456789') = ''
and numer_lini <> ''
then integer(numer_lini) else 0 end
between 100 and 999
Although that does fail if there is an embedded space in the input. E.g. '30 0'. To cater for that, a regular expressing is probably preferred. E.g.
select numer_lini, war_trasy, id_prz1, id_prz2
from alaska.trasa
where case when regexp_like(numer_lini, '^\s*[+-]?\s*((\d+\.?\d*)|(\d*\.?\d+))\s*$'))
then integer(numer_lini) else 0 end
between 100 and 999

SELECT CASE WHEN TINYINT field condition met return string

I am using SQL Server 2005. I am trying to do a select from a TINYINT field. If the field's value is 0 I want to select an empty string instead ' ' . The fields name is level. HEre is what I am doing:
SELECT
[Level] =
Case
t.[Level]
WHEN 0 THEN ' '
ELSE t.[Level]
END
FROM table t
This code always returns 0. I was trying to troubleshoot the issue and tried this:
SELECT
[Level] =
Case
t.[Level]
WHEN 0 THEN 'test'
ELSE t.[Level]
END
FROM table t
And I got a the error Conversion failed when converting the varchar value 'test' to data type tinyint
So I'm seeing that there is a conversion problem here. I've tried:
SELECT
[Level] =
Case
t.[Level]
WHEN 0 THEN CONVERT(VARCHAR,t.[level])
ELSE t.[Level]
END
FROM table t
But this of course still returns 0, just the character, so it's still not doing what I need. I am thinking that there is most likely a better way to do this but am not sure how to approach it. Could anyone give me some advice on how to handle this? Thanks much!
For CASE statements, all returned values must be of the same type (or automatically convertable); that's the reason why '' was working, but 'test' wasn´t.
SELECT [Level] =
Case t.[Level]
WHEN 0 THEN ' '
ELSE CONVERT(VARCHAR(3), t.[Level])
END
FROM table t
Your attempt to fix this went the wrong way; you needed to convert the else portion to a varchar to match the varchar empty string:
SELECT
[Level] =
Case
t.[Level]
WHEN 0 THEN ''
ELSE CAST(t.[Level] AS VARCHAR(10))
END
FROM table t

How can I generate ID with Prefix, Numeric Number and suffix?

I want to generate an ID in MSSQL Server 2008. Which will be Prefix + Numeric Number + suffix Like 'PV#000001#SV'. Which will be user defined (depends on configuration ) prefix, numeric length, suffix and starting number. Numeric number will be increased every time.
I tied to write this :
Blockquote
ALTER PROCEDURE [dbo].[spACC_SELECT_VOUCHER_NUMBER]
#COMPANY_ID uniqueidentifier,
#VOUCHER_TYPE INT
AS BEGIN
DECLARE #IS_AUTOMETIC BIT = (SELECT VOUCHER_CONFIG_NUMBERING_METHOD
FROM ACC_VOUCHER_CONFIG WHERE
ACC_VOUCHER_CONFIG.VOUCHER_CONFIG_VALUE=#VOUCHER_TYPE )
IF(#IS_AUTOMETIC=1)
BEGIN
SELECT CASE WHEN SUBSTRING(V.VOUCHER_CODE, 7, 23) IS NULL
THEN CASE WHEN VC.VOUCHER_CONFIG_PREFIX IS NULL THEN '' ELSE VC.VOUCHER_CONFIG_PREFIX END +
RIGHT ('0000000000000'+ CAST( VC.VOUCHER_CONFIG_BEGINING_NUMBER AS VARCHAR), VC.VOUCHER_CONFIG_NUMERIC_WIDTH) +
CASE WHEN VC.VOUCHER_CONFIG_SUFFIX IS NULL THEN '' ELSE VC.VOUCHER_CONFIG_SUFFIX END
ELSE CASE WHEN VC.VOUCHER_CONFIG_PREFIX IS NULL THEN '' ELSE VC.VOUCHER_CONFIG_PREFIX END +
RIGHT ('0000000000000'+ CAST((CAST( SUBSTRING(V.VOUCHER_CODE, 7, 23) AS INT)+1) AS VARCHAR), VC.VOUCHER_CONFIG_NUMERIC_WIDTH) +
CASE WHEN VC.VOUCHER_CONFIG_SUFFIX IS NULL THEN '' ELSE VC.VOUCHER_CONFIG_SUFFIX END
END AS VOUCHER_CODE FROM ACC_VOUCHER_CONFIG VC
LEFT OUTER JOIN ACC_VOUCHER V ON VC.VOUCHER_CONFIG_VALUE = V.VOUCHER_TYPE
WHERE VC.COMPANY_ID=#COMPANY_ID AND VC.VOUCHER_CONFIG_VALUE=#VOUCHER_TYPE
END
END
When I change the numeric length / suffix its not working.
Thanks
Nahid
For the six-digit number you're struggling with, add leading zeroes like this:
SELECT RIGHT('00000'+ CONVERT(VARCHAR,Num),6) AS NUM FROM your_table
Where Num is your sequential number.
This prepends 5 zeroes and then takes the right 6 characters from the resulting string.
A more detailed writeup of custom ID generation is here:
http://www.sqlteam.com/article/custom-auto-generated-sequences-with-sql-server
My suggestion would be to store just a number in the database (i.e. an int) and format the ID client side with tools that are better suited for it (i.e. a programming language that has sprintf or equivalent string formatting).

TSQL CASE LTRIM (RTRIM NULL

SQL Syntax is still something I am learning. I am getting the error noted below the this snippet of code.
SELECT
CASE WHEN LTRIM(RTRIM(cLehmanNo)) =' ' THEN NULL
WHEN cLehmanNo IS NOT NULL THEN REPLACE ( cLehmanNo,SUBSTRING (cLehmanNo,PATINDEX( '%[^a-zA-Z0-9 '''''']%',cLehmanNo),1), ' ' )
END asLOAN_NUMBER
,CASE WHEN LTRIM(RTRIM(cMERS)) =' ' THEN NULL
WHEN cMERS IS NOT NULL THEN REPLACE ( cMERS,SUBSTRING (cMERS,PATINDEX( '%[^a-zA-Z0-9 '''''']%',cMERS),1), ' ' )
END asMERS_ID
and 100+ more of same.
Msg 8133, Level 16, State 1, Line 1
None of the result expressions in a CASE specification can be NULL.
What am I doing wrong? How do I keep the gist of the statement and not get this crazy error?
This happens when it can't infer the type.
e.g.
SELECT CASE WHEN 1 = 2 THEN NULL ELSE NULL END
But this works
SELECT CASE WHEN 1 = 2 THEN NULL ELSE replace(NULL,'','') END
so I doubt the error is from the code you have shown us (You are using string functions and the following quick test shows that it will assume that to be varchar(8000))
SELECT CASE WHEN 1 = 2 THEN NULL ELSE REPLACE(NULL,'','') END a
INTO t /*Creates column of datatype varchar(8000)*/
You need to convert NULL to a correct type matching the overall values, e.g. CONVERT(VARCHAR(10), NULL), otherwise the server can't deduce which type to make the resulting value.
The error message actually means that all results in one of your case expressions are null. You have an expression like:
case when something then null when something then null end
At least one of the results has to be something other than null. You could circumvent this, but most likely there is a mistake in the query, as a case exression that always returns the same result is pointless.
The error message has been changed to:
At least one of the result expressions
in a CASE specification must be an
expression other than the NULL
constant.
SELECT
CASE WHEN LTRIM(RTRIM(cLehmanNo)) =' ' THEN NULL
WHEN cLehmanNo IS NOT NULL THEN REPLACE ( cLehmanNo,SUBSTRING (cLehmanNo,PATINDEX( '%[^a-zA-Z0-9 '''''']%',cLehmanNo),1), ' ' )
ELSE ''
END asLOAN_NUMBER
,CASE WHEN LTRIM(RTRIM(cMERS)) =' ' THEN NULL
WHEN cMERS IS NOT NULL THEN REPLACE ( cMERS,SUBSTRING (cMERS,PATINDEX( '%[^a-zA-Z0-9 '''''']%',cMERS),1), ' ' )
ELSE ''
END asMERS_ID