Check any 4 out 10 conditions are satisfied in SQL - sql

I apologize for asking a very vague question but here it is.
I have to write a SQL query in SQL Server as follows.
I have a table say tblA having 10 columns from col1, col2,.....col10.
Each column is not null and definitely holds some value and all of type int.
The query should be to select all such records in which at least 4 columns are matching with given filter criteria, where the filter criteria has values for all 10 columns.
I googled dint get a clue. It needs to be done in SQL server and single query.
Please suggest.
Thanks in advance.

SELECT *
FROM
yourtable
WHERE
case col1 when #value1 then 1 else 0 end +
case col2 when #value2 then 1 else 0 end +
...
case col10 when #value10 then 1 else 0 end
>=4

You can use CASE expressions to determine if four or more columns match:
SELECT *
FROM YourTable
WHERE CASE WHEN Col1 = Filter1 THEN 1 ELSE 0 END +
CASE WHEN Col2 = Filter2 THEN 1 ELSE 0 END +
....
CASE WHEN Col10 = Filter10 THEN 1 ELSE 0 END >= 4

You can do something like this:
select *
from (select t.*,
(case when col1 <whatever> then 1 else 0 end) as col01_matches,
(case when col2 <whatever> then 1 else 0 end) as col02_matches,
. . .
from t
) t
where (col1_matches + col2_matches + col3_matches . . .) >= 4
This creates a separate indicator variable for each match. You could also do the sum in the subquery, in a single variable. I would prefer to have each match separately, just in case the logic gets more complicated or I want to see what matches.

Related

SQL Ignore non matching columns

I'm trying to develop a stored procedure which does a select on a table. The stored procedure has 4 inputs: Input1, Input2, Input3, Input4.
Table has 4 columns: Col1,Col2,Col3, Col4.
The requirement is if there is no match on all selects, we need to ignore that and select pick next one:
Use case:
Select *
from table
where Col1=Input1
and Col2=Input2
and Col3=Input3
and Col4=Input4;
If there are no returns for the condition due to Col2 not equal to Input2, select needs to ignore it and try to match on others like:
Select *
from table
where Col1=Input1
and Col3=Input3
and Col4=Input4;
It should go like that till last possible option for the response:
Select *
from table
where Col1=Input1;
Please assist if there is a way and thanks in advance.
As it's mentioned in the comments, the question is a bit vague so I'm afraid any answer would be a bit vague too. You could do something like this:
select top 1 *,
case when Coll=input1 then 1 else 0 end as match1,
case when Col2=input2 then 1 else 0 end as match2,
case when Col3=input3 then 1 else 0 end as match3,
case when Col4=input4 then 1 else 0 end as match4
from table
order by match1+match2+match3+match4 desc
this is assuming SQL server as the DBMS (if it is Oracle, you may need to use LIMIT instead of TOP, for instance) and I also assumed that the columns have all the same weight in terms of matching.
Also, I'm assuming that you want only one of the best matches, if not you may need to do some changes to the TOP and/or use a where clause.
Finally, You may want to use isnull() if your columns and/or inputs are nullable.
Hmmm . . . One method is:
select t.*
from (select t.*,
sum(case when condition1 then 1 else 0 end) over () as c_1,
sum(case when condition1 and condition2 then 1 else 0 end) over () as c_12,
sum(case when condition1 and condition2 and condition3 then 1 else 0 end) over () as c_123,
sum(case when condition1 and condition2 and condition3 and condition4 then 1 else 0 end) over () as c_1234
from t
) t
where (c_1234 > 0 and condition1 and condition2 and condition3 and condition4) or
(c_1234 = 0 and c_123 > 0 and condition1 and condition2 and condition3) and
(c_123 = 0 and c_12 > 0 and condition1 and condition2 ) and
(c_12 = 0 and c_1 > 0 and condition1 ) ;
Depending on the conditions and other considerations -- such as whether you only expect one row -- there might be simpler methods.

How to do countIf() in Oracle

How do I select a variable which gives an output the same as the excel function:
COUNTIFS(A1:D1,"<25", A1:D1, ">16")?
I.e. to count the number of times the values in my four fields are between 16 and 25.
You can do this with count() and case expressions. Note: case has an optional else clause; if it is not used, then the default else "value" is null. Count only counts non-null values, so you can combine these observations to write compact code. WHAT is counted doesn't matter; so the case expression may return a number (1 is usual, but 0 is just as valid and will give the same result, since the values are COUNTED, not SUMMED) - but you could also have a string like 'x' or a date in the case expression. Just to illustrate that, I will use a string.
select count( case when col > 16 and col < 25 then 'x' end ) as ct from your_table;
Here your_table is the name of your table, col is the name of the column containing the values, and ct is the name of the resulting column (the label for the count of values that satisfy your condition).
Of course, in a database you can get the same result more easily:
select count(*) as ct from your_table where col > 16 and col < 25;
Note, though, that the values are in one column.
If instead you have a table with four COLUMNS and many rows, and all the values in all columns are numbers, and you want to add a column showing how many values are strictly between 16 and 25 IN EACH ROW, the solution is different (but it uses the same ideas):
select col1, col2, col3, col4,
case when col1 > 16 and col1 < 25 then 1 else 0 end +
case when col2 > 16 and col2 < 25 then 1 else 0 end +
case when col3 > 16 and col3 < 25 then 1 else 0 end +
case when col4 > 16 and col4 < 25 then 1 else 0 end as ct
from my_table;
You would do this in a SQL query using case:
select sum(case when col between 16 and 25 then 1 else 0 end)
from t;
Note that between is inclusive in SQL, not exclusive, so based on your code logic:
select sum(case when col > 16 and col < 25 then 1 else 0 end)
from t;
Think, you have a 'user_table', where some of the user's 'status' are active (code-11) and others are inactive (code-22). You can count active and inactive with this sql below.
select count(case when status = 11 then 1 end) u_active, count(case when status = 22 then 1 end) u_inactive, from user_table;

Sum on multiple columns with nullable values

I have to edit a stored procedure who has to return the sums of three columns having nullable values. If there is a null value, I need to cast it to 0
Here is a screenshot of data :
And here is the originial request using the first column only :
SELECT SUM(reglProj.Montant) /* SUM of 'Montant', 'FraisMagasing', 'FraisVendeur' instead */ AS SommeReglement
FROM Projet.LigneEcheancierProjet ligne
INNER JOIN Projet.ReglementProjetEcheance reglProj ON reglProj.LigneEcheancierProjetId = ligne.LigneEcheancierProjetId
....
Do you have some best practices using the sum and case conditions in T-SQL ?
--ANSI standard
SELECT SUM(COALESCE(col1,0)) + SUM(COALESCE(col2,0)) + SUM(COALESCE(col3,0))
--SQL Server Style
SELECT SUM(ISNULL(col1,0)) + SUM(ISNULL(col2,0)) + SUM(ISNULL(col3,0))
--The one wthout functions. It will work the same as previous OR FASTER.
SELECT SUM(CASE WHEN col1 IS NULL THEN 0 ELSE col1 END) + SUM(CASE WHEN col2 IS NULL THEN 0 ELSE col2 END) + SUM(CASE WHEN col3 IS NULL THEN 0 ELSE col3 END)
Choose one for yourself.
OR you might need following (if you want to add sums by row):
--ANSI standard
SELECT SUM(COALESCE(col1,0) +COALESCE(col2,0) + COALESCE(col3,0))
--SQL Server Style
SELECT SUM(ISNULL(col1,0)+ ISNULL(col2,0) + ISNULL(col3,0))
--The one wthout functions. It will work the same as previous OR FASTER.
SELECT SUM(CASE WHEN col1 IS NULL THEN 0 ELSE col1 END + CASE WHEN col2 IS NULL THEN 0 ELSE col2 END + CASE WHEN col3 IS NULL THEN 0 ELSE col3 END)
In Sql Server, (and probably in most if not all relational databases) the SUM Aggregation function ignores null values by default, so there really is no need to use coalesce or isnull inside it.
If you want the sum of all 3 columns for every single row, then you need to use isnull:
SELECT ISNULL(reglProj.Montant,0) +
ISNULL(reglProj.FraisMagasing ,0) +
ISNULL(reglProj.FraisVendeur,0)
FROM Projet.LigneEcheancierProjet ligne
INNER JOIN Projet.ReglementProjetEcheance reglProj
ON reglProj.LigneEcheancierProjetId = ligne.LigneEcheancierProjetId
If you need the aggregated sum of all 3 columns you can simply do it like this:
SELECT ISNULL(SUM(reglProj.Montant), 0) +
ISNULL(SUM(reglProj.FraisMagasing), 0) +
ISNULL(SUM(reglProj.FraisVendeur), 0)
FROM Projet.LigneEcheancierProjet ligne
INNER JOIN Projet.ReglementProjetEcheance reglProj
ON reglProj.LigneEcheancierProjetId = ligne.LigneEcheancierProjetId
It seems you are looking for ISNULL actually
SELECT SUM( ISNULL(reglProj.Montant,0) + ISNULL(FraisMagasing,0)+ ISNULL(FraisVendeur,0)) AS SommeReglement
FROM Projet.LigneEcheancierProjet ligne
INNER JOIN Projet.ReglementProjetEcheance reglProj ON reglProj.LigneEcheancierProjetId = ligne.LigneEcheancierProjetId

SQL Server counting unrelated conditions

Folks,
I have researched this question first and came up with nothing for my specific issue, I found SUM/CASE which is neat but not exactly what I need. Here is my situation:
I have been asked to report back the total number of people who meet 5 out of 8 conditions.
I am having trouble coming up with the best way of doing this. It must be something to do with having a counter for each condition and then adding the counter at the end and returning the count of people who met 5 of the 8 conditions (call them condition a - h)
So can you do a count of a count?
Something like
if exists (code for condition A) 1 ELSE 0
if exists (code for condition B) 1 ELSE 0
etc
sum(count)
Thank you
Since the conditions are spread across rows, you can do this by combining MAX() and a CASE statement in a HAVING clause:
SELECT person_ID
FROM YourTable
GROUP BY Person_ID
HAVING MAX(CASE WHEN ConditionA THEN 1 END)
+ MAX(CASE WHEN ConditionB THEN 1 END)
+ MAX(CASE WHEN ConditionC THEN 1 END)
+ MAX(CASE WHEN ConditionD THEN 1 END)
+ MAX(CASE WHEN ConditionE THEN 1 END)
+ MAX(CASE WHEN ConditionF THEN 1 END)
+ MAX(CASE WHEN ConditionG THEN 1 END)
+ MAX(CASE WHEN ConditionH THEN 1 END)
>= 5
How about:
WHERE CASE WHEN (Code for condition A) then 1 else 0 end +
CASE WHEN (Code for condition B) then 1 else 0 end +
CASE WHEN (Code for condition C) then 1 else 0 end +
...
= 5
User defined function to the rescue
SELECT * FROM view_people_with_conditions_count WHERE conditions_count BETWEEN 5 AND 8
CREATE VIEW view_people_with_conditions_count
AS
SELECT *, dbo.GetCondtionsCount(id) as conditions_count FROM peoples_table
CREATE function GetCondtionsCount(#id int)
RETURNS int
AS
BEGIN
DECLARE #counter int
' implement your conditions here
IF (condtion1) SET #counter = #counter + 1
IF (condtion2) SET #counter = #counter + 1
IF (condtion3) SET #counter = #counter + 1
RETURN #counter
END
I ended up completing this by using a WITH statement
something like this:
WITH
(
Select statement for first condition AS blah
Select statement for second condition AS blah
Select statement for third condition AS blah
Select statement for fourth condition AS blah
Select statement for fifth condition AS blah
Select statement for sixth condition AS blah
Select statement for seventh condition AS blah
Select statement for eighth condition AS blah
)
select
CASE WHEN (8 cases based on the 8 selects above
I just put the results in a spreadsheet and did all the math in Excel

SQL: sort by number of empty columns

I have a SQL query which displays a list of results. Every row in my database has about
20 columns and not every column is mandatory. I would like the result of the SQL query to be
sorted by the number of filled in columns. The rows with the least empty columns at the top, the ones with the most empty columns at the bottom. Do any of you guys have an idea how to do this?
I thought about adding an extra column to the table which if updated every time the user edits their row, this number would indicate the number of empty columns and I could sort my list with that. This however, sounds like unnecessary troubles, but maybe there is no other way? I'm sure somebody on here will know!
Thanks,
Sander
You can do it in just about any database with a giant case statement:
order by ((case when col1 is not null then 1 else 0 end) +
(case when col2 is not null then 1 else 0 end) +
. . .
(case when col20 is not null then 1 else 0 end)
) desc
You could order by the amount of empty columns:
order by
case when col1 is null then 1 else 0 end +
case when col2 is null then 1 else 0 end +
case when col3 is null then 1 else 0 end +
...
case when col20 is null then 1 else 0 end
(Note the + at the end of the lines: it's only one column with the integer count of empty fields, sorted in ascending order.)