Like in CASE statement not evaluating as expected - sql

Given this data:
CREATE TABLE tmpTable(
fldField varchar(10) null);
INSERT INTO tmpTable
SELECT 'XXX'
UNION ALL
SELECT 'XXX'
UNION ALL
SELECT 'ZZZ'
UNION ALL
SELECT 'ZZZ'
UNION ALL
SELECT 'YYY'
SELECT
CASE WHEN fldField like 'YYY' THEN 'OTH' ELSE 'XXX' END AS newField
FROM tmpTable
The expected resultset is:
XXX
XXX
XXX
XXX
OTH
What situation would casue SQL server 2000 to NOT find 'YYY'? And return the following as the resultset:
XXX
XXX
XXX
XXX
XXX
The problem is with the like 'YYY', I have found other ways to write this to get it to work, but I want to know why this exact method doesn't work. Another difficulty is that it works in most of my SQL Server 2000 environments. I need to find out what is different between them to cause this. Thanks for your help.

Check your service pack. After upgrading my SQL 2000 box to SP4 I now get the correct values for your situation.
I'm still getting the swapped data that I reported in my earlier post though :(
If you do SELECT ##version you should get 8.00.2039. Any version number less than that and you should install SP4.

I ran the code on a SQL 2000 box and got identical results. Not only that, but when I ran some additional code to test I got some VERY bizarre results:
CREATE TABLE dbo.TestLike ( my_field varchar(10) null);
GO
CREATE CLUSTERED INDEX IDX_TestLike ON dbo.TestLike (my_field)
GO
INSERT INTO dbo.TestLike (my_field) VALUES ('XXX')
INSERT INTO dbo.TestLike (my_field) VALUES ('XXX')
INSERT INTO dbo.TestLike (my_field) VALUES ('ZZZ')
INSERT INTO dbo.TestLike (my_field) VALUES ('ZZZ')
INSERT INTO dbo.TestLike (my_field) VALUES ('YYY')
GO
SELECT
my_field,
case my_field when 'YYY' THEN 'Y' ELSE 'N' END AS C2,
case when my_field like 'YYY' THEN 'Y' ELSE 'N' END AS C3,
my_field
FROM dbo.TestLike
GO
My results:
my_field C2 C3 my_field
---------- ---- ---- ----------
N XXX N XXX
N XXX N XXX
Y YYY N YYY
N ZZZ N ZZZ
N ZZZ N ZZZ
Notice how my_field has two different values in the same row? I've asked some others at the office here to give it a quick test. Looks like a bug to me.

It worked as expected on my SQL 2005 installation. If it works on other machines, it sounds like you've got an environment difference. Try comparing your connection properties in SQL Server Management Studio for a connection that works and one that doesn't to see if you can figure out what the differences are.

I am an Oracle person, not a SQL*Server person, but it seems to me you should be either:-
SELECT
CASE WHEN fldField like '%YYY%' THEN
'OTH'
ELSE 'XXX'
END AS newField
FROM
tmpTable
or ...
SELECT
CASE WHEN fldField = 'YYY' THEN
'OTH'
ELSE 'XXX'
END AS newField
FROM
tmpTable
The second is the direction I'd go in, as at least in Oracle equality resolves quicker than like.

When you use LIKE without specifying any search criteria, it behaves like an = comparison. In your example, I would expect it to work properly. In your real data, you probably have a hidden (non-printable) character in your data (think about Carriage Return, Line Feed, Tab, etc....).
Take a look at this example...
Declare #tmpTable TABLE(
fldField varchar(10) null);
INSERT INTO #tmpTable
SELECT 'XXX'
UNION ALL
SELECT 'XXX'
UNION ALL
SELECT 'ZZZ'
UNION ALL
SELECT 'ZZZ'
UNION ALL
SELECT 'YYY'
UNION ALL
SELECT 'YYY' + Char(10)
SELECT CASE WHEN fldField like 'YYY' THEN 'OTH' ELSE 'XXX' END AS YourOriginalTest,
CASE WHEN fldField like 'YYY%' THEN 'OTH' ELSE 'XXX' END AS newField
FROM #tmpTable
You'll notice that the last piece of data I added is YYY and a Line Feed. If you select this data, you won't notice the line feed in the data, but it's there, so your LIKE condition (which is acting like an equal condition) doesn't match.
The common 'hidden' characters are Tab, Carriage Return, and Line Feed. To determine if this is causing your problem...
Select *
From Table
Where Column Like '%[' + Char(10) + Char(9) + Char(13) + ']%'

What a cute bug. I think I know the cause. If I'm right, then you'll get the results you expect from:
SELECT
CASE
WHEN fldField like 'YYY ' -- 7 spaces
THEN 'OTH'
ELSE 'XXX'
END as newField
from tmpTable
The bug is that varchar(10) is behaving like char(10) is supposed to. As for why it doesn't, you'll need to understand the old trivia question of how two strings with no metacharacters can be = but not LIKE each other.
The issue is that a char(10) is internally supposed to be space padded. The like operator does not ignore those spaces. The = operator is supposed to in the case of chars. Memory tells me that Oracle ignores spaces for strings in general. Postgres does some tricks with casting. I have not used SQL*Server so I can't tell you how it does it.

How about fldField = '%YYY%'?

By adding (%) to the expression , it will work fine.
SELECT
CASE
WHEN fldField like '%YYY%' THEN 'OTH'
ELSE 'XXX' END AS newField
END

You aren't specifying what you are selecting and checking the CASE against...
SELECT CASE fldField WHEN 'YYY'
THEN 'OTH' ELSE 'XXX' END AS newField FROM tmpTable

Related

Remove Punctuation in a field in Oracle SQL Developer

I have a field in table in Oracle SQL developer that I need to remove all punctuations in a specific field. The table name is "punctuationtest"
and the function that I tried does not work, I got error with the #InputString
Here is the function that I tried : any ideas and suggestions will be helpful thanks
CREATE FUNCTION dbo.fn_RemovePunctuation
(
#InputString VARCHAR(500)
)
RETURNS VARCHAR(500)
AS
BEGIN
SELECT
#InputString = REPLACE(#InputString, P.Symbol, '')
FROM
Punctuationtest P
RETURN #InputString
END
GO
"Oracle SQL Developer" is a tool you use to establish connection to a database and do something. Which database is it? Usually, it is Oracle. But, then again, code you posted certainly is NOT Oracle - hence error you got (at least, I think so).
If your database really is Oracle, then you could try with such a regular expression (see line #6):
SQL> with test (col) as
2 (select 'abc,!23' from dual union all
3 select '?xyz,.' from dual
4 )
5 select col,
6 regexp_replace(col, '[[:punct:]]') result
7 from test;
COL RESULT
------- ----------------------------
abc,!23 abc23
?xyz,. xyz
SQL>
Or, as you want to update a table, you'd
update your_table set
that_column = regexp_replace(that_column, '[[:punct:]]');

How to safely convert a varchar(255) to an int?

I was using a column in a case expression and it was working fine on server1. When I ran it on server2 it was failing because the column had the value 'false' in it.
The column is a varchar(255), but in my case expression I was using it as if it was an INT type. It worked fine but now it is failing because of the 'false' value in server2.
How can I safely convert to an INT, and if the conversion fails, default to 0.
Is this possible?
My query looks like:
UPDATE t1
set
c1 = ISNULL(
(
SELECT CASE c2
WHEN 123 then 'hello'
WHEN 234 then 'bye'
ELSE ''
END
)
, '')
FROM table1 as t1
There are quite a few ways to convert from numeric to varchar, but none of them are available to you (really, SQLServer2008 is disappointing because it's lacking just a few cool features that are nearly required).
In your case, the best way to do is it simply to convert your numeric expression to varchar like this :
UPDATE t1
set c1 = CASE c2
WHEN '123' then 'hello'
WHEN '234' then 'bye'
ELSE ''
END
FROM #t1 t1
It doesn't answer the question, but it solves your problem.
Use TRY_PARSE (SQL Server 2012+):
SELECT ISNULL(TRY_PARSE(column_name AS INT),0)
FROM your_table;
LiveDemo

Oracle/PL SQL/SQL null comparison on where clause

Just a question about dealing will null values in a query.
For example I have the following table with the following fields and values
TABLEX
Column1
1
2
3
4
5
---------
Column2
null
A
B
C
null
I'm passing a variableY on a specific procedure. Inside the procedure is a cursor like this
CURSOR c_results IS
SELECT * FROM TABLEX where column2 = variableY
now the problem is variableY can be either null, A, B or C
if the variableY is null i want to select all record where column2 is null, else where column2 is either A, B or C.
I cannot do the above cursor/query because if variableY is null it won't work because the comparison should be
CURSOR c_results IS
SELECT * FROM TABLEX where column2 IS NULL
What cursor/query should I use that will accomodate either null or string variable.
Sorry if my question is a bit confusing. I'm not that good in explaining things. Thanks in advance.
Either produce different SQL depending on the contents of that parameter, or alter your SQL like this:
WHERE (column2 = variableY) OR (variableY IS NULL AND column2 IS NULL)
Oracle's Ask Tom says:
where decode( col1, col2, 1, 0 ) = 0 -- finds differences
or
where decode( col1, col2, 1, 0 ) = 1 -- finds sameness - even if both NULL
Safely Comparing NULL Columns as Equal
You could use something like:
SELECT * FROM TABLEX WHERE COALESCE(column2, '') = COALESCE(variableY, '')
(COALESCE takes the first non NULL value)
Note this will only work when you the column content cannot be '' (empty string). Else this statement will fail because NULL will match '' (empty string).
(edit)
You could also consider:
SELECT * FROM TABLEX WHERE COALESCE(column2, 'a string that never occurs') = COALESCE(variableY, 'a string that never occurs')
This will fix the '' fail hypothesis.
Below is similar to "top" answer but more concise:
WHERE ((column2 = variableY ) or COALESCE( column2, variableY) IS NULL)
May not be appropriate depending on the data you're looking at, but one trick I've seen (and used) is to compare NVL(fieldname,somenonexistentvalue).
For example, if AGE is an optional column, you could use:
if nvl(table1.AGE,-1) = nvl(table2.AGE,-1)
This relies on there being a value that you know will never be allowed. Age is a good example, salary, sequence numbers, and other numerics that can't be negative. Strings may be trickier of course - you may say that you'll never have anyone named 'xyzzymaryhadalittlelamb" or something like that, but the day you run with that assumption you KNOW they'll hire someone with that name!!
All that said: "where a = b or (a is null and b is null)" is the traditional way to solve it. Which is unfortunate, as even experienced programmers forget that part of it sometimes.
Try using the ISNULL() function. you can check if the variable is null and if so, set a default return value. camparing null to null is not really possible. remember: null <> null
WHERE variableY is null or column2 = variableY
for example:
create table t_abc (
id number(19) not null,
name varchar(20)
);
insert into t_abc(id, name) values (1, 'name');
insert into t_abc(id, name) values (2, null);
commit;
select * from t_abc where null is null or name = null;
--get all records
select * from t_abc where 'name' is null or name = 'name';
--get one record with name = 'name'
You could use DUMP:
SELECT *
FROM TABLEX
WHERE DUMP(column2) = DUMP(variableY);
DBFiddle Demo
Warning: This is not SARG-able expression so there will be no index usage.
With this approach you don't need to search for value that won't exists in your data (like NVL/COALESCE).

Best practice to avoid code replication?

currently I have to change a view, a big part of the view code looks like this:
CAST('My_Tag_' + CASE
WHEN FieldX isnull
THEN ''
WHEN FieldX = '123'
THEN 'some_number'
WHEN FieldX = 'abc'
THEN 'some_text'
ELSE 'strange' ) AS VARCHAR(128) AS myField
)
Just a chunk of code, that puts together a string (the code itself doesn't even matter right now, I have like 50 other examples, where I have a lot of code replication). Now I have exact the same code for 30 more fields in the view, just the 'My_Tag_' and FieldX is changing. If this would be C#, I would just write a little helper function.
Of course I could write a function here, too. But as this is a bigger project with a lot of tables, views, etc, I would have hundreds of functions soon.
Now I am pretty new to SQL and normally my home is the OOP-world. But there has to be a solution to avoid code replication and to avoid having hundreds of helper functions in the database?
What's best practice in this case?
The best practice may be to create a user defined function.
The arguments would be the fields that change and it would return the intended value.
You can use a CTE to add a field to a table:
; with TableWithExtraField as
(
select case ... end as NewField
, table1
)
select NewField
from TableWithExtraField
Or a subquery also works:
select NewField
from (
select case ... end as NewField
, table1
) as TableWithExtraField
CREATE FUNCTION dbo.MyTag(#myfield VARCHAR(MAX))
RETURNS VARCHAR(128)
AS
BEGIN
RETURN CAST('My_Tag_' + CASE
WHEN #myfield IS NULL
THEN ''
WHEN #myfield = '123'
THEN 'some_number'
WHEN #myfield = 'abc'
THEN 'some_text'
ELSE 'strange' END AS VARCHAR(128))
)
END

SQL results operation question

I know I missasked the question, and that's probably the reason I can't find the solution myself:
How do I take a result in a stored procedure and and change the data such as
if column1 = AAA then
column1=Hello
else if column1 = BBB then
column1 = goodbye
and have the data return with the new values?
Thanks
Use the CASE statement:
SELECT CASE
WHEN column1 = 'AAA' THEN 'Hello'
WHEN column1 = 'BBB' THEN 'goodbye'
ELSE ... -- optional
END
these two queries:
UPDATE table_name SET column1='Hello' WHERE column1='AAA'
UPDATE table_name SET column1='goodbye' WHERE column1='BBB'
If you can get to the content of the stored procedure, you should change it to a table-valued function instead - if possible, an inline one (because that has a much higher chance of being able to be optimised).
This will let you query the results and change it as needed:
SELECT CASE column1
WHEN 'AAA' THEN 'Hello'
WHEN 'BBB' THEN 'goodbye'
END as NewColumn,
,*
FROM dbo.yourNewFunction() as t;
If you can't do this, and are very much stuck with a stored procedure, you could wrap it up in OPENROWSET:
SELECT CASE column1
WHEN 'AAA' THEN 'Hello'
WHEN 'BBB' THEN 'goodbye'
END as NewColumn,
, *
FROM OPENROWSET('SQLNCLI','Server=.;Trusted_Connection=Yes;','EXEC dbo.YourProc') as t;
This is a way to treat the output of a procedure as a table expression, but if you can do it without the procedure, that's better still.
you'll have to rename everything starting with my to your actual table names
CREATE PROCEDURE myProc
AS
BEGIN
UPDATE myTable
SET Column1 =
CASE
WHEN 'AAA' THEN 'Hello'
WHEN 'BBB' THEN 'goodbye'
END
WHERE Column1 in ('AAA','BBB')
OUTPUT myTable.myIDColumn, INSERTED.Column1
END
Updates the table and returns only the updated records once you
EXEC myProc
Reference
1. http://technet.microsoft.com/en-us/library/ms177564.aspx