Simple CASE expression in SQL - sql

I am new to SQL. I am trying to practice writing CASE expressions. Below is a query I have been working with.
SELECT bill,
'provider' as
case
when refer != '' THEN refer
WHEN render != '' THEN render
ELSE 'NULL'
END
FROM billing
This is the criteria for my query -
1) I need a new column in the select that is not part of the table. I have named it provider in the above query.
2) I need the new column's value to be the refer column's value if refer is not empty.
3) I need it to be equal to the render column's value if render is not empty.
4) I need it to be NULL if both are empty.
5) The output should look like
Bill Provider
123 Health
456 Org
789 NULL

The correct syntax is:
SELECT bill,
(CASE WHEN refer <> '' THEN refer
WHEN render <> '' THEN render
END) as provider
FROM billing;
Notes:
The column alias comes after the definition.
Although != works, <> is the tradition comparison operator for not equals.
Do not use single quotes for column aliases. Only use them for string and date constants.

You've already got a fine answer, but I figured I'd mention a few other commands to investigate while you're learning about CASE. They may not apply to your current problem, but you'll likely find over time that FILTER and COALESCE are equally worth knowing about. FILTER often works as a simpler-to-read alternative to CASE. Check it out while you're CASE, and you'll have another option for future problems. Here's a short write-up you might like:
https://medium.com/little-programming-joys/the-filter-clause-in-postgres-9-4-3dd327d3c852
I use FILTER for manually constructed pivot tables, and it's much simpler to construct and review in that situation.
COALESCE you may already know about. But, if not, it's super handy. Pass in a list of possible values, and get back the first one (reading left-to-right) that's not null. That can sometimes be what you need where you would otherwise have to write a CASE.
https://www.postgresql.org/docs/current/functions-conditional.html

Related

SQL CASE How do I correct assign a value?

First, I would like to say I have already spent the due time and diligence on my part by watching videos, using other sources, and reading other posts and answers on SOF before asking this and have been unable to find a solution.
The issue I am running into, in a particular case, is a certain type is being passed in, which would require the use of LIKE as the specific type itself will not match anything as three types use the one type, say 'painting' in this situation. The database has a 'painting small' and 'painting large.'
Code
// I tried this
CASE WHEN type = 'painting' THEN inventory.type LIKE '%'+type+'%' ELSE inventory.type = type END
I keep running into the "An expression of a non-boolean type specified in a context where a condition is expected. There are a few other variations I have tried as well as IF ELSE, however, I run into the same issue. Someone else may be able to word this question better.
I mainly want to be pointed in the right direction and receive clarification on what I am doing wrong.
Thank you
There are a few problems with your query. Rather than the CASE expression itself I'm going to address the less obvious problem, your lack of prefixing. Take this clause:
inventory.type LIKE '%'+type+'%'
This could likely either error, due to an ambiguous column name, or resolve to inventory.type LIKE '%'+inventory.type+'%'; obviously the latter is going to always be true unless the column type has the value NULL. Always prefix your column names, especially when your query contains 2+ tables.
As for the actual problem, this is presumably part of a WHERE, therefore use OR and AND logic:
WHERE (({Other Table Prefix}.[type] = 'painting' AND inventory.[type] LIKE '%' + {Other Table Prefix}.[Type] + '%')
OR ({Other Table Prefix}.[type] != 'painting' AND inventory.[type] = {Other Table Prefix}.[Type]))
Obviously, you need to appropriately replace {Other Table Prefix} with the correct prefix.
The problem seems to be in the
LIKE '%'+type+'%'
where LIKE may be returning a boolean value.

Avoid calling COUNT twice in CASE expression (PostgreSQL)

Inside a larger query, I have to COUNT a variable, then if it is larger than 1, have the count as a string otherwise an empty string:
CASE COUNT(measurement.id) > 1 THEN to_char(COUNT(measurement.id),' 999') ELSE ''
I'm afraid this is slow because I use COUNT() twice. Is there a better way?
This expression:
CASE COUNT(measurement.id) > 1 THEN to_char(COUNT(measurement.id),' 999') ELSE ''
is not slow because COUNT() is called twice. The hard work of aggregating the data is the part where the key values are brought together. The individual aggregation functions are generally not particularly expensive (there are exceptions such as COUNT(DISTINCT)). So, even if it were being called multiple times, this would not be an issue.
You could change the query to something more cryptic like:
coalesce(to_char(nullif(count(measurement.id), 0), '999')), '')
This takes a count of 0, converts it to NULL, which is then turned into an empty string (and I think it would only evaluate the argument once in Postgres, although SQL Server would evaluate it twice, in which case you use isnull() instead of coalesce()). I much prefer your version, if you feel the need to convert nice numbers into strings.
EDIT:
COUNT() appears to be defined as "immutable" which is even stronger than "stable". I'm not even sure if this is correct, but that is the case on SQL Fiddle. In any case, it probably isn't called twice, but the expensive part is the GROUP BY anyway.

SQL isnull error

While trying to do a select query using the isnull which, i've tried in 2 differents servers that are identical one to the other. (They both use the same procedure, dll, return page, they just change from one ip to the other)
SELECT * FROM
ITEM_TEST
WHERE ITEM_NAME = isnull(#ITEM_TESTE, ITEM_NAME)
The operation is working without problems in one of the servers, returning all options when the #ITEM_TESTE is NULL, while in the other, it returns ONLY the ones that are NOT NULL.
I'm using a sybase-based-application (version 12.5) called SQLdbx (version 3.14)
Case it's not so openly understood, #ITEM_TESTE is a variable given from the user that is optional, meaning it can be null where the ITEM_NAME accepts a STRING to it, while it's also option the ITEM_TEST is a table with more than 10 variables, i'm simplifing it. This search, however, want's all the possibles results even if ITEM_NAME is UNKOWN while using others variables to narrow down the search. (I thought about creating a search with an IF condition that excluded ITEM_NAME and it worked, but the it made the search so "laggy" due to perfomance issues.)
EDIT
Change the name of the variables to make it less confusing (both with the same name) and added an explaining for easier understanding
Also, due to copyright issues that i can't post the exact code here.
This is your where clause:
WHERE ITEM_TESTE = isnull(#ITEM_TESTE, ITEM_TESTE)
This where clause will never be true when ITEM_TESTE is NULL, because NULL = NULL evaluates to not true in the SQL world.
Presumably, you want:
WHERE (ITEM_TEST = #ITEM_TESTE OR #ITEM_TESTE IS NULL)
The way it was explained to me and has forever stuck after all of these years, is that NULL is not nothing, it is unknown, so you cannot use an equality check to verify two things you know nothing about are equally nothing. IS is checking that they are in the same unknown state, which has nothing to do with a value.
So as the others have said = NULL will never work, because = implies value comparison.

Whitespace in SSRS

I've got a part of a query that I wrote in SSRS that doesn't work like I need it to. The situation is that I'm trying to create a drop-down parameter with a list of values from a column - we'll call it Column5. The problem is that some of the rows in that column are blank; they're not null, they're just made up entirely of whitespace. Because of this, they're not showing up in the list of values for the parameter, which means those rows will be completely ignored.
What I'm trying to do is something like this:
SELECT [...stuff...] CASE WHEN (RTRIM(LTRIM(Column5)) = '') THEN 'None' ELSE Column5 END AS ColumnAlias
I've tried the above and a few variations on it, but nothing seems to work.
NB: I pasted the above query into SQL Server, and it worked just fine. Seems like SQL Server and SSRS deal with whitespace differently.
EDIT: Apparently part of the problem is that SQL won't filter based on a renamed column. I pasted my query into SQL Server, and included WHERE Column5 = 'None'. It didn't return any rows, even though there were a few thousand rows that clearly said 'None' in Column5. It seems like I might have to rethink my whole approach.
Try this:
SELECT [...stuff...] CASE WHEN LEFT(Column5, 1) = ' ' or RIGHT(Column5, 1) = ' ' THEN 'None' ELSE Column5 END AS ColumnAlias
The reason for this is that when you specify a condition, trailing spaces are ignored. In other words, "HI", "hi", and "Hi " (with a space after it) are all considered equal.
I know this is an older post, but just in case you're still having trouble...
I've seen that empty strings or strings of spaces seem to handled and identified differently in different systems (Access vs SSRS vs T-SQL run in SSMS). I find that using the len() function works pretty well everywhere. I usually do something like the following:
Case
When ISNULL(LEN(LTRIM(RTRIM([Column5]))),0) = 0
Then 'None'
Else [Column5]
End
This way it's actively looking for and counting characters after you've removed all spacers.
Once you've identified these fields, you can put your whole query (assuming it's not overly complex) into a CTE and work with the end product. This can sometimes be easier than trying to work with the data as it is being derived.
With Selected_Records as (
Select ...
...
)
Select Distinct Column5
From Selected_Records
Then you can add any other conditions, ordering, or aggregates on top of the derived data without hindering its derivation. This works pretty well until the query in the CTE gets very complicated or utilizes many parameters (these are anecdotal observations).
Hope this helps someone.

Is there any reason why you cannot select a statement as a bit in SQL Server?

I am wondering why the following fails:
SELECT price<500 as PriceIsCheap
and forces you to do the following:
SELECT CASE WHEN (price<500) THEN 1 ELSE 0 END as PriceIsCheap
When, as per the answer to this related question, the conversion table says that an implicit conversion should occur.
There is no boolean data type in SQL, BIT is kind of a hack, but the main problem is that due to the SQL concept of NULL true boolean logic is impossible (for example, what would your query return if price was NULL?)
Note that I'm not saying that there are not possible ways to implement boolean logic that "mostly" work (for example, you could say that TRUE OR NULL is NULL or whatever) just that the people who designed the SQL standard couldn't decide on The One True Representation for boolean logic (for example, you could also argue that TRUE OR NULL is TRUE, since TRUE OR <anything> is TRUE).
The boolean expressions (=, <=, >=, etc) are only valid in certain places (notably, WHERE clauses and CASE labels) and not in any other place.
Well you'll also find you can't if you have a bit column called IsCheap do SELECT * FROM STUFF WHERE IsCheap, you have to do WHERE IsCheap=1.
The reason is simple, the data type is a bit, not a bool. True, it's basically the only use you'll put it to and it's implicitly converted by almost any data access framework, but it's still technically a bit with 0 or 1 rather than a bool with true or false. There's an obvious connection we can all see, but SQL wasn't written with this assumption in it so we have to provide the logic to convert true/false to 1/0.
The expression price < 500 returns a logical value: TRUE, FALSE or UNKNOWN. It is not a data value, which is why you need to use a CASE expression to return a corresponding data value.
FWIW the Microsoft Access Database Engine does indeed treat the results of expressions as data values e.g. you can ask all kinds of wacky questions such as:
SELECT 1 = 1, 1 = NULL, 1 <> NULL, 1 IN (NULL)
FROM Foo;
...and it will happily provide answers but of course this merely proves that Access does not implement the SQL language!
I am not MSSQL person, but I ran into the same problem with Oracle. The trivial answer is, because Boolean is not a valid column type in those databases. Now, why they decided you don't need Booleans as values is anybody's guess.
#paxdiablo, that's so missing the point... The OP's example is just a minimal example. This is still simplistic but real-world example: Consider a People table, containing names and ages. You want to get all the people, but also want to know if they are underage. In both MySQL and PostgreSQL, you can write
SELECT name, age < 18 AS minor FROM people