Get all data except the value on the first case statement - sql

I have a simple select like this:
DECLARE #ReportType AS VARCHAR(255) = 'Machine'
SELECT
[WOCust3] AS [Machine]
FROM MyTable WHERE
(#ReportType IS NULL
OR [WOCust3] LIKE (CASE WHEN #ReportType = 'Slack'
THEN '%Slack test%'
ELSE '%'
END
))
As you can see, I use the case to compare if the variable equals Slack then filter by that field; the problem is in the else clause. When the report type equals to another string it is throwing all results including Slack test results, and I want to get all except for slack test results, how can I achieve that?

I re-worked your CASE expression to better handle instances where the #ReportType is not Slack.
SQL:
DECLARE #ReportType AS VARCHAR(255) = 'Machine'
SELECT
a.Machine_Name
FROM
Machine_Data a
WHERE
a.Machine_Name IN (
CASE
WHEN #ReportType = 'Slack' AND a.Machine_Name LIKE '%Slack test%' THEN a.Machine_Name
WHEN ISNULL(#ReportType, 'N/A') <> 'Slack' AND a.Machine_Name NOT LIKE '%Slack test%' THEN a.Machine_Name
END)
Result with Machine or NULL as ReportType:
| Machine_Name |
|--------------|
| Machine 1 |
| Machine 2 |
| Other |
| Test |
| Dev |
Result with Slack as ReportType:
| Machine_Name |
|--------------|
| Slack test |
SQL Fiddle:
http://sqlfiddle.com/#!18/ca614/13

You can break the case expression down into these pieces:
where case
when #ReportType is NULL then 1
when #ReportType = 'Slack' and WOCust3 like '%Slack test%' then 1
when #ReportType <> 'Slack' and WOCust3 not like '%Slack test%' then 1
else 0 end = 1;
Note that performance is likely to suffer with this sort of logic.
Suggested reading: Dynamic Search Conditions in T-SQL.

Related

Create a custom column based on the values of three other columns

In this project I'm currently working on (I'm building a bridge between a desktop app and a new e-shop) there is a products table that has some spare columns defined that can be used for whatever reason the end user might need some custom data to be stored into.
So, the user needed to set a true/false flag to determine whether the products would appear in three different sliders... Unfortunately, the person who implemented this, didn't even use the same type of spare columns... So,
Slider1's flag is stored in a varchar(50) column
Slider2's flag is stored in a float column
Slider3's flag is stored in a float column
Additionally I ran a SELECT DISTINCT <column> for each one of them to get an idea of the actual data stored in each column and got the following results:
The varchar column has the following data stored in it:
FLDSTRING1
NULL
''
0
1
194276400456
The float column has the following data stored:
FLDFLOAT5
NULL
0
1
And the other float column has this:
FLDFLOAT6
NULL
1
Also, I ran the following query to find the different combinations of the data stored for each column:
SELECT FLDSTRING1, FLDFLOAT5, FLDFLOAT6
FROM MATERIAL
GROUP BY FLDSTRING1, FLDFLOAT5, FLDFLOAT6
and got the following combinations...
FLDSTRING1
FLDFLOAT5
FLDFLOAT6
NULL
NULL
NULL
NULL
NULL
1
NULL
0
NULL
NULL
1
NULL
NULL
1
1
''
NULL
NULL
''
NULL
1
0
NULL
NULL
0
0
NULL
1
NULL
NULL
1
NULL
1
1
0
NULL
1
1
NULL
1
1
1
194276400456
0
NULL
What I need after all this introduction...
I want a concatenated string of three comma-separated values like this
NEWPROD for when FLDSTRING1 would evaluate to true - anything not NULL, 0, or ''
CUSTOM1 for when FLDFLOAT5 would evaluate to true - basically the value 1
CUSTOM2 for when FLDFLOAT6 would evaluate to true - again value 1
After some trial and error I managed to bring this to a point that it kind of works, in the sense that it brings the correct values, not comma-separated though...
SELECT
FLDSTRING1, FLDFLOAT5, FLDFLOAT6,
CONCAT(CASE WHEN ISNULL(FLDSTRING1, '') = '' THEN '' ELSE 'NEWPROD' END,
CASE WHEN ISNULL(FLDFLOAT5, '') = '' THEN '' ELSE 'CUSTOM1' END,
CASE WHEN ISNULL(FLDFLOAT6, '') = '' THEN '' ELSE 'CUSTOM2' END) AS TAGS
FROM
MATERIAL
GROUP BY
FLDSTRING1, FLDFLOAT5, FLDFLOAT6;
FLDSTRING1
FLDFLOAT5
FLDFLOAT6
TAGS
NULL
NULL
NULL
NULL
NULL
1
CUSTOM2
NULL
0
NULL
NULL
1
NULL
CUSTOM1
NULL
1
1
CUSTOM1CUSTOM2
''
NULL
NULL
''
NULL
1
CUSTOM2
0
NULL
NULL
NEWPROD
0
0
NULL
NEWPROD
1
NULL
NULL
NEWPROD
1
NULL
1
NEWPRODCUSTOM2
1
0
NULL
NEWPROD
1
1
NULL
NEWPRODCUSTOM1
1
1
1
NEWPRODCUSTOM1CUSTOM2
194276400456
0
NULL
NEWPROD
Problem #1 is I don't quite understand how this works... I mean, value 0 isn't '', but still for the combination of NULL 0 NULL I get an empty value, which is what I wanted... But how does it do that?
And also, can someone update my final query to comma-separate the calculated TAGS column? Problem #2 is that I don't want it to contain just two commas, like ,,, when the combination wouldn't justify any of the three values to appear... It should work like PHP's implode() works...
To help you help me with this, I'm including a fiddle with the setup of the scenario I describe here... Thanks in advance!
Since you are using SQL Server 2014, instead of CONCAT_WS you may try STUFF as shown below. By prepending the delimiter , before all strings ,the STUFF will remove the first comma found.
SELECT
FLDSTRING1,
FLDFLOAT5,
FLDFLOAT6,
STUFF(
CONCAT(
CASE WHEN FLDSTRING1 IS NULL OR FLDSTRING1 IN ('0','') THEN '' THEN '' ELSE ',NEWPROD' END,
CASE WHEN FLDFLOAT5 IS NULL THEN '' ELSE ',CUSTOM1' END,
CASE WHEN FLDFLOAT6 IS NULL THEN '' ELSE ',CUSTOM2' END
),
1,1,''
) AS TAGS
FROM #MATERIAL
GROUP BY FLDSTRING1, FLDFLOAT5, FLDFLOAT6;
View working demo db fiddle
Let me know if this works for you.
Use the CONCAT_WS() function to concat values into a comma (or other separator) separated list, which ignores nulls.
To use CONCAT_WS(), you want to pass it a true NULL if the value is "blank" (by your definition), otherwise your custom label:
SELECT DISTINCT
FLDSTRING1,
FLDFLOAT5,
FLDFLOAT6,
CONCAT_WS(',',
CASE WHEN FLDSTRING1 IS NULL OR FLDSTRING1 = '' OR FLDSTRING1 = '0' THEN NULL ELSE 'NEWPROD' END,
CASE WHEN FLDFLOAT5 IS NULL OR FLDFLOAT5 = 0 THEN NULL ELSE 'CUSTOM1' END,
CASE WHEN FLDFLOAT6 IS NULL OR FLDFLOAT6 = 0 THEN NULL ELSE 'CUSTOM2' END) AS TAGS
FROM MATERIAL
Replaced GROUP BY with DISTINCT because it's simpler and (here) achieves the same thing.
If CONCAT_WS is not available:
SELECT DISTINCT
FLDSTRING1,
FLDFLOAT5,
FLDFLOAT6,
REPLACE(REPLACE(REPLACE(CONCAT(
CASE WHEN FLDSTRING1 IS NULL OR FLDSTRING1 = '' OR FLDSTRING1 = '0' THEN 'X' ELSE 'NEWPROD' END,
',',
CASE WHEN FLDFLOAT5 IS NULL OR FLDFLOAT5 = 0 THEN 'X' ELSE 'CUSTOM1' END,
',',
CASE WHEN FLDFLOAT6 IS NULL OR FLDFLOAT6 = 0 THEN 'X' ELSE 'CUSTOM2' END
), ',X', ''), 'X,', ''), 'X', '') AS TAGS
FROM MATERIAL
See dbfiddle.

How to write case statments

I have two tables(ABC,CDE) in my Netezza. I need to write the following list of case statement to display value.
Tables and Columns:
Table name ABC:
Column: (Id(PK), Desc)
Table name CDE:
Columns: (value(like 1, 2, 3), ID(FK))
select case when value Is Not Null then 'CALIFORNIA' else ID end
from CDE
NZ displays pg atoi (I think it is unable to do the conversion for ID to varchar)
How to write CASE using NZ?
SELECT
CASE
WHEN value IS NOT NULL THEN 'CALIFORNIA'
ELSE TO_CHAR(ID)
END
FROM CDE;
Do this:
CAST(Id AS VARCHAR(50))
or
| Function | Return Type | Description |
======================================================================
| to_char(int, text) | text | converts integer to string |
Try this:
SELECT
CASE
WHEN value IS NOT NULL THEN 'CALIFORNIA'
ELSE to_char(ID, text)
END
FROM CDE;

Using ISNULL in where Clause doesn't return records with NULL field

Table:
ID AppType AppSubType Factor
1 SC CD 1.0000000000
2 SC CD 2.0000000000
3 SC NULL 3.0000000000
4 SC NULL 4.0000000000
Query:
declare #ast varchar(10)
set #ast = null
select *
from tbl
where AppType = 'SC' and AppSubType = ISNULL(#ast, AppSubType)
Result:
ID AppType AppSubType Factor
1 SC CD 1.0000000000
2 SC CD 2.0000000000
Question:
Shouldn't this query return all 4 records and not just the first 2?
Abviously #ast is null and Isnull would exchange null with other value, so you shouldn't expect #ast to be not null. If your AppSubType is null , so the result become null but AppSubType=null doesn't mean because AppSubType is null is true. Because null is not a value so it cant work with equal.
for your expected result this code will work.
declare #ast varchar(10)
set #ast = null
select *
from tbl
where AppType = 'SC' and (AppSubType = ISNULL(#ast, AppSubType) Or AppSubType is null)
You can write a case condition in where clause as:
declare #ast varchar(10)
set #ast = null
select *
from tbl
where AppType = 'SC' and 1=
case when isnull(#ast ,'') = '' and isnull(AppSubType ,'') = '' then 1
when AppSubType = ISNULL(#ast, AppSubType) then 1
else 0
end
Please understand the behavior of ISNULL explained in below blog.
the very first expression in the isnull function is a column value or the expression of some result.
ISNULL Explored in Detail
The code looks like a search functionality. If the value is not given then replace them with whatever is there in database and if given pull only those records which are matched.

How to create a ternary condition on a bit field in T-SQL

I have a SQLExpress table that includes a bit field for storing TRUE/FALSE state.
Something like:
+----+---------+
| ID | IsAlive |
+----+---------+
| 1 | 1 |
| 2 | 0 |
| 3 | NULL |
| 4 | 1 |
+----+---------+
Using that table as our example, I want to create one Stored Procedure that will do any one of the following:
Retrieve all records.
Retrieve only the records with IsAlive=1.
Retrieve only the records with IsAlive=0 or NULL.
I am trying to think of how I can create my query without having to write IF/ELSE conditions - It seems to me there is a better/cleaner way than to do something like this:
-- The ternary logic...
-- 0 or NULL retrieves records where IsAlive = 0 or NULL
-- 1 retrieves records where IsAlive = 1
-- Otherwise return all records
-- sproc .....
#IsAlive tinyint = 2 -- Return all records by default
AS
BEGIN
IF(#SentToNTService = 0 OR #SentToNTService = 1)
BEGIN
SELECT *
FROM MyTable
WHERE IsAlive = #IsAlive;
END
ELSE -- Lame redundancy
BEGIN
SELECT *
FROM MyTable
END
END
Is there another way of creating the same results without having to create two different queries as I did above?
2 suggestions of how to do this:
Assuming your variable #isalive is declared as 'bit' as well (which is should be)
SELECT * FROM #t
WHERE #isalive is null or #isalive = coalesce(isalive, 0)
If you want to use a 'bit compare' solution that doesn't require #isalive to be 'bit' (it will work for bit as well as tinyint)
SELECT * FROM #t
WHERE coalesce((1-coalesce(isalive, 0)) ^ #isalive, 1) > 0
Second solution is for nerds like me. Some hardcore people may find it interesting (or at least amusing) as I think it offer the best possible performance (please, someone correct me if i am wrong). It is a powerful solution but hard to read.
This will do what you want:
SELECT *
FROM MyTable
WHERE COALESCE(IsAlive, 0) = COALESCE(#IsAlive, COALESCE(IsAlive, 0))
Based on the value of #IsAlive:
If NULL, then will return everything (because the condition is always true)
If 1, then will return those rows with IsAlive = 1
If 0, then will return those rows with IsAlive = 0 or NULL
COALESCE is a function that returns it's first argument, unless it's NULL, in which case it returns its second argument.
So the LHS returns 0 if IsAlive is NULL or 0 and 1 if IsAlive is 1.
The RHS returns the same when the stored procedure argument #IsAlive is NULL and just returns the #IsAlive argument otherwise.
EDIT:
This assumed that #IsAlive is BIT. In the case of tinyint you can add a case statement:
SELECT *
FROM MyTable
WHERE COALESCE(IsAlive, 0) = CASE #IsAlive
WHEN 0 THEN 0
WHEN 1 THEN 1
ELSE COALESCE(IsAlive, 0)
END
try this:
SELECT * FROM MyTable WHERE ISNULL (IsAlive, 0) = ISNULL (#IsAlive, 0)
UNION
SELECT * FROM MyTable WHERE ISNULL (#IsAlive, 0) > 1
This isnt exact, but pretty close to what you can do:
SELECT *
FROM MyTable
WHERE CASE #IsAlive
WHEN 0 THEN IsAlive = #IsAlive
WHEN 1 THEN IsAlive = #IsAlive
ELSE 1=1 --dummy true value, when null or anything else
END
Something like this should also work.
SELECT *
FROM MyTable
WHERE (#IsAlive = 0 and IsAlive=0)
OR (#IsAlive =1 and IsAlive =1)
OR (#IsAlive is null)

Order by and Different Types in a CASE

I have a requirement to order a result set by a column, based on an Integer input into a parameter.
Problem is, I need to use a CASE for the OrderBy, and it seems the code accepts the first 'TYPE' in the case column... any other types fail.
My code is like this:
WITH error_table AS
(
SELECT Row_Number() OVER
(ORDER BY
CASE #orderBy
WHEN 1 THEN received_date -- Last Rx'd message
WHEN 2 THEN message_id -- Message Id
WHEN 3 THEN zibmat.short_name -- Message action type
WHEN 4 THEN error_action.short_name -- Status type
WHEN 5 THEN ime.[allocated_date] -- Allocated Date
ELSE received_date
END) AS RowNumber
,ime.[ijis_message_error_id]
,ime.[message_id]
,ime.[message_version]
So, when OrderBy is 1, it works. It sorts by rx_date... but when I sent it a 2, it fails with a data time conversion error.
It looks like all the types must be the same...
Sending a 5 works fine, as that's a date time too.
Is there a way I can fix this?
A CASE statement must resolve to only one data type. This is regardless of the fact that you know that #orderby will choose only one branch and it will be a particular data type.
You could use something like this, which would be clunky but will work.
ORDER BY
CASE #orderBy WHEN 1 THEN received_date -- Last Rx'd message
WHEN 2 THEN 0
WHEN 3 THEN 0
WHEN 4 THEN 0
WHEN 5 THEN ime.[allocated_date] -- Allocated Date
ELSE received_date END,
CASE #orderBy WHEN 1 THEN 0
WHEN 2 THEN message_id -- Message Id
WHEN 3 THEN 0
WHEN 4 THEN 0
WHEN 5 THEN 0
ELSE 0 END,
CASE #orderBy WHEN 1 THEN ''
WHEN 2 THEN ''
WHEN 3 THEN zibmat.short_name -- Message action type
WHEN 4 THEN error_action.short_name -- Status type
WHEN 5 THEN ''
ELSE '' END
This also appears to work.
ORDER BY
CASE #orderBy
WHEN 1 THEN CAST(received_date AS SQL_VARIANT) -- Last Rx'd message
WHEN 2 THEN message_id -- Message Id
WHEN 3 THEN zibmat.short_name -- Message action type
WHEN 4 THEN error_action.short_name -- Status type
WHEN 5 THEN ime.[allocated_date] -- Allocated Date
ELSE received_date
END
Test case (int, string, date)
DECLARE #P INT = Datepart(SECOND, Getdate())%3;
SELECT 'Sorting By ' + CASE #P
WHEN 1 THEN 'modify_date'
WHEN 2 THEN 'object_id'
ELSE 'name'
END
SELECT object_id,
name,
modify_date
FROM sys.objects
ORDER BY CASE #P
WHEN 1 THEN CAST(modify_date AS SQL_VARIANT)
WHEN 2 THEN object_id
ELSE name
END
I think the following one would be more cleaner option (similar to RichardTheKiwi's approach)
WITH error_table AS
(
SELECT ROW_NUMBER() OVER
(
ORDER BY
CASE WHEN #orderBy = 1 THEN received_date END,
CASE WHEN #orderBy = 2 THEN message_id END ,
CASE WHEN #orderBy = 3 THEN zibmat.short_name END,
CASE WHEN #orderBy =4 THEN error_action.short_name END,
CASE WHEN #orderBy =5 THEN ime.[allocated_date] END
) AS RowNumber
,ime.[ijis_message_error_id]
,ime.[message_id]
--..other columns...
-- FROM YOURTABLE
)