Dangling "else" Resolution in T-SQL - sql

So I've been tasked with converting some T-SQL code to C code. Whoever wrote the code I'm converting indulged in little to no code etiquette. I know this because of the complete lack of commenting, lack of indentation, and lack of begin/end blocks except where absolutely syntactically necessary (and a few thrown in arbitrarily for good measure).
This raises a few problems. The code I'm converting is based on the Metaphone algorithm. I say "based on" because it has quite a few... "undocumented improvements" that make it differ from the official implementation. Because of this, I can't just go grab some Metaphone implementation, because then it wouldn't actually be a "correct" translation.
So here's the root of the issue(s):
if #str1='d'
if substring(#str,#cnt,3) in ('dge','dgy','dgi')
set #Result=#Result + 'j'
else
set #Result=#Result + 't'
Because of how Metaphone works, I'm pretty sure they meant:
if #str1='d'
if substring(#str,#cnt,3) in ('dge','dgy','dgi')
set #Result=#Result + 'j'
else
set #Result=#Result + 't'
But I'm not sure if it's actually being interpreted as:
if #str1='d'
if substring(#str,#cnt,3) in ('dge','dgy','dgi')
set #Result=#Result + 'j'
else
set #Result=#Result + 't'
This little snippet isn't too big of a deal, but just after it, there's a section with five "if" statements and only one "else" statement and with no begin/end blocks to explicitly arrange them. None of this would be a big deal if I could actually run the code to to test and see, but sadly, I don't have an environment to test it and nor do I have any output from previous usage of the code.
tl;dr: Do any of you T-SQL gurus out there know which of the above two statements it'll be interpreted as and what's the rule of them with shift/reduce conflicts in T-SQL? (Attach to first "if" statement, attach to last "if" statement, pick one at random?)
EDIT: Here's another fun one a few lines down.
if #str1='t'
if substring(#str,#cnt,3) in ('tia','tio')
set #Result=#Result + 'x'
else
if #str2='th'
set #Result=#Result + '0'
else
if substring(#str,#cnt,3) <> 'tch'
set #Result=#Result + 't'
EDIT2: Ok, if I'm reading these answers correctly, that means the above is actually
if #str1='t'
if substring(#str,#cnt,3) in ('tia','tio')
set #Result=#Result + 'x'
else
if #str2='th'
set #Result=#Result + '0'
else
if substring(#str,#cnt,3) <> 'tch'
set #Result=#Result + 't'

Here is something that should help you out
DECLARE #testvar INT;
DECLARE #testvar2 INT;
SET #testvar = 1;
SET #testvar2 = 1;
IF #testvar = 1
IF #testvar2 = 1
SELECT 'Got to 1';
ELSE
SELECT 'Got to 2';
If testvar and testvar2 are both 1, it outputs "Got to 1".
If testvar=1 and testvar2=2, it outputs "Got to 2".
If testvar=2, there is no output. So the else is getting paired off against the nearest if

Your assumption is correct.
IF () IF () X ELSE Y is equivilent to IF () BEGIN IF () X ELSE Y END
The ELSE keyword looks back to the most recent IF statement in the same scope. So to get what you think was intended, you need to add BEGIN and END statements...
IF (#str1='d')
BEGIN
IF (substring(#str,#cnt,3) in ('dge','dgy','dgi'))
SET #Result=#Result + 'j'
END
ELSE
SET #Result=#Result + 't'

Related

Does the SQL'%' wild card character capture null values?

Recently I've come across a problem with a query that isn't returning everything that it's expected to return. Part of the query which selects by a condition is as follows:
AND field LIKE
CASE WHEN #val = 1 THEN
'%'
ELSE
'N'
END
Now, when #val is 1, I'd expect this piece of code to essentially do nothing, as in the condition to basically accept any value what so ever, including null values.
Am I wrong about this? And if so, does anyone have a solution? I was thinking something along the lines of
AND field LIKE
CASE WHEN #val = 1 THEN
'%' OR ISNULL(field)
ELSE
'N'
END
However SQL doesn't work like that, but that's basically what I wish to accomplish.
Thanks all, and sorry if this is a duplicate, I couldn't find an answer.
Based on what you're trying to accomplish, it seems your query could be optimized to this:
AND (#val = 1 OR field = 'N')
There doesn't seem to be a reason for the LIKE.
UPDATE
Since you are trying to understand the behavior of LIKE and CASE moreso than working with existing queries, here are some variations of the accepted answer.
To use CASE within the LIKE, you have to use something like COALESCE to handle the null case as well.
COALESCE(Field, '') LIKE (CASE WHEN #val = 1 THEN '%' ELSE 'N' END)
Otherwise, you can use the LIKE within the CASE (like accepted answer), but probably personal preference that this seems easier to read:
1 = (CASE WHEN #val = 1 OR Field LIKE 'N' THEN 1 ELSE 0 END)
field LIKE '%' does not match null. CASE expressions must return a single type of result, I like int in most of mine.
AND 1 = CASE
WHEN #val = 1 THEN 1
WHEN field like 'N' THEN 1
ELSE 0
END
Try this (assuming that field is varchar or nvarchar) -
AND ISNULL(field,'') LIKE
CASE WHEN #val = 1 THEN
'%'
ELSE
'N'
END

Why does this work in SSMS?

I was in a database class the other night and we noticed that the following code seemed to work, but we could not logically understand why.
DECLARE #counter integer
SET #counter = 42
WHILE #counter < 52
BEGIN
set #counter = #counter+++++ + 1
PRINT 'The counter is ' + cast(#counter as char)
END
We realized that we could add any number of +'s on to our #counter variable, and SSMS does not seem to care, even though it does not match the original variable. Anybody happen to know why this works?
Appending a + to a variable name does not change the variable name, as white space is ignored (and + is not a valid character in a variable name anyway).
See here for a discussion about why multiple + signs works. Essentially, every + (or -) after the first one is considered a unary operator.

Why does the content of one scenario in a case decide whether the other's content should be right trimmed?

My co-workers and I have encountered a strange behaviour in SQL-Server.
Consider the following code:
select a =
case when 1=1 then
'a '
else
'b '
end + 'c'
One would should reasonable assume that the above code produces the string 'a c'. And it does.
However, if I extend the else's constant ('b ') with an additional space, something strange happens:
select a =
case when 1=1 then
'a '
else
'b '
end + 'c'
Now the result is 'ac', the space from 'a ' has disappeared. The reverse is also true:
select a =
case when 1=0 then
'a '
else
'b '
end + 'c'
This will yield 'bc'. So whenever constant not being returned is longer than the constant being returned, the constant being returned is right trimmed.
And it's only a right trim, because the following code will yield ' ac':
select a =
case when 1=1 then
' a '
else
'b '
end + 'c'
Is there a reason for this behaviour? I assumed it might have something to do with cleaning up strings for trailing whitespace, but then why does it only happen when the constant not being returned is longer?
The above behaviour have been observed on a SQL Server 2014 Express Edition.
Update:
As mentioned in the comments, this behaviour do not appear to be reproducible on a standard installations of SQL Server 2014.
It looks like your session has SET ANSI_PADDING OFF, which will trim trailing spaces from varchar. Try running with SET ANSI_PADDING ON (recommended setting).
SET ANSI_PADDING OFF;
--trims trailing spaces
SELECT a =
CASE WHEN 1=1 THEN
'a '
ELSE
'b '
END + 'c';
--does not trim trailing spaces
SET ANSI_PADDING ON;
SELECT a =
CASE WHEN 1=1 THEN
'a '
ELSE
'b '
END + 'c';
EDIT:
Per the SQL Server ANSI_PADDING documentation, the setting should only apply to column storage, not to expression results. I asked about this behavior and SQL Server MVP Erland Sommarskog did some regression tests using ancient SQL Server versions and identified this behavior as a bug introduced in SQL 7. It was corrected in SQL 2000, but only for ANSI_PADDING ON; ANSI_PADDING OFF continues to misbehave to this day.
I strongly suggest you change your code as to not rely on undocumented and buggy behavior. This bug may be fixed in a future SQL Server version or patch and break your code. Explicitly use the RTRIM function if your application needs trailing spaces removed.

Using mathematical operators in multi variable parameters

I'm wondering if it's possible to include mathematical operators and situations in a multivariable parameter.
In my example, I'm looking to have the freedom to enter 'Overdue' 'Less than one day' or 'More than one day' as #SLA_Days.
Currently, I can enter any of these one variables and it will return a result.
if #sla_days = 'Overdue'
BEGIN
SELECT #sql = #sql + 'and sla_days < 0'
END
ELSE
if #sla_days = 'Less than one day'
BEGIN
SELECT #sql = #sql + 'and sla_days <1 and sla_days >0'
END
ELSE
if #sla_days = 'More than one day'
BEGIN
SELECT #sql = #sql + 'and sla_days > 1'
END
EXEC sp_exectuesql #SQL
I do have a function that I've created that will look a comma delimited string and pull out variables. I do not need help in creating this
What I do need help is, is it possible to enter #SLA_days = 'Overdue,Less than one day,More than one day' and still have it recognize these rules?
Therefore, if I selected Overdue and less than one day, it would return both values where sla_days < 0 and sla days < 1
You don't need a function to pull out variables. This will perform much better.
The trick for your specific problem is to use OR instead of AND, because the value in a column could never be, for example, > 0 AND < 0. Also I add commas to each side of the variable so that conditions can be identified in any order. Note, though, that it will not find values if there are spaces between the commas as well - you'll have to adjust for that.
SET #SLA_days = ',' + #SLA_days + ',';
SET #sql = #sql + ' (1=2 '
+ CASE WHEN #SLA_days LIKE '%,Overdue,%'
THEN ' OR (sla_days < 0)' ELSE '' END
+ CASE WHEN #SLA_days LIKE '%,Less than one day,%'
THEN ' OR (sla_days < 1 AND sla_days > 0)' ELSE '' END
+ CASE WHEN #SLA_days LIKE '%,More than one day,%'
THEN ' OR (sla_days > 1)' ELSE '' END + ')';
Finally, do you really need to pass these big ugly strings to the database? Presumably the users are selecting from a multi-select or checking boxes, in which case you could use much tidier coefficients to represent each case.

SQL Server Query with "ELSE:"

I have various VB6 projects I'm maintaining with some of the queries being passed to the server having "ELSE:" with the colon used in case statements.
I'm wondering can someone tell me what the **ll the colon is used for? It causes errors in SQL2005 and greater, but SQL2000 works with no complaints.
I'd like to just remove it from the code & re-compile, but I'm afraid it'll break 10 other things in the application..
Thanks in advance...
Here is the deal.. somebody used the ELSE keyword as a LABEL in your code.
A word in TSQL followed by a colon is a Label. Here is a sample:
DECLARE #Count int
SET #Count = 0
ONE_MORE_TIME:
IF #Count <> 33
PRINT ‘Hello World’
SET #Count = #Count + 1
END
IF #Count <> GOTO ONE_MORE_TIME
In your case, the label might be "ELSE"
DECLARE #Count int
SET #Count = 0
ELSE:
IF #Count < 33
PRINT ‘Hello World’
SET #Count = #Count + 1
END
IF #Count < 33 GOTO ELSE
I have a feeling that this code is going to be badly indented as well. I'd almost be willing to put some money down on that.
why not remove it, recompile, and test the application to see what impact it may have?
Hmm, it causes errors in SQL2005?
In SQL server, ELSE is used in case statements to catch anything that hasn't been caught yet (it pertains to things that have "fallen through" all of the other cases). For example, suppose we'd like to list some items from a price database and categorize them with a custom 'Budget' text column, so for each item we'll look at it's price and set what it's 'Budget' value should be:
SELECT title, price,
Budget = CASE price
WHEN price > 20.00 THEN 'Expensive'
WHEN price BETWEEN 10.00 AND 19.99 THEN 'Moderate'
WHEN price < 10.00 THEN 'Inexpensive'
ELSE 'Unknown'
END,
FROM titles
The ELSE here catches everything that didn't really fall under "Expensive" or "Moderate" or "Inexpensive". Removing these ELSEs here will definitely mess your queries up.