SQL - Condition to flag more than 1 column is not null - sql

My table has 10 columns. Col1, Col2...Col10
Only one of them should have a numeric value > 0 in it. If more than 1 has a value > 0, then I want to create an error. How can I setup a condition to do that?
Where Col1 is > 0 and Col2 = 0, Col3 = 0, Col4 = 0...
Because there are too many columns, the manual way of writing every scenario is too much code.
What is a more efficient way to do this?

A check constraint to ensure not more than one column has a value greater than zero could look like this:
alter table mytable add constraint check_my_columns
check
(
case when col1 > 0 then 1 else 0 end +
case when col2 > 0 then 1 else 0 end +
case when col3 > 0 then 1 else 0 end +
case when col4 > 0 then 1 else 0 end +
...
<= 1
);

Considering the Cols are type Bit (can hold 0 or 1)
You can sum all the columns and if you get a value > 1 then show the error message
Ex:
if (isnull(col1,0) + isnull(col2,0) + isnull(col3,0) + ... + isnull(col10,0) > 1)
begin
Show error message
Return;
end

You can use apply:
select t.*,
(case when s.col > 2 then 'ERROR' else 'OKAY' end) as status
from t cross apply
(select count(*) as cnt
from (values (t.col1), (t.col2), . . . ) v(col)
where col > 0
) s
You can adjust the where clause to count non-NULL values or whatever.

Related

SQL - want to write unique error value per IF condition

I'm handing lots of data and error checking it to ensure that it follows a correct format. So I'm creating a lot of IF conditions and so far I can group lots of conditions but only output one kind of error .. or i end up writing a heck of a lot of code per condition, just to have a crystal clear error output.
Is there a way to put into the IF condition a specific error output string?
Here is my code:
-- set the initial #IntHosp_SUM value
SELECT #IntHosp_SUM = 0
-- Store the sum value to #IntHosp_SUM
SELECT #IntHosp_SUM =
(
(COALESCE (CASE WHEN #Int1_5hosp = 1 THEN COUNT (Int1_5) ELSE NULL END, 0)) +
(COALESCE (CASE WHEN #Int2_5hosp = 1 THEN COUNT (Int2_5) ELSE NULL END, 0)) +
(COALESCE (CASE WHEN #Int3_5hosp = 1 THEN COUNT (Int3_5) ELSE NULL END, 0)) +
(COALESCE (CASE WHEN #Int4_5hosp = 1 THEN COUNT (Int4_5) ELSE NULL END, 0)) +
(COALESCE (CASE WHEN #Int5_5hosp = 1 THEN COUNT (Int5_5) ELSE NULL END, 0)) +
(COALESCE (CASE WHEN #Int6_5hosp = 1 THEN COUNT (Int6_5) ELSE NULL END, 0))
)
FROM ASSSIST2_M0Teacher
-- Check RESUSE_SUM Value against Intervention Entries
SELECT #ErrorID = IsNull(Max(ErrorID) + 1, 1)
FROM ErrorTemp
WHERE (Quest = #DB + #Quest) AND (ValidateID = #ValidateID)
SELECT #Error = 0
IF (#IntHosp_SUM = 1 and #Hosp1_info is null )
or (#IntHosp_SUM = 2 and (#Hosp1_info is null or #Hosp2_info is null))
or (#IntHosp_SUM >=3 and (#Hosp1_info is null or #Hosp2_info is null or #Hosp3_info is null))
or (#IntHosp_SUM = 0 and (#Hosp1_info is not null or #Hosp2_info is not null or #Hosp3_info is not null ))
or (#IntHosp_SUM = 1 and (#Hosp2_info is not null or #Hosp3_info is not null ))
or (#IntHosp_SUM >= 2 and #Hosp3_info is not null)
BEGIN
SELECT #Error = 1
END
BEGIN
IF #Error = 1
INSERT INTO ErrorTemp
(Quest,
ValidateID,
ErrorID,
ErrorType,
ErrorDesc,
Field)
VALUES (#DB + #Quest,
#ValidateID,
#ErrorID,
'Validation',
'Too much or no info present, so incorrect',
'Hosp Info - admissions')
SELECT #ErrorID = #ErrorID + 1
END
I have declared all variables and where they are in the DB, but didn't want to clutter with extra code. You can see that I sum a series of fields if they contain the value 1 .. I don't want to add any other values, just those with '1'.
My IF conditions then check if that if there is a sum of 1, there is one entry. If 2, then two entries .. we have to be thorough and check the inverse of that, so if the sum value is 0, then no entries are made and so forth.
Is there a way to pass a string from the IF / OR condition to the error output so that I can write a unique error per condition?
SQL Server Management Studio 17.2

A SQL query to count how many attributes a record has in common with another record?

I am trying to write a SQL query to take the count of columns with equal value in my schema for each row by comparison to a single record.
Example:
record1: 1, 0, 1, 0, 0
record2: 0, 0, 0, 0, 1
record3: 0, 0, 1, 0, 0
record1 has 2 attributes in common with record2, go through the entire table and order by number of attributes each record has in common with record1
Is there a way to write a SQL statement that will do this? I have only found ways to compare each row and specify which attributes must be of equal value.
You can do:
select t.*,
((case when t.col1 = t1.col1 then 1 else 0 end) +
(case when t.col2 = t1.col2 then 1 else 0 end) +
(case when t.col3 = t1.col3 then 1 else 0 end) +
. . .
) as num_in_common
from t cross join
t t1
where t1.id = 1; -- or however you define "record1"
order by num_in_common desc;
Here's a nice routine you can use in SQL Server that will do this if you'd like. Replace #temp with your table name:
declare #holding table (id int, col1 int, col2 int, col3 int, col4 int, col5 int, num_in_common int)
declare #iterator int = 1
declare #row1col1 int, #row1col2 int, #row1col3 int, #row1col4 int ,#row1col5 int
while #iterator<=(select max(id) from #temp)
begin
if #iterator=1
select #row1col1=col1, #row1col2=col2, #row1col3=col3, #row1col4=col4 ,#row1col5=col5
from #temp where id=#iterator
else
insert #holding
select *, case when col1-#row1col1 = 0 then 1 else 0 end +
case when col2-#row1col2 = 0 then 1 else 0 end +
case when col3-#row1col3 = 0 then 1 else 0 end +
case when col4-#row1col4 = 0 then 1 else 0 end +
case when col5-#row1col5 = 0 then 1 else 0 end
from #temp where id=#iterator
set #iterator=#iterator+1
end
select * from #holding

How to combine two nullable boolean fields in SQL

I have these fields :
ISNULL(
dbo.Features.TheifAlarm,
0
) AS ExtraTheifAlarm,
ISNULL(
dbo.Features.FireAlarm,
0
) AS ExtraFireAlarm,
dbo.Features.TheifAlarm and dbo.Features.FireAlarm are nullable bits. But I'm using EntityFramework and they are nullable boolean in my model. I want to combine them with OR.
This is my attempt :
ISNULL(
dbo.Features.TheifAlarm,
0
)
OR
ISNULL(
dbo.Features.FireAlarm,
0
)
But it didn't work, how can I combine these two columns? Thanks in advance
To 'combine' two fields you can use bitwise operators.
In your case (in the SELECT list):
SELECT (ISNULL(col1, 0) | ISNULL(col2, 0)) AS combinedCol FROM ...
Or as a WHERE condition:
SELECT ... WHERE (ISNULL(col1, 0) = 1 OR ISNULL(col2, 0) = 1)
Actually you don't even need the ISNULL() in the WHERE condition (col1 = 1 evaluates to FALSE (ok, NULL, but treated as FALSE) when col1 is null):
SELECT ... WHERE (col1 = 1 OR col2 = 1)
Edit: Just to add some alternatives:
Simple addition
SELECT IIF((ISNULL(col1, 0) + ISNULL(col2, 0)) > 0, 1, 0)
CASE..WHEN structure (as iamdave mentioned)
SELECT CASE WHEN col1 = 1 THEN 1 ELSE IIF(col2 = 1, 1, 0) END
Another CASE..WHEN
SELECT CASE WHEN (col1 = 1 OR col2 = 1) THEN 1 ELSE 0 END
or with IIF
SELECT IIF(col1 = 1 OR col2 = 1), 1, 0)
I am assuming you want to return a true if either field is true?
If so, you can check for both with a case:
select case when ISNULL(dbo.Features.TheifAlarm,0) = 0 -- If the first is False
then ISNULL(dbo.Features.FireAlarm,0) -- Return the value of the second
else 1 -- Otherwise return True
end as EitherAlarmCheck
If you are looking to filter your dataset for one of these values being True, you can use an OR in your where clause:
select Cols
from Table
where ISNULL(dbo.Features.TheifAlarm,0) = 1
or ISNULL(dbo.Features.FireAlarm,0) = 1

coalesce and a case statement - explanation?

I'm working on a query that doesn't seem to be doing what it's supposed to since being transferred from one SSRS server to another, and the following line in part of the where statement appears to be where the difference is, or at least from what I can find.
where COALESCE(field, -1) = CASE field WHEN 1 THEN 0 ELSE -1 END
I know it's probably a bit generic but can anyone shed any light on what this may be doing? I've read up on coalesce and gather that it finds the first non-null value from the specified list of fields, but I don't understand what the '-1' does there.
I hope it's not too general a question and that someone can give me a clue as to what it may be doing
Without context it's difficult to give a truly helpful answer.
At first glance it looks as though it could be rewritten much more simply from this:
WHERE COALESCE(#field, -1) = CASE #field WHEN 1 THEN 0 ELSE -1 END
to this:
WHERE COALESCE(#field, -1) = -1
If that is true then basically you are saying that if the field is null or the field equals -1 then the condition is true otherwise it's false.
Here are some tests to try to prove this:
-- Original
DECLARE #field INT
SELECT 1 WHERE COALESCE(#field, -1) = CASE #field WHEN 1 THEN 0 ELSE -1 END
SET #field = -1
SELECT 1 WHERE COALESCE(#field, -1) = CASE #field WHEN 1 THEN 0 ELSE -1 END
SET #field = 0
SELECT 1 WHERE COALESCE(#field, -1) = CASE #field WHEN 1 THEN 0 ELSE -1 END
SET #field = 1
SELECT 1 WHERE COALESCE(#field, -1) = CASE #field WHEN 1 THEN 0 ELSE -1 END
SET #field = 2
SELECT 1 WHERE COALESCE(#field, -1) = CASE #field WHEN 1 THEN 0 ELSE -1 END
SET #field = 3
SELECT 1 WHERE COALESCE(#field, -1) = CASE #field WHEN 1 THEN 0 ELSE -1 END
--Rewritten
DECLARE #field INT
SELECT 1 WHERE COALESCE(#field, -1) = -1
SET #field = -1
SELECT 1 WHERE COALESCE(#field, -1) = -1
SET #field = 0
SELECT 1 WHERE COALESCE(#field, -1) = -1
SET #field = 1
SELECT 1 WHERE COALESCE(#field, -1) = -1
SET #field = 2
SELECT 1 WHERE COALESCE(#field, -1) = -1
SET #field = 3
SELECT 1 WHERE COALESCE(#field, -1) = -1
Both sets of queries in this test give the same results, but as I said without context and realistic test data it's difficult to know if there was a reason why the query was written in the way that it originally was.
Here is another example from a different perspective, using a LEFT JOIN:
DECLARE #MainTable AS TABLE(ident INT)
DECLARE #PossibleNullTable AS TABLE(mainIdent INT, field INT)
INSERT INTO #MainTable(ident) VALUES(1)
INSERT INTO #MainTable(ident) VALUES(2)
INSERT INTO #MainTable(ident) VALUES(3)
INSERT INTO #MainTable(ident) VALUES(4)
INSERT INTO #MainTable(ident) VALUES(5)
INSERT INTO #PossibleNullTable(mainIdent, field) VALUES(1,-1)
INSERT INTO #PossibleNullTable(mainIdent, field) VALUES(1,1)
INSERT INTO #PossibleNullTable(mainIdent, field) VALUES(1,0)
INSERT INTO #PossibleNullTable(mainIdent, field) VALUES(2,0)
INSERT INTO #PossibleNullTable(mainIdent, field) VALUES(3,1)
INSERT INTO #PossibleNullTable(mainIdent, field) VALUES(5,-1)
--Original
SELECT *
FROM #MainTable mt
LEFT JOIN #PossibleNullTable pnt
ON mt.ident = pnt.mainIdent
WHERE COALESCE(field, -1) = CASE field WHEN 1 THEN 0 ELSE -1 END
--Original Result
ident mainIdent field
1 1 -1
4 NULL NULL
5 5 -1
--Rewritten
SELECT *
FROM #MainTable mt
LEFT JOIN #PossibleNullTable pnt
ON mt.ident = pnt.mainIdent
WHERE COALESCE(field, -1) = -1
--Rewritten Result
ident mainIdent field
1 1 -1
4 NULL NULL
5 5 -1
Again both queries in this test give the same results.
the first non-null value from the specified list of fields
This means the list of fields between the parenthesis. For instance:
COALESCE(col1,col2,col3,-1)
means that if col1 is not null then use this, else check col2. If col2 is null then check col3. If that is null too then use -1 as the value.
In your example, COALESCE(field, -1) is equivalent to ISNULL(field, -1)
In my example COALESCE(col1,col2,col3,-1) is equivalent to ISNULL(ISNULL(ISNULL(col1, col2), col3), -1)
This is a bit complex expression used in where clause, that suggests
use field column, replace NULL values with -1 & compare all these values of field column with the case expression such as,
here at first, we have to consider that there is no null value in field column as we have replaced all null with -1 using coalesce().
then in case statement, if the values is 1 then it is replace by 0. So 0 is checked with coalesce(field,-1), if it is also 0, then expression is true else false.
Similar for field value -1 using case.

Retrieve Individual character from varchar field in SQL Server

How can I retrieve individual characters of field values (e.g A - C - D from value ACD) and manipulate in SQL Server if I have a column of type varchar(4) with following variable length values.
Column
---
ACD
BC
CD
Thanks
select case when CHARINDEX('A', columnName, 1) > 0 then 1 else 0 end has_a,
case when CHARINDEX('B', columnName, 1) > 0 then 1 else 0 end has_b,
case when CHARINDEX('C', columnName, 1) > 0 then 1 else 0 end has_c,
case when CHARINDEX('D', columnName, 1) > 0 then 1 else 0 end has_d
from tableName
You may use SUBSTRING:
SELECT SUBSTRING('ABC',1,1)
The first param is the input string, the second one is the start index 1-based, and the third is the length of the result.
So if you have a column Col1 in a table Table1:
SELECT SUBSTRING(Col1,1,1),
SUBSTRING(Col1,2,1),
SUBSTRING(Col1,3,1),
SUBSTRING(Col1,4,1)
FROM Table1
You will get an empty string if for instance you have three characters and you try to get the fourth.
Fiddle Example Here