SQL Server: interpreting 'y' as BIT value - sql

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. :)

Related

Using SUM in CASE if value is numeric

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)

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

SQL 'CASE WHEN x' vs. 'CASE x WHEN' with greater-than condition?

This is a questions about the two ways to use the SELECT CASE in MS SQL [CASE WHEN X = Y] and [CASE X WHEN Y]
I am trying to define buckets for a field based on its values. I would need to use ranges, so it is necessary to use the "<" or ">" identifiers.
As a simple example, I know it works like this:
SELECT CASE WHEN x < 0 THEN 'a' WHEN X > 100 THEN 'b' ELSE 'c' END
Now I have to write a lot of these, there will be more than 3 buckets and the field names are quite long, so this becomes very difficult to keep clean and easy to follow. I was hoping to use the other way of the select command but to me it looks like I can only use it with equals:
SELECT CASE X WHEN 0 then 'y' ELSE 'z' END
But how can I use this form to specify range conditions just as above? Something like:
SELECT CASE X WHEN < 0 THEN 'a' WHEN > 100 THEN 'b' ELSE "c" END
This one does not work.
Thank You!
As an alternative approach, remember that it is possible to do math on the value that is the input to the "simple" CASE statement. I often use ROUND for this purpose, like this:
SELECT
CASE ROUND(X, -2, 1)
WHEN 0 THEN 'b' -- 0-99
WHEN 100 THEN 'c' -- 100-199
ELSE 'a' -- 200+
END
Since your example includes both positive and negative open-ended ranges, this approach may not work for you.
Still another approach: if you are only thinking about readability in the SELECT statement, you could write a scalar-valued function to hide all the messiness:
CREATE FUNCTION dbo.ufn_SortValuesIntoBuckets (#inputValue INT) RETURNS VARCHAR(10) AS
BEGIN
DECLARE #outputValue VARCHAR(10);
SELECT #outputValue =
CASE WHEN #inputValue < 0 THEN 'a'
WHEN #inputValue BETWEEN 0 AND 100 THEN 'b'
WHEN #inputValue > 100 THEN 'c'
END;
RETURN #outputValue;
END;
GO
So now your SELECT statement is just:
SELECT dbo.ufn_SortValuesIntoBuckets(X);
One final consideration: I have often found, during benchmark testing, that the "searched" form (which you are trying to avoid) actually has better performance than the "simple" form, depending how many CASEs you have. So if performance is a consideration, it might be worth your while to do a little benchmarking before you change things around too much.
There is no such "third form" of the CASE - only the searched and the simple cases are supported *.
You need to use the searched kind (i.e. with separate conditions) even though the variable to which you apply the condition is always the same.
If you are looking to avoid repetition in your SQL when X represents a complex expression, use WITH clause or a nested query to assign a name to the expression that you are selecting.
* The official name of your first example is "searched CASE expression"; your second example is called the "simple CASE expression".
It won't look so bad using BETWEENs:
SELECT CASE
WHEN X < 0 THEN 'a'
WHEN X BETWEEN 0 AND 100 THEN 'b'
WHEN X BETWEEN 100 AND 200 THEN 'c'
ELSE 'd' END
That is actually very interesting question. It resembles C# 9.0 relational pattern
SELECT CASE X WHEN < 0 THEN 'a'
WHEN > 100 THEN 'b'
ELSE 'c'
END
C# 9.0
DeliveryTruck t when t.GrossWeightClass switch
{
> 5000 => 10.00m + 5.00m,
< 3000 => 10.00m - 2.00m,
_ => 10.00m,
},
SQL CASE expression as mentioned in previous answers has two forms: simple and searched.
But SQL standard defines also
SQL:2003 Extended CASE expression(F262). This feature is barely adopted by major vendors.
case — Conditional Expressions by Markus Winand
The so-called extended case accepts a comparison operator right after when and thus lifts the limitation that simple case always uses equals (=) comparisons. The following example uses the less than operator (<) to map values into intervals. It also relies on the precedence of when clauses: the first true condition wins.
CASE x WHEN < 0 THEN '< 0'
WHEN < 10 THEN '[0, 10['
WHEN < 100 THEN '[10, 100['
ELSE '>100'
END

Can somebody verify this for me in sql or teradata

I have a line of code in Oracle and I had to convert it into Teradata.
The Oracle query is
/* add to avoid invalid number due to junk in column */
AND regexp_instr(table.column, ''[^[:digit:]]'', 1, 1) = 0
The code I have written in Teradata
AND (CASE WHEN (POSITION('' '' IN TRIM(table.column)) > 0) OR (UPPER(TRIM(table.column))
(CASESPECIFIC) <> LOWER(TRIM(table.column)) (CASESPECIFIC))
THEN 1 ELSE 0 end ) = 0
The column is defined as a VARCHAR(20) but I only want to select rows where the data is all numeric. I cannot verify the Teradata query as it is a very long-running query and I don't have access to create tables or rather I can not verify the out put on the database I have. I some how tried and it looks like it works but I once wanted to verify the syntax and my understanding of REGEXP_INSTR.
If I am reading correctly and based on my testing this will break your logic (both return 1):
SELECT (CASE WHEN (POSITION('' '' IN TRIM('1234')) > 0) OR (UPPER(TRIM('1234'))
(CASESPECIFIC) <> LOWER(TRIM('1234')) (CASESPECIFIC))
THEN 1 ELSE 0 END )
SELECT (CASE WHEN (POSITION('' '' IN TRIM('abcd ef1')) > 0) OR (UPPER(TRIM('abcd ef1'))
(CASESPECIFIC) <> LOWER(TRIM('abcd ef1')) (CASESPECIFIC))
THEN 1 ELSE 0 END )
The Teradata Developer's Exchange contains a library of Oracle functions that have been converted to Teradata UDF's that may help you address this problem. With a little effort you could write your own UDF around the isdigit() C function. (isdigit)
It may not be helpful now, but one of the recently announced Teradata 14.0 features is support of regular expressions.
EDIT: Added TD 14 example with REGEXP_INSTR that should solve the problem
SELECT table.column
FROM table
WHERE REGEXP_INSTR(table.column, '[^[digit]])') = 0;

Specify order of (T)SQL execution

I have seen similar questions asked elsewhere on this site, but more in the context of optimization.
I am having an issue with the order of execution of the conditions in a WHERE clause. I have a field which stores codes, most of which are numeric but some of which contain non-numeric characters. I need to do some operations on the numeric codes which will cause errors if attempted on non-numeric strings. I am trying to do something like
WHERE isnumeric(code) = 1
AND CAST(code AS integer) % 2 = 1
Is there any way to make sure that the isnumeric() executes first? If it doesn't, I get an error...
Thanks in advance!
The only place order of evaluation is guaranteed is CASE
WHERE
CASE WHEN isnumeric(code) = 1
THEN CAST(code AS integer) % 2
END = 1
Also just because it passes the isnumeric test doesn't guarantee that it will successfully cast to an integer.
SELECT ISNUMERIC('$') /*Returns 1*/
SELECT CAST('$' AS INTEGER) /*Fails*/
Depending upon your needs you may find these alternatives preferable.
Why not simply do it using LIKE?:
Where Code Not Like '%[^0-9]%'
Btw, either using my solution or using IsNumeric, there are some edge cases which might lead one to using a UDF such as 1,234,567 where IsNumeric will return 1 but Cast will throw an exception.
Why not use a CASE statement to say something like:
WHERE
CASE WHEN isnumeric(code) = 1
THEN CAST(code AS int) % 2 = 1
ELSE /* What ever else if not numeric */ END
You could do it in a case statement in the select clause, then limit by the value in an outer select
select * from (
select
case when isNum = 1 then CAST(code AS integer) % 2 else 0 end as castVal
from (
select
Case when isnumeric(code) = 1 then 1 else 0 end as isNum
from table) t
) t2
where castval = 1