The CASE
expression is one of my favorite constructs in T-SQL. It is quite flexible, and is sometimes the only way to control the order in which SQL Server will evaluate predicates.
However, it is often misunderstood.
What is the T-SQL CASE Expression?
In T-SQL, CASE
is an expression that evaluates one or more possible expressions and returns the first appropriate expression. The term expression may be a bit overloaded here, but basically it is anything that can be evaluated as a single, scalar value, such as a variable, a column, a string literal, or even the output of a built-in or scalar function.
There are two forms of CASE in T-SQL:
- Simple CASE expression – when you only need to evaluate equality:
CASE <input> WHEN <eval> THEN <return> … [ELSE <return>] END
- Searched CASE expression – when you need to evaluate more complex expressions, such as inequality, LIKE, or IS NOT NULL:
CASE WHEN <input_bool> THEN <return> … [ELSE <return>] END
The return expression is always a single value, and the output data type is determined by data type precedence.
As I said, the CASE expression is often misunderstood; here are some examples:
CASE is an expression, not a statement
Likely not important to most people, and perhaps this is just my pedantic side, but a lot of people call it a CASE
statement – including Microsoft, whose documentation uses statement and expression interchangeably at times. I find this mildly annoying (like row/record and column/field) and, while it's mostly semantics, but there is an important distinction between an expression and a statement: an expression returns a result. When people think of CASE
as a statement, it leads to experiments in code shortening like this:
SELECT CASE [status]
WHEN 'A' THEN
StatusLabel = 'Authorized',
LastEvent = AuthorizedTime
WHEN 'C' THEN
StatusLabel = 'Completed',
LastEvent = CompletedTime
END
FROM dbo.some_table;
Or this:
SELECT CASE WHEN @foo = 1 THEN
(SELECT foo, bar FROM dbo.fizzbuzz)
ELSE
(SELECT blat, mort FROM dbo.splunge)
END;
This type of control-of-flow logic may be possible with CASE
statements in other languages (like VBScript), but not in Transact-SQL's CASE
expression. To use CASE
within the same query logic, you would have to use a CASE
expression for each output column:
SELECT
StatusLabel = CASE [status]
WHEN 'A' THEN 'Authorized'
WHEN 'C' THEN 'Completed' END,
LastEvent = CASE [status]
WHEN 'A' THEN AuthorizedTime
WHEN 'C' THEN CompletedTime END
FROM dbo.some_table;
CASE will not always short circuit
The official documentation once implied that the entire expression will short-circuit, meaning it will evaluate the expression from left-to-right, and stop evaluating when it hits a match:
However, this isn't always true. And to its credit, in a more current version, the page went on to try to explain one scenario where this isn't guaranteed. But it only gets part of the story:
The divide by zero example is pretty easy to reproduce, and I demonstrated it in this answer on dba.stackexchange.com:
DECLARE @i INT = 1;
SELECT CASE WHEN @i = 1 THEN 1 ELSE MIN(1/0) END;
Result:
Divide by zero error encountered.
There are trivial workarounds (such as ELSE (SELECT MIN(1/0)) END
), but this comes as a real surprise to many who haven't memorized the above sentences from Books Online. I was first made aware of this specific scenario in a conversation on a private e-mail distribution list by Itzik Ben-Gan (@ItzikBenGan), who in turn was initially notified by Jaime Lafargue. I reported the bug in Connect #690017 : CASE / COALESCE won't always evaluate in textual order; it was swiftly closed as "By Design." Paul White (blog | @SQL_Kiwi) subsequently filed Connect #691535 : Aggregates Don't Follow the Semantics Of CASE, and it was closed as "Fixed." The fix, in this case, was clarification in the Books Online article; namely, the snippet I copied above.
This behavior can yield itself in some other, less obvious scenarios, too. For example, Connect #780132 : FREETEXT() does not honor order of evaluation in CASE statements (no aggregates involved) shows that, well, CASE
evaluation order is not guaranteed to be left-to-right when using certain full-text functions either. On that item, Paul White commented that he also observed something similar using the new LAG()
function introduced in SQL Server 2012. I don't have a repro handy, but I do believe him, and I don't think we've unearthed all of the edge cases where this may occur.
So, when aggregates or non-native services like Full-Text Search are involved, please do not make any assumptions about short circuiting in a CASE
expression.
RAND() can be evaluated more than once
I often see people writing a simple CASE
expression, like this:
SELECT CASE @variable
WHEN 1 THEN 'foo'
WHEN 2 THEN 'bar'
END
It is important to understand that this will be executed as a searched CASE
expression, like this:
SELECT CASE
WHEN @variable = 1 THEN 'foo'
WHEN @variable = 2 THEN 'bar'
END
The reason it is important to understand that the expression being evaluated will be evaluated multiple times, is because it can actually be evaluated multiple times. When this is a variable, or a constant, or a column reference, this is unlikely to be a real problem; however, things can change quickly when it's a non-deterministic function. Consider that this expression yields a SMALLINT
between 1 and 3; go ahead and run it many times, and you will always get one of those three values:
SELECT CONVERT(SMALLINT, 1+RAND()*3);
Now, put this into a simple CASE
expression, and run it a dozen times – eventually you will get a result of NULL
:
SELECT [result] = CASE CONVERT(SMALLINT, 1+RAND()*3)
WHEN 1 THEN 'one'
WHEN 2 THEN 'two'
WHEN 3 THEN 'three'
END;
How does this happen? Well, the entire CASE
expression is expanded to a searched expression, as follows:
SELECT [result] = CASE
WHEN CONVERT(SMALLINT, 1+RAND()*3) = 1 THEN 'one'
WHEN CONVERT(SMALLINT, 1+RAND()*3) = 2 THEN 'two'
WHEN CONVERT(SMALLINT, 1+RAND()*3) = 3 THEN 'three'
ELSE NULL -- this is always implicitly there
END;
In turn, what happens is that each WHEN
clause evaluates and invokes RAND()
independently – and in each case it could yield a different value. Let's say we enter the expression, and we check the first WHEN
clause, and the result is 3; we skip that clause and move on. It is conceivable that the next two clauses will both return 1 when RAND()
is evaluated again – in which case none of the conditions are evaluated to true, so the ELSE
takes over.
Other expressions can be evaluated more than once
This problem is not limited to the RAND()
function. Imagine the same style of non-determinism coming from these moving targets:
SELECT
[crypt_gen] = 1+ABS(CRYPT_GEN_RANDOM(10) % 20),
[newid] = LEFT(NEWID(),2),
[checksum] = ABS(CHECKSUM(NEWID())%3);
These expressions can obviously yield a different value if evaluated multiple times. And with a searched CASE
expression, there will be times when every re-evaluation happens to fall out of the search specific to the current WHEN
, and ultimately hit the ELSE
clause. To protect yourself from this, one option is to always hard-code your own explicit ELSE
; just be careful about the fallback value you choose to return, because this will have some skew effect if you are looking for even distribution. Another option is to just change the last WHEN
clause to ELSE
, but this will still lead to uneven distribution. The preferred option, in my opinion, is to try and coerce SQL Server to evaluate the condition once (though this isn't always possible within a single query). For example, compare these two results:
-- Query A: expression referenced directly in CASE; no ELSE:
SELECT x, COUNT(*) FROM
(
SELECT x = CASE ABS(CHECKSUM(NEWID())%3)
WHEN 0 THEN '0' WHEN 1 THEN '1' WHEN 2 THEN '2' END
FROM sys.all_columns
) AS y GROUP BY x;
-- Query B: additional ELSE clause:
SELECT x, COUNT(*) FROM
(
SELECT x = CASE ABS(CHECKSUM(NEWID())%3)
WHEN 0 THEN '0' WHEN 1 THEN '1' WHEN 2 THEN '2' ELSE '2' END
FROM sys.all_columns
) AS y GROUP BY x;
-- Query C: Final WHEN converted to ELSE:
SELECT x, COUNT(*) FROM
(
SELECT x = CASE ABS(CHECKSUM(NEWID())%3)
WHEN 0 THEN '0' WHEN 1 THEN '1' ELSE '2' END
FROM sys.all_columns
) AS y GROUP BY x;
-- Query D: Push evaluation of NEWID() to subquery:
SELECT x, COUNT(*) FROM
(
SELECT x = CASE x WHEN 0 THEN '0' WHEN 1 THEN '1' WHEN 2 THEN '2' END
FROM
(
SELECT x = ABS(CHECKSUM(NEWID())%3) FROM sys.all_columns
) AS x
) AS y GROUP BY x;
Distribution:
Value | Query A | Query B | Query C | Query D |
---|---|---|---|---|
NULL | 2,572 | – | – | – |
0 | 2,923 | 2,900 | 2,928 | 2,949 |
1 | 1,946 | 1,959 | 1,927 | 2,896 |
2 | 1,295 | 3,877 | 3,881 | 2,891 |
Distribution of values with different query techniques
In this case, I am relying on the fact that SQL Server chose to evaluate the expression in the subquery and not introduce it to the searched CASE
expression, but this is merely to demonstrate that distribution can be coerced to be more even. In reality, this may not always be the choice the optimizer makes, so please don't learn from this little trick. :-)
CHOOSE() is also affected
You will observe that if you replace the CHECKSUM(NEWID())
expression with the RAND()
expression, you'll get entirely different results; most notably, the latter will only ever return one value. This is because RAND()
, like GETDATE()
and some other built-in functions, is given special treatment as a runtime constant, and only evaluated once per reference for the entire row. Note that it can still return NULL
just like the first query in the preceding code sample.
This problem is also not limited to the CASE
expression; you can see similar behavior with other built-in functions that use the same underlying semantics. For example, CHOOSE
is merely syntactic sugar for a more elaborate searched CASE
expression, and this will also yield NULL
occasionally:
SELECT [choose] = CHOOSE(CONVERT(SMALLINT, 1+RAND()*3),'one','two','three');
IIF()
is a function that I expected to fall into this same trap, but this function is really just a searched CASE
expression with only two possible outcomes, and no ELSE
– so it is tough, without nesting and introducing other functions, to envision a scenario where this can break unexpectedly. While in the simple case it is decent shorthand for CASE
, it is also tough to do anything useful with it if you need more than two possible outcomes. :-)
COALESCE() is also affected
Finally, we should examine that COALESCE
can have similar issues. Let's consider that these expressions are equivalent:
SELECT COALESCE(@variable, 'constant');
SELECT CASE WHEN @variable IS NOT NULL THEN @variable ELSE 'constant' END);
In this case, @variable
would be evaluated twice (as would any function or subquery, as described in this Connect item).
I was really able to get some puzzled looks when I brought the following example up in a recent forum discussion. Let's say I want to populate a table with a distribution of values from 1-5, but whenever a 3 is encountered, I want to use -1 instead. Not a very real-world scenario, but easy to construct and follow. One way to write this expression is:
SELECT COALESCE(NULLIF(CONVERT(SMALLINT,1+RAND()*5),3),-1);
(In English, working from the inside out: convert the result of the expression 1+RAND()*5
to a smallint; if the result of that conversion is 3, set it to NULL
; if the result of that is NULL
, set it to -1. You could write this with a more verbose CASE
expression, but concise seems to be king.)
If you run that a bunch of times, you should see a range of values from 1-5, as well as -1. You will see some instances of 3, and you may have also noticed that you occasionally see NULL
, though you might not expect either of those results. Let's check the distribution:
USE tempdb;
GO
CREATE TABLE dbo.dist(TheNumber SMALLINT);
GO
INSERT dbo.dist(TheNumber) SELECT COALESCE(NULLIF(CONVERT(SMALLINT,1+RAND()*5),3),-1);
GO 10000
SELECT TheNumber, occurences = COUNT(*) FROM dbo.dist
GROUP BY TheNumber ORDER BY TheNumber;
GO
DROP TABLE dbo.dist;
Results (your results will certainly vary, but the basic trend should be similar):
TheNumber | occurences |
---|---|
NULL | 1,654 |
-1 | 2,002 |
1 | 1,290 |
2 | 1,266 |
3 | 1,287 |
4 | 1,251 |
5 | 1,250 |
Distribution of TheNumber using COALESCE
Breaking down a searched CASE expression
Are you scratching your head yet? How do the values NULL
and 3 show up, and why is the distribution for NULL
and -1 substantially higher? Well, I'll answer the former directly, and invite hypotheses for the latter.
The expression roughly expands to the following, logically, since RAND()
is evaluated twice inside NULLIF
, and then multiply that by two evaluations for each branch of the COALESCE
function. I don't have a debugger handy, so this isn't necessarily *exactly* what is done inside of SQL Server, but it should be equivalent enough to explain the point:
SELECT
CASE WHEN
CASE WHEN CONVERT(SMALLINT,1+RAND()*5) = 3 THEN NULL
ELSE CONVERT(SMALLINT,1+RAND()*5)
END
IS NOT NULL
THEN
CASE WHEN CONVERT(SMALLINT,1+RAND()*5) = 3 THEN NULL
ELSE CONVERT(SMALLINT,1+RAND()*5)
END
ELSE -1
END
END
So you can see that being evaluated multiple times can quickly become a Choose Your Own Adventure™ book, and how both NULL
and 3 are possible outcomes that don't seem possible when examining the original statement. An interesting side note: this doesn't happen quite the same if you take the above distribution script and replace COALESCE
with ISNULL
. In that case, there is no possibility for a NULL
output; the distribution is roughly as follows:
TheNumber | occurences |
---|---|
-1 | 1,966 |
1 | 1,585 |
2 | 1,644 |
3 | 1,573 |
4 | 1,598 |
5 | 1,634 |
Distribution of TheNumber using ISNULL
Again, your actual results will certainly vary, but shouldn't by much. The point is that we can still see that 3 falls through the cracks quite often, but ISNULL
magically eliminates the potential for NULL
to make it all the way through.
I talked about some of the other differences between COALESCE
and ISNULL
in a tip, entitled "Deciding between COALESCE and ISNULL in SQL Server." When I wrote that, I was heavily in favor of using COALESCE
except in the case where the first argument was a subquery (again, due to this bug "feature gap"). Now I'm not so sure I feel as strongly about that.
Simple CASE expressions can become nested over linked servers
One of the few limitations of the CASE
expression is that is restricted to 10 nest levels. In this example over on dba.stackexchange.com, Paul White demonstrates (using Plan Explorer) that a simple expression like this:
SELECT CASE column_name
WHEN '1' THEN 'a'
WHEN '2' THEN 'b'
WHEN '3' THEN 'c'
...
END
FROM ...
Gets expanded by the parser to the searched form:
SELECT CASE
WHEN column_name = '1' THEN 'a'
WHEN column_name = '2' THEN 'b'
WHEN column_name = '3' THEN 'c'
...
END
FROM ...
But can actually be transmitted over a linked server connection as the following, much more verbose query:
SELECT
CASE WHEN column_name = '1' THEN 'a' ELSE
CASE WHEN column_name = '2' THEN 'b' ELSE
CASE WHEN column_name = '3' THEN 'c' ELSE
...
ELSE NULL
END
END
END
FROM ...
In this situation, even though the original query only had a single CASE
expression with 10+ possible outcomes, when sent to the linked server, it had 10+ nested CASE
expressions. As such, as you might expect, it returned an error:
Statement(s) could not be prepared.
Msg 125, Level 15, State 4
Case expressions may only be nested to level 10.
In some cases, you can rewrite it as Paul suggested, with an expression like this (assuming column_name
is a varchar column):
SELECT CASE CONVERT(VARCHAR(MAX), SUBSTRING(column_name, 1, 255))
WHEN 'a' THEN '1'
WHEN 'b' THEN '2'
WHEN 'c' THEN '3'
...
END
FROM ...
In some cases, only the SUBSTRING
may be required to alter the location where the expression is evaluated; in others, only the CONVERT
. I did not perform exhaustive testing, but this may have to do with the linked server provider, options like Collation Compatible and Use Remote Collation, and the version of SQL Server at either end of the pipe.
Long story short, it is important to remember that your CASE
expression can be re-written for you without warning, and that any workaround you use may later be overruled by the optimizer, even if it works for you now.
CASE Expression Final Thoughts and Additional Resources
I hope I've given some food for thought on some of the lesser-known aspects of the CASE
expression, and some insight into situations where CASE
– and some of the functions that use the same underlying logic – return unexpected results. Some other interesting scenarios where this type of problem has cropped up:
Excellent post! Thanks for explaining in simple terms exactly what is going on in these queries which give "unexpected" results. I've been caught by several of these in the past, but will try and remember lessons from this post in the future.
This entire (very useful) post should be unnecessary. It is certainly possible to assign intuitive and consistent semantics to non-deterministic functions that run as part of a query. This is not an impossible problem to solve.
Logically, each non-deterministic function should be invoked once per row per instance of that function.
Physically, I see no reason what duplicate invocations cannot be avoided by another layer of Compute Scalar. The CASE and COALESCE duplications seem very unnecessary from an implementations point of view.
Things like this generate no end of labor and support costs. I see questions about this issue about once per week on Stack Overflow. Issues that hit product support over and over should be fixed in the product.
Really good article – thank you!
Here's my hypothesis for the uneven distribution in your brain-teaser:
COALESCE and NULLIF each evaluate their expressions. If the result is the prescribed one, the emit the prescribed value. If not they re-evaluate and emit the result of that.
So in terms of the distribution this can be described in pseudo-code as follows ("rand(1-5)" is evaluated each time appears):
if rand(1-5) = 3
— ~0.2 of cases
result is -1 (~0.2 of total cases)
else
— ~0.8 of cases
if rand(1-5) = 3
— ~0.2 of cases
result is null (~0.2 * 0.8 of total cases)
else
— ~0.8 of cases
result is rand(1-5) (~0.2 * 0.8 * 0.8 of total cases for each value)
end if
end if
Actually the distribution is not a very good distribution at all. Increasing the total count to 100,000 and then 1,000,000 the ditribution tightened up to null 160,000 -1 200,000 and 1-5 as 128,000 from null 1562 -1 1996 1-5 1280 which is almost 1/100th of the result for 1,000,000 counts so this is not random at all but has a true pattern that goes to the limit as a perfect pattern I would imagine. I wlould not use MSSql Server as a tool for doing big data random calculations of any sort. Their algorithms leave something to be desired.
You say
"SELECT [result] = CASE
WHEN CONVERT(SMALLINT, 1+RAND())*3 = 1 THEN 'one'
WHEN CONVERT(SMALLINT, 1+RAND())*3 = 2 THEN 'two'
WHEN CONVERT(SMALLINT, 1+RAND())*3 = 3 THEN 'three'
ELSE NULL — this is always implicitly there
END;
So, what happens is that each WHEN clause evaluates and invokes RAND() independently – and in each case it could yield a different value."
And then you say:
"This is because RAND(), like GETDATE() and some other built-in
functions, is given special treatment as a runtime constant, and only evaluated once for the entire row. "
Either of the statements is false, because if they are both true then they contradict each other.
I don't think what I'm saying is contradictory at all. Consider the following query:
The
RAND()
call that populatesa
is materialized exactly once, and theRAND()
call that populatesb
is materialized exactly once. But since they are mentioned twice per row they yield independent and different values per row – similar to what happens when theCASE
expression turns one reference into multiple per row for you. SinceRAND()
is one of these functions that gets treated as a special runtime constant, you'll notice that later on I use a loop rather than a set-based query to forceRAND()
to be evaluated 10,000 times instead of once (even though within each row it would be evaluated multiple times).Yes, this is true, this was not meant to be a post that extols the virtues of
RAND()
and proclaim it to provide really good distribution in all cases. I just meant that in this case it was predictable enough to demonstrate…hopefully the aggregate function inside a case is evaluated first – what else woud you expect ?
If you want short-circuiting, just write
DECLARE @i INT = 1;
SELECT min(CASE WHEN @i = 1 THEN 1 ELSE 1/0 END );
Your mention of "CASE will not always short-circuit" is something I've known about since SQL 2005, particularly when it comes to dealing with "divide by zero" errors. However, SQL only evaulates the expression at it's top level. The following is something I used as part of creating a multi-fork SQL job that evaluated several pieces of information, then took the appropriate actions based on that evaluation:
"SELECT
LocalServer,
DNSAlias,
CASE WHEN LocalServer = DNSAlias THEN "A" — Local is active
ELSE CONVERT(VARCHAR,0/0) — Local is not active
END AS Result"
The only time the "0/0" portion of the CASE expression gets evaulated is if, at the actual run time, that branch is explicitly being followed. Otherwise, it either assumes (I would guess) that the contents of the value being converted are a valid string, or it ignores anything within the next level of processing (inside the CONVERT) unless called upon to actually execute it.
Herbert, now that I know that aggregates are treated differently, re-writing the expression is quite intuitive. The problem is that for a long time many people – myself included – assumed (and often reiterated to others) that "CASE always short circuits." So it came as quite a surprise to many who came across this scenario where it did not follow the CASE expression from left-to-right.
Joshua, right, and I was just pointing out that in particular scenarios (e.g. when you introduce aggregates), then SQL Server might *not* ignore anything within the next level of processing, so it's useful to keep that in mind if you get interesting behavior where you think short-circuiting should happen but it doesn't.
I think I broke my brain.
Wow. Nice write up.
Great article.
Thanks for this bright article. It really helps understanding the CASE expression limits.
Excellent
Excellent discussion, very well written and explained.
Trying to assign values to variables using CASE.
Why I see
NULL
value for@i1
, not for@i2
?Because the assignment can only happen on a single, arbitrary row. Remember that there is an implicit
ELSE NULL
in each of yourCASE
expressions, and on any given row, it will returnNULL
for one expression or the other, since on any single row,i
can't be both 1 and 2 at the same time. Look what happens if you just say:You get these results:
What you need to do is aggregate or pivot. Here is a simple aggregate:
You also might need to consider what you want to do when there is more than one row where
i
is 1 or 2.update MUREXDB.GTR#DEALGLOBALSCOPE_DBF
set scope.M_UNMASK = CASE
WHEN scope.M_UNMASK "
THEN
CASE WHEN u.M_RTR_CA_MB = "Y" THEN scope.M_UNMASK||";CA.MB.MSC" END
CASE WHEN u.M_RTR_CA_ON = "Y" THEN scope.M_UNMASK||";CA.ON.OSC" END
CASE WHEN u.M_RTR_CA_QC = "Y" THEN scope.M_UNMASK||";CA.QC.AMF" END
CASE WHEN u.M_RTR_CA_RTD ="Y" THEN scope.M_UNMASK||";CA.QC.AMF" END
ELSE
CASE
WHEN u.M_RTR_CA_MB = "Y"
THEN "CA.MB.MSC"
END
CASE
WHEN u.M_RTR_CA_ON = "Y"
THEN "CA.ON.OSC"
END
CASE
WHEN u.M_RTR_CA_QC = "Y"
THEN "CA.QC.AMF"
END
CASE
WHEN u.M_RTR_CA_RTD ="Y"
THEN "CA.QC.AMF"
END
END
this gives me error. How do i implement this ? please help
WHEN scope.M_UNMASK "
<– this seems incomplete= "Y"
<– SQL Server uses ' for string delimiters, not "scope.M_UNMASK||";CA.QC.AMF"
<– SQL Server uses + for concatenation, not ||Can anyone please convert the below if code to case expression which we use it in select statement
If(VIDEO_VIEW_TYPE='DVR', 'DVR',
If(VIDEO_VIEW_TYPE='OTT/VOD', If(STREAMING_SOURCE='VOD',
'VOD','OTT'), VIDEO_VIEW_TYPE)) as VIDEO_VIEW_PLATFORM_NEW,
Looks like:
case when (FIELD5 in ('A', 'B', 'C', 'D', 'E', 'F', 'G', 'H') and field10 = 'XYZ') then amount*-1 else amount end
I want the amount to be multiplied *-1 when field5 is from A-H – however, it has to choose the amount which is also field10 = XYZ.
Otherwise, the amount should be reported as amount, again when field10 = XYZ.
I find that adding 'and field10' does not work.
I tried this query (for another table) without the field10 and it works fine.
Any idea how I could achieve what I want using the case statement, or some other way?
Thank you.
Nice text editor themed colors!
Could you please share these settings?
Hi Tommy, I posted about my color scheme here:
https://blogs.sentryone.com/aaronbertrand/making-ssms-pretty-my-dark-theme/
Hello
I wrote a bunch of code in c++ to check for a multiple conditions and also I make use of case. Now am struggling to convert it sql case. Below is the c++ code
Switch(TypeEmp){
case 0:
if(Age =0) || (income <=1880000)){
amnt= income * 52 ;
}else if(other condition){
calculate it amnt;
}
}
break;
}
The existence of Null and 3 in the results depends on the functionality of the functions; looking at NULLIF, it possibly writes the first argument twice and that means RAND() is run twice thus the second one can give 3 mean while there is no test on the second run, the same with COALESCE it checks the first argument if it is null it goes to the second but if it is not null it returns the argument but on the return a different RAND() is run which could be null and since there are more opportunities for this null apears pretty many times.
How do I have the case statements only display "one" match in the results? For example,
Match = case
when cg.ServNPI = PR.NPI then 'YES'
when cg.BillNPI = PR.NPI then 'YES'
When aux.RendProvPrimId = PR.NPI then 'YES'
else 'NO'
end
I only what the "else" results displayed. Who can help?
The else results would be displayed if the first three clauses return false. Only one of your four possible outputs would appear on any one row. Perhaps you could explain what you're after in a different way?
Hi,
I think this is a great article.
You say: Long story short, it is important to remember that your CASE expression can be re-written for you without warning, and that any workaround you use may later be overruled by the optimizer, even if it works for you now.
I see this as a BIG PROBLEM. I would appreciate hearing practical solution(s) or alternatives so that "my" SQL will be accurate.
The proper way to do this, now knowing this, would then be:
WITH MyRandT as ( SELECT CONVERT(SMALLINT,1+RAND()*5) as MyRand ) SELECT COALESCE(NULLIF(MyRand,3),-1) FROM MyRandT
Or the old-fashioned subquery in the From.
I'll try to remember and keep this in mind for volatile functions.
Your "expanded" sample always returns "three".
SELECT [result] = CASE
WHEN CONVERT(SMALLINT, 1+RAND())*3 = 1 THEN 'one'
WHEN CONVERT(SMALLINT, 1+RAND())*3 = 2 THEN 'two'
WHEN CONVERT(SMALLINT, 1+RAND())*3 = 3 THEN 'three'
ELSE NULL — this is always implicitly there
END;
It should be:
SELECT [result] = CASE
WHEN CONVERT(SMALLINT, 1+RAND()*3) = 1 THEN 'one'
WHEN CONVERT(SMALLINT, 1+RAND()*3) = 2 THEN 'two'
WHEN CONVERT(SMALLINT, 1+RAND()*3) = 3 THEN 'three'
ELSE NULL — this is always implicitly there
END;
Been enjoying your Bad Habits Revival post.
Thanks, that's what I get when I only validate that it parses. :-\
Hi
I have been reading the threads and scratching my head as to how to use the result of my first CASE expression being 'Qty' as the value instead of SOP30300.QUANTITY within the next CASE expression. Any guidance would be appreciated Cheers
SELECT SOP30300.ITEMNMBR, SOP30300.QUANTITY, SOP30300.SOPTYPE,
case SOP30300.SOPTYPE
when 3 then SOP30300.QUANTITY
when 4 then – SOP30300.QUANTITY
end 'Qty',
case SOP30300.ITEMNMBR
when '00111' then SOP30300.QUANTITY * 10
when '00112' then SOP30300.QUANTITY * 100
ELSE SOP30300.QUANTITY * 1
end Qty
FROM ………
Sounds like whatever database you use doesn't like single-quoted identifiers, or maybe it doesn't like duplicated. Try bracketed instead? Your query works perfectly in SQL Server and MariaDB.
I really appreciate you as i resolved my complex problem with help of the your case statement.
SET @TokenWithDescription =
[token] + ' is ' +
CASE [token]
WHEN 'CASE' THEN 'always an expression!'
ELSE 'possibly a statement…'
END
:-]