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
Related
I'm creating a yes/no flag for SQL data and I have something similar to:
'''
CASE WHEN col_name IS NULL OR col_name = 0 THEN "N"
ELSE "Y"
END AS col_name_flag
'''
Is there any way to be more concise so I don't have to have the column name written out twice? Many of the column names are very long so I was wondering if there would be a way to make it more visually pleasing.
Probably the simplest method is to reverse the comparison:
(CASE WHEN col_name <> 0 THEN 'Y' ELSE 'N' END) AS col_name_flag
You could also use:
CASE IsNull(col_name,0) =0 THEN 'N' ELSE 'Y' END as col_name_flag
But I would go with Gordon's answer for performance...
I have a similar situation to the CASE clause well known problem:
DECLARE #i INT = 1;
SELECT CASE WHEN #i = 1 THEN 1 ELSE MIN(1/0) END;
In that case the code will throw an exception, Divide by zero error encountered, even though in theory you would never reach that min(1/0) scenario.
So I have similar situation:
CASE WHEN CodeValue in ('Numeric1','Numeric2') THEN SUM(cast(VarcharValue as int)) ELSE max(VarcharValue) END
In other words I need to use SUM function if VarcharValue is numeric (it could be determined by CodeValue if it is numeric), if that's not the case, I need return VarcharValue with MAX function.
Any suggestions?
You have a strange construct, because one case branch returns a string and another a number. SQL Server decides, in such a case, that the expression returns a number.
And you'll have problems with the ELSE, because a non-numeric string will be converted to a number. Error.
The following might work:
(CASE WHEN CodeValue in ('Numeric1', 'Numeric2')
THEN CAST(VARCHAR(MAX), SUM(CAST(VarcharValue as int)))
ELSE MAX(VarcharValue)
END)
You might still have a problem, if the SUM() is being calculated over all data before filtering (which I think is possible with an aggregation query). My recommendation is to upgrade to a supported version of SQL Server and use:
(CASE WHEN CodeValue in ('Numeric1', 'Numeric2')
THEN CAST(VARCHAR(MAX), SUM(TRY_CAST(VarcharValue as int)))
ELSE MAX(VarcharValue)
END)
However, you can repeat the CASE logic:
(CASE WHEN CodeValue in ('Numeric1', 'Numeric2')
THEN CAST(VARCHAR(MAX), SUM(CASE WHEN CodeValue in ('Numeric1', 'Numeric2') THEN CAST(VarcharValue as int) END))
ELSE MAX(VarcharValue)
END)
So I have EXISTS in huge query which looks like this:
EXISTS(
SELECT
*
FROM
ExistTable
WHERE
ExTableFieldA = #SomeGuid AND
ExTableFieldB = MainTableFieldB AND
ExTableFieldA <> (
CASE
WHEN MainTableFieldZ = 10 THEN MainTableFieldYYY
ELSE NULL
END
)
)
The problem comes from ELSE part of CASE statement, this ExTableFieldA <> NULL will be always false. I could easily write another parameter #EmptyGuid and make it equal to '00000000-0000-0000-0000-000000000000' and everything will work but is this the best approach ?
Pretty much I want to execute another check into the exist for the small size of the records which return the "main" query.
How about removing the case and just using boolean logic?
WHERE ExTableFieldA = #SomeGuid AND
ExTableFieldB = MainTableFieldB AND
(MainTableFieldZ <> 10 OR ExTableFieldA <> MainTableFieldYYY)
I would also recommend that you qualify the column names by including the table alias.
Note: This does assume that MainTableFieldZ is not NULL. If that is a possibility than that logic can easily be incorporated.
ELSE NULL is implied even if you don't list it, but you could use ISNULL here.
ISNULL(ExTableFieldA,'') <> (
CASE
WHEN MainTableFieldZ = 10 THEN MainTableFieldYYY
ELSE ''
END
)
You may need to use some other value like 9999 instead of ''
This is my problem: I added a column Cause to my table. This column contains different conditions (up to here, everything is fine). But since I have a lot of lines for each product, it can have 3 conditions at the same time.
What I'm trying to do is that once it finds a condition, it does not go to the one after (and it is by this order of priority).
I do not know if I was clear, but if you want more explanation do not hesitate to ask me questions
Cause = (CASE
WHEN Four IS NOT NULL THEN 'Retards'
WHEN (MAX(DateP BETWEEN '2018-10-24' AND '2018-10-14') THEN 'stock'
WHEN Reference = 0 THEN 'respecté'
WHEN Produit = 2 THEN 'non respecté'
ELSE 'Erreur'
END)
This is an example of what I want to do:
The CASE expression stops just after the first WHEN..THEN is found. If you want to concatenate labels and check all conditions, you can use multiple CASE expression.
(case when Four IS NOT NULL THEN 'Retards' ELSE '' END +
case when (MAX(DateP) between '2018-10-24' AND '2018-10-14') THEN 'stock' ELSE '' END +
case when Reference = 0 THEN 'respecté' ELSE '' END +
case when Produit = 2 THEN 'non respecté' ELSE '' END
) AS Cause
In C, when you compared true/false value to 1/0, it worked very well.
I would want the similar possibility with SQL Server - when I have a bit column, I would like to compare myBitField = 'y' / myBitField = 'n'
Is there anything I can do about that? Maybe change some SQL interpreter settings or something?
Example of what I would like to do:
select * from
(
select CAST(1 AS BIT) as result
) as main
where main.result = 'y'
Currently, it throws an error, and I would like it to return 1/true/'y', whatever, but I would like it to be able to make that comparison.
I suppose you want to do it for some yes/no thing. But this is generally a wrong concept, your application which is accessing the SQL Server should interpret y as a 1 and n as a 0 and afterwards set the correct parameters for the query. You should not (actually I'm temped to write "must not") do this in SQL Server, that's what you have a business logic for.
As others have said, BIT and CHAR / VARCHAR are entirely different datatypes. But if you want to cast them during the select, you can use CASE expression like so:
-- Reading string as BIT
SELECT CAST(CASE RESULT WHEN 'Y' THEN 1 WHEN 'N' THEN 0 ELSE NULL END AS BIT) RESULT
-- Reading BIT as string
SELECT CAST(CASE RESULT WHEN 1 THEN 'Y' WHEN 0 THEN 'N' ELSE NULL END AS CHAR(1)) RESULT
And that's about as far as your options go here, far as I can understand. :)