I have a database where the creator has used decided to use smallint instead of bit for a true/false scenario. False is represented with 0. True is represented with -1. I would like to translate this, preferably in a simpler syntax than CASE. Is there any way I can do something like this:
SELECT (column == -1) 'value_as_bool'
FROM myTable
Which would (in most languages) translate the column back to a true/false column...
I think the simplest solution would be to just cast the int value to a bit. 0 will be False everything else True.
SELECT CAST(column As Bit) 'value_as_bool'
FROM myTable
You can use IIF function. Usage; IIF ( boolean_expression, true_value, false_value )
In a Case, the statement would be:
Case when myTable.column = -1 then 0 else 1 end as [NAME]
You can replace 0 and 1 with FALSE and TRUE, which uses the BOOLEAN/bit type in SQL, if you wish :)
You can declare a variable a return it after set in a IF/ELSE Statement like :
DECLARE #Result Bit
IF column =-1
SET #Result = 0
ELSE
SET #Result = 1
RETURN #Result
Here for more Info about bool values in SQL:
In Management Studio it displays as a false/true value (at least in
recent versions).
When accessing the database through ASP.NET it will expose the field
as a boolean value.
Hope this help you.
Related
I am trying to optimize a humongous SQL query that was written by a self taught developer that used a ton of functions instead of JOINS. Anyway, I am having trouble displaying a space or a empty string('') when there is no value in the field selected. I've included only the SELECT in question. I am having the weirdest problem or just overlooking the correct answer in troubleshooting. Whenever I use COALESCE, when the field is supposed to be a blank string, it displays a zero. And when I use IS NULL, I get back NULL. All info online seems to point toward using COALESCE(value, '') as depicted in the code. But I am getting a 0 instead of ''. Does anyone see what I'm doing wrong? I'm using SSMS.
SELECT
pss8.dbo.xml_StripIllegalChars(dbo.rpt_get_series_volume(b.bookkey)) AS p_seriesvol --SELECT to be replaced that works but is slow due to function use I am told
,COALESCE(bd.seriesvolume, '') AS p_seriesvol --my SELECT that won't work!
FROM
bookdetail bd
WHERE
--bd.bookkey='303177'
bd.bookkey='6002'
The bookkeys at the bottom are for testing as I know the top one returns a 1 and the bottom one returns a '' previously when it worked. The SELECT above my commented SELECT is the code that works but is slow... According to what I read online, I am saying 'if there isn't a series volume number, then it equals an empty string.' Does COALESCE not work like this? Can it only return a 0 if the field has no value, or in this case, has no volume number? All help much appreciated. I'm very curious to hear a solution!
Here's more intel. This is how the this SELECT works:
pss8.dbo.xml_StripIllegalChars(dbo.rpt_get_series_volume(b.bookkey)) AS p_seriesvol
The
.rpt_get_series_vol
function manages to create an empty string with this code... Does this reveal anything?
DECLARE #RETURN
VARCHAR(5)
DECLARE #v_desc
VARCHAR(5)
DECLARE #i_volumenumber INT
SELECT #i_volumenumber = volumenumber
FROM bookdetail
WHERE bookkey = #i_bookkey and volumenumber <> 0
IF #i_volumenumber > 0
BEGIN
SELECT #RETURN = CAST(#i_volumenumber as varchar(5))
END
ELSE
BEGIN
SELECT #RETURN = ''
END
RETURN #RETURN
END
As you are looking for a '0' not a NULL COALESCE()is not useful, instead use a simple CASE:
select
...,
case bd.seriesvolume when '0' then '' else bd.seriesvolume end as p_seriesvol
from
...
Or if you want '' for 0 or NULL
case when bd.seriesvolume is null or bd.seriesvolume = '0' then '' else bd.seriesvolume end as p_seriesvo
COALESCE() function returns the 1st non null value
SELECT COALESCE(NULL, NULL, 'third_value', 'fourth_value'); returns the third value because the third value is the first value that is not null.
So in your case COALESCE(bd.seriesvolume, '') AS p_seriesvol if seriesvolume colum value is null then it will return blank string
What is the best way to determine whether or not a field's value is an integer in SQL Server (2000/2005/2008)?
IsNumeric returns true for a variety of formats that would not likely convert to an integer. Examples include '15,000' and '15.1'.
You can use a like statement but that only appears to work well for fields that have a pre-determined number of digits...
select * where zipcode like '[0-9][0-9][0-9][0-9][0-9]'
I could write a user defined function that attempts to convert a varchar parameter to an int within a try/catch block but I'm checking with the community to see if someone has come across any succient methods to achieve this goal - preferably one that can be used within the where clause of a SQL statement without creating other objects.
Late entry that handles negative
ISNUMERIC(zipcode + '.0e0') --integer
ISNUMERIC(zipcode + 'e0') --decimal
For more see this
1 approach is
zipcode NOT LIKE '%[^0-9]%'
Double negatives, got to love 'em!
If SQL Server 2005+, I'd enable CLR and create the function to support regexes. For SQL Server 2000, see this article for creating a UDF to do the same thing.
Then I'd use the regex: ^\d{5}$
This expression gives 1 for an integer value and 0 otherwise
floor((floor(abs(zipcode)))/abs(zipcode))
Why not just use the following? I can't see to find any cases where it fails.
1 = integer
0 = not integer
null = non-numeric
DECLARE #TestValue nvarchar(MAX)
SET #TestValue = '1.04343234e5'
SELECT CASE WHEN ISNUMERIC(#TestValue) = 1
THEN CASE WHEN ROUND(#TestValue,0,1) = #TestValue
THEN 1
ELSE 0
END
ELSE null
END AS Analysis
It looks like this question needs an updated answer.
Limiting the answer to the question title:
where ISNUMERIC(zipcode) = 1
and zipcode - FLOOR(zipcode) = 0
Expounding based on the text of the question...
Currently-supported versions of SQL Server all support/include the TRY-CONVERT function.
declare #a varchar(100)
set #a = '-1.2a'
--set #a = '-1.2'
--set #a = '-1'
--set #a = '-1.0'
--set #a = '-0'
--set #a = '0'
--set #a = '1'
select #a as 'Value'
, ISNUMERIC(#a) as ISNUMERIC
, case when ISNUMERIC(#a) = 1 and #a - FLOOR(#a) = 0 then 1 else 0 end as ISINTEGER
, case when try_convert(int, #a) >= 0 and left(#a, 1) <> '-' then 1 else 0 end as ISWHOLENUMBER
, case when try_convert(int, #a) > 0 then 1 else 0 end as ISCOUNTINGNUMBER
You'll notice that TRY_CONVERT(INT, -1.0) returns NULL. So TRY_CONVERT(INT, #a) IS NOT NULL is not quite right for ISINTEGER.
case when ISNUMERIC(#a) = 1 and #a - FLOOR(#a) = 0 then 1 else 0 end as ISINTEGER
...works because if ISNUMERIC(#a) = 1 is false, FLOOR(#a) is not evaluated. Reversing the order...
case when #a - FLOOR(#a) = 0 and ISNUMERIC(#a) = 1 then 1 else 0 end as ISINTEGER
...generates an error when the value (#a) is not numeric.
So, for the case of zipcode, assuming you want to verify that a 5-digit zip code is a number and it must be 5 digits (so it can't be zero or less) and would never contain a decimal point (so you don't need to know if 12345.000 is an integer):
where try_convert(int, zipcode) > 0
and len(zipcode) = 5
I came up with the perfect answer for this on another StackO question.
It also proves you cannot use ".0e0" like one user suggests here.
It does so without CLR or non-scalar functions.
Please check it out: https://stackoverflow.com/a/10645764/555798
After moving to sql 2008, I was struggling with isnumeric('\8') returning true but throwing an error when casting to an integer. Apparently forward slash is valid currency for yen or won - (reference http://www.louiebao.net/blog/200910/isnumeric/)
My solution was
case when ISNUMERIC(#str) > 0 and not rtrim(#str) LIKE '[^0-9]%' and not rtrim(#str) LIKE '%[^0-9]' and not rtrim(#str) LIKE '[^0-9]%' then rtrim(#str) else null end
See whether the below code will help.
In the below values only 9, 2147483647, 1234567 are eligible as
Integer. We can create this as function and can use this.
CREATE TABLE MY_TABLE(MY_FIELD VARCHAR(50))
INSERT INTO MY_TABLE
VALUES('9.123'),('1234567'),('9'),('2147483647'),('2147483647.01'),('2147483648'), ('2147483648ABCD'),('214,7483,648')
SELECT *
FROM MY_TABLE
WHERE CHARINDEX('.',MY_FIELD) = 0 AND CHARINDEX(',',MY_FIELD) = 0
AND ISNUMERIC(MY_FIELD) = 1 AND CONVERT(FLOAT,MY_FIELD) / 2147483647 <= 1
DROP TABLE MY_TABLE
I did it using a Case statement:
Cast(Case When Quantity/[# of Days]= Cast(Quantity/[# of Days] as int) Then abs(Quantity/[# of Days]) Else 0 End as int)
To test whether the input value is an integer or not we can use SQL_VARIANT_PROPERTY function of SQL SERVER.
The following SQL Script will take input and test it whether the data type turns out to be integer or not
declare #convertedTempValue bigint, #inputValue nvarchar(255) = '1' --Change '1' to any input value
set #convertedTempValue = TRY_PARSE(#inputValue as bigint) --we trying to convert to bigint
declare #var3 nvarchar(255) = cast (SQL_VARIANT_PROPERTY(#convertedTempValue,'BaseType') as nvarchar(255)) --we using SQL_VARIANT_PROPERTY to find out datatype
if ( #var3 like '%int%')
begin
print 'value is integer'
end
else
begin
print 'value is non integer'
end
go
Really late to this but would this work?
select * from from table
where (ISNUMERIC(zipcode) = 0 OR zipcode like '%.%')
Filters out items that are integers.
Maybe you should only store integer data in integer datatypes.
I am currently working on a tool to help my users port their SQL code to SQL-Server 2005. For this purpose, I parse the SQL into a syntax tree, analyze it for constructs which need attentions, modify it and transform it back into T-SQL.
On thing that I want to support, is the "bools are values too" semantics of other RDBMS. For example, MS-Access allows me to write select A.x and A.y as r from A, which is impossible in T-SQL because:
Columns can't have boolean type (column values can't be and'ed)
Logical predicates can not be used where expressions are expected.
Therefore, my transformation routine converts the above statement into this:
select case
when (A.x<>0) and (A.y<>0)
then -1
when not((A.x<>0) and (A.y<>0))
then 0
else
null
end as r
from A;
Which works, but is annoying, because I have to duplicate the logical expression (which can be very complex or contain subqueries etc.) in order to distinguish between true, false and unknown - the latter shall map to null. So I wonder if the T-SQL pro's here know a better way to achieve this?
UPDATE:
I would like to point out, that solutions which try to keep the operands in the integer domain have to take into account, that some operands may be logical expressions in the first place. This means that a efficient solution to convert a bool to a value is stil required. For example:
select A.x and exists (select * from B where B.y=A.y) from A;
I don't think there's a good answer, really it's a limitation of TSQL.
You could create a UDF for each boolean expression you need
CREATE FUNCTION AndIntInt
(
#x as int,#y as int
)
RETURNS int
AS
BEGIN
if (#x<>0) and (#y<>0)
return -1
if not((#x<>0) and (#y<>0))
return 0
return null
END
used via
select AndIntInt(A.x,A.y) as r from A
Boolean handling
Access seems to use the logic that given 2 booleans
Both have to be true to return true
Either being false returns false (regardless of nulls)
Otherwise return null
I'm not sure if this is how other DBMS (Oracle, DB2, PostgreSQL) deal with bool+null, but this answer is based on the Access determination (MySQL and SQLite agree). The table of outcomes is presented below.
X Y A.X AND B.Y
0 0 0
0 -1 0
0 (null) 0
-1 0 0
-1 -1 -1
-1 (null) (null)
(null) 0 0
(null) -1 (null)
(null) (null) (null)
SQL Server helper 1: function for boolean from any "single value"
In SQL Server in general, this function will fill the gap for the missing any value as boolean functionality. It returns a ternary result, either 1/0/null - 1 and 0 being the SQL Server equivalent of true/false (without actually being boolean).
drop function dbo.BoolFromAny
GO
create function dbo.BoolFromAny(#v varchar(max)) returns bit as
begin
return (case
when #v is null then null
when isnumeric(#v) = 1 and #v like '[0-9]%' and (#v * 1.0 = 0) then 0
else 1 end)
end
GO
Note: taking Access as a starting point, only the numeric value 0 evaluates to FALSE
This uses some SQL Server tricks
everything is convertible to varchar. Therefore only one function taking varchar input is required.
isnumeric is not comprehensive, '.' returns 1 for isnumeric but will fail at #v * 1.0, so an explicit test for LIKE [0-9]%`` is required to "fix" isnumeric.
#v * 1.0 is required to overcome some arithmetic issues. If you pass the string "1" into the function without *1.0, it will bomb
Now we can test the function.
select dbo.BoolFromAny('abc')
select dbo.BoolFromAny(1)
select dbo.BoolFromAny(0) -- the only false
select dbo.BoolFromAny(0.1)
select dbo.BoolFromAny(-1)
select dbo.BoolFromAny('')
select dbo.BoolFromAny('.')
select dbo.BoolFromAny(null) -- the only null
You can now safely use it in a query against ANY SINGLE COLUMN, such as
SELECT dbo.BoolFromAny(X) = 1
SQL Server helper 2: function to return result of BOOL AND BOOL
Now the next part is creating the same truth table in SQL Server. This query shows you how two bit columns interact and the simple CASE statement to produce the same table as Access and your more complicated one.
select a.a, b.a,
case
when a.a = 0 or b.a = 0 then 0
when a.a = b.a then 1
end
from
(select 1 A union all select 0 union all select null) a,
(select 1 A union all select 0 union all select null) b
order by a.a, b.a
This is easily expressed as a function
create function dbo.BoolFromBits(#a bit, #b bit) returns bit as
begin
return case
when #a = 0 or #b = 0 then 0
when #a = #b then 1
end
end
SQL Server conversion of other expressions (not of a single value)
Due to lack of support for bit-from-boolean conversion, expressions that are already [true/false/null] in SQL Server require repetition in a CASE statement.
An example is a "true boolean" in SQL Server, which cannot be the result for a column.
select A > B -- A=B resolves to one of true/false/null
from C
Needs to be expressed as
select case when A is null or B is null then null when A > B then 1 else 0 end
from C
But if A is not a scalar value but a subquery like (select sum(x)...), then as you can see A will appear twice and be evaluated twice in the CASE statement (repeated).
FINAL TEST
Now we put all the conversion rules to use in this long expression
SELECT X AND Y=Z AND C FROM ..
( assume X is numeric 5, and C is varchar "H" )
( note C contributes either TRUE or NULL in Access )
This translates to SQL Server (chaining the two functions and using CASE)
SELECT dbo.BoolFromBits(
dbo.BoolFromBits(dbo.BoolFromAny(X), CASE WHEN Y=Z then 1 else 0 end),
dbo.BoolFromAny(C))
FROM ...
Access Bool or bool
For completeness, here is the truth table for Access bool OR bool. Essentially, it is the opposite of AND, so
Both have to be false to return false
Either being true returns true (regardless of nulls)
Otherwise return null
The SQL SERVER case statement would therefore be
case
when a.a = 1 or b.a = 1 then 1
when a.a = b.a then 0
end
(the omission of an ELSE clause is intentional as the result is NULL when omitted)
EDIT: Based on additional information added to the Question and comments made on one of the suggested Answers, I am reformulating this answer:
If you are porting to SQL Server then I would expect that you are also transforming the data to match SQL Server types. If you have a boolean field then True, False, and Unknown map to 1, 0, and NULL as a NULLable BIT field.
With this in mind, you only need to worry about transforming pure boolean values. Expressions such as:
exists (select * from B where B.y=A.y)
and:
A.x in (1,2,3)
are already in a workable form. Meaning, statements like:
IF (EXISTS(SELECT 1 FROM Table))
and:
IF (value IN (list))
are already correct. So you just need to worry about the fact that "1" for "True" is not by itself enough. Hence, you can convert "1" values to boolean expressions by testing if they are in fact equal to "1". For example:
IF (value = 1)
is the equivalent of what you previously had as:
IF (value)
Putting all of this together, you should be able to simply translate all instances of pure boolean values of the old code into boolean expressions in the form of "value = 1" since a 1 will produce a True, 0 will produce False, and NULL will give you False.
HOWEVER, the real complexity is that SELECTing the value vs. testing with it via a WHERE condition is different. Boolean expressions evaluate correctly in WHERE conditions but have no direct representation to SELECT (especially since NULL / Unknown isn't really boolean). So, you can use the "Value = 1" translation in WHERE conditions but you will still need a CASE statement if you want to SELECT it as a result.
As mentioned briefly a moment ago, since NULL / Unknown isn't truly boolean, it is meaningless trying to convert "NULL AND NULL" to NULL for the purposes of a WHERE condition. In effect, NULL is truly FALSE since it cannot be determined to be TRUE. Again, this might be different for your purposes in a SELECT statement which is again why the CASE statement is your only choice there.
What is the best way to determine whether or not a field's value is an integer in SQL Server (2000/2005/2008)?
IsNumeric returns true for a variety of formats that would not likely convert to an integer. Examples include '15,000' and '15.1'.
You can use a like statement but that only appears to work well for fields that have a pre-determined number of digits...
select * where zipcode like '[0-9][0-9][0-9][0-9][0-9]'
I could write a user defined function that attempts to convert a varchar parameter to an int within a try/catch block but I'm checking with the community to see if someone has come across any succient methods to achieve this goal - preferably one that can be used within the where clause of a SQL statement without creating other objects.
Late entry that handles negative
ISNUMERIC(zipcode + '.0e0') --integer
ISNUMERIC(zipcode + 'e0') --decimal
For more see this
1 approach is
zipcode NOT LIKE '%[^0-9]%'
Double negatives, got to love 'em!
If SQL Server 2005+, I'd enable CLR and create the function to support regexes. For SQL Server 2000, see this article for creating a UDF to do the same thing.
Then I'd use the regex: ^\d{5}$
This expression gives 1 for an integer value and 0 otherwise
floor((floor(abs(zipcode)))/abs(zipcode))
Why not just use the following? I can't see to find any cases where it fails.
1 = integer
0 = not integer
null = non-numeric
DECLARE #TestValue nvarchar(MAX)
SET #TestValue = '1.04343234e5'
SELECT CASE WHEN ISNUMERIC(#TestValue) = 1
THEN CASE WHEN ROUND(#TestValue,0,1) = #TestValue
THEN 1
ELSE 0
END
ELSE null
END AS Analysis
It looks like this question needs an updated answer.
Limiting the answer to the question title:
where ISNUMERIC(zipcode) = 1
and zipcode - FLOOR(zipcode) = 0
Expounding based on the text of the question...
Currently-supported versions of SQL Server all support/include the TRY-CONVERT function.
declare #a varchar(100)
set #a = '-1.2a'
--set #a = '-1.2'
--set #a = '-1'
--set #a = '-1.0'
--set #a = '-0'
--set #a = '0'
--set #a = '1'
select #a as 'Value'
, ISNUMERIC(#a) as ISNUMERIC
, case when ISNUMERIC(#a) = 1 and #a - FLOOR(#a) = 0 then 1 else 0 end as ISINTEGER
, case when try_convert(int, #a) >= 0 and left(#a, 1) <> '-' then 1 else 0 end as ISWHOLENUMBER
, case when try_convert(int, #a) > 0 then 1 else 0 end as ISCOUNTINGNUMBER
You'll notice that TRY_CONVERT(INT, -1.0) returns NULL. So TRY_CONVERT(INT, #a) IS NOT NULL is not quite right for ISINTEGER.
case when ISNUMERIC(#a) = 1 and #a - FLOOR(#a) = 0 then 1 else 0 end as ISINTEGER
...works because if ISNUMERIC(#a) = 1 is false, FLOOR(#a) is not evaluated. Reversing the order...
case when #a - FLOOR(#a) = 0 and ISNUMERIC(#a) = 1 then 1 else 0 end as ISINTEGER
...generates an error when the value (#a) is not numeric.
So, for the case of zipcode, assuming you want to verify that a 5-digit zip code is a number and it must be 5 digits (so it can't be zero or less) and would never contain a decimal point (so you don't need to know if 12345.000 is an integer):
where try_convert(int, zipcode) > 0
and len(zipcode) = 5
I came up with the perfect answer for this on another StackO question.
It also proves you cannot use ".0e0" like one user suggests here.
It does so without CLR or non-scalar functions.
Please check it out: https://stackoverflow.com/a/10645764/555798
After moving to sql 2008, I was struggling with isnumeric('\8') returning true but throwing an error when casting to an integer. Apparently forward slash is valid currency for yen or won - (reference http://www.louiebao.net/blog/200910/isnumeric/)
My solution was
case when ISNUMERIC(#str) > 0 and not rtrim(#str) LIKE '[^0-9]%' and not rtrim(#str) LIKE '%[^0-9]' and not rtrim(#str) LIKE '[^0-9]%' then rtrim(#str) else null end
See whether the below code will help.
In the below values only 9, 2147483647, 1234567 are eligible as
Integer. We can create this as function and can use this.
CREATE TABLE MY_TABLE(MY_FIELD VARCHAR(50))
INSERT INTO MY_TABLE
VALUES('9.123'),('1234567'),('9'),('2147483647'),('2147483647.01'),('2147483648'), ('2147483648ABCD'),('214,7483,648')
SELECT *
FROM MY_TABLE
WHERE CHARINDEX('.',MY_FIELD) = 0 AND CHARINDEX(',',MY_FIELD) = 0
AND ISNUMERIC(MY_FIELD) = 1 AND CONVERT(FLOAT,MY_FIELD) / 2147483647 <= 1
DROP TABLE MY_TABLE
I did it using a Case statement:
Cast(Case When Quantity/[# of Days]= Cast(Quantity/[# of Days] as int) Then abs(Quantity/[# of Days]) Else 0 End as int)
To test whether the input value is an integer or not we can use SQL_VARIANT_PROPERTY function of SQL SERVER.
The following SQL Script will take input and test it whether the data type turns out to be integer or not
declare #convertedTempValue bigint, #inputValue nvarchar(255) = '1' --Change '1' to any input value
set #convertedTempValue = TRY_PARSE(#inputValue as bigint) --we trying to convert to bigint
declare #var3 nvarchar(255) = cast (SQL_VARIANT_PROPERTY(#convertedTempValue,'BaseType') as nvarchar(255)) --we using SQL_VARIANT_PROPERTY to find out datatype
if ( #var3 like '%int%')
begin
print 'value is integer'
end
else
begin
print 'value is non integer'
end
go
Really late to this but would this work?
select * from from table
where (ISNUMERIC(zipcode) = 0 OR zipcode like '%.%')
Filters out items that are integers.
Maybe you should only store integer data in integer datatypes.
Simple query, possibly impossible but I know there are some clever people out there :)
Given a boolean parameter, I wish to define my where clause to either limit a certain column's output - or do nothing.
So, given parameter #bit = 1 this would be the result:
where column = 1
given parameter #bit = 0 this would be the result:
where column = 1 or 0
i.e. have no effect/show all results (column is a bit field)
I'm not wanting dynamic sql - I can settle for fixing this in code but I just wondered if there's some clever magic that would make the above neat and simple.
Is there? I'm using sql server.
cheers :D
The answer column = 1 or #bit = 0 works if column may only be 0 or 1. If column may be any value you want: column = 1 or #bit = 0 and column = 0.
SELECT *
FROM mytable
WHERE column = 1 OR #bit = 0
If you have an index on column1, this one will be more efficient:
SELECT *
FROM mytable
WHERE column = 1 AND #bit = 1
UNION ALL
SELECT *
FROM mytable
WHERE #bit = 0
See this article in my blog for performance comparison of a single WHERE condition vs. UNION ALL:
IN with a comma separated list: SQL Server
where column BETWEEN #bit AND 1
select *
from MyTable
where (#bit = 0 OR MyColumn = 1)
select ...
from [table]
where #bit = 0 or (column = #bit)
I had come up with a different answer and felt dumb when seeing the consensus answer.
So, just for yucks, compared the two using my own database. I don't really know if they are really comparable, but my execution plans give a slight advantage to my goofy answer:
select *
from MyTable
where column <> case #bit when 1 then 0 else -1 end
I realize indices, table size, etc. can affect this.
Also, realized you probably can't compare a bit to a -1...
Just thought I'd share.
try this
select ...
from table
where column = case when #bit = 0 then 0 else column end
this works no matter what the datatype of column is (could even be a string, for example). If it were, of course, it would be a different default value (not 0)
WHERE column >= #bit
However, this only works for > 0 values in a numeric column. #bit will be implicitly cast to int, smallint etc because of data type precedence.