return 1 when multiple conditions meet - sql

I have a table that looks like this:
ACCOUNT hour_count
A 24
B 24
C 23
D 22
I want to create an sql statement that just returns "1" when ALL conditions meet:
when account is A, hour_count = 24
when account is B, hour_count = 24
when account is C, hour_count > 22
when account is D, hour_count > 22
How can I achieve this?
I tried using a CASE statement, but i couldn
if 't figure out how to include multiple conditions. It wouldn't work with an AND within a single WHEN statement:
SELECT
CASE
WHEN ((ACCOUNT = 'A' AND hour_count = 24) )
THEN 1
END
FROM hour_counts

if you only a single 1 returned if all of the conditions met else nothing returned
with cte as (
select case
when account in (‘A’,’B’) and hour_count = 24) then 1
when account in (‘C’,’D’) and hour_count > 22) then 1
end as c_count
from hour_count)
select 1 as one
from cte
having sum(c_count)=4 —- if all conditions met

You can turn each check into an exists predicate in a where clause:
(Optional sample test data):
create or replace table T1 as
select
COLUMN1::string as "ACCOUNT",
COLUMN2::float as "HOUR_COUNT"
from (values
('A',24),
('B',24),
('C',23),
('D',22)
);
Query with exists predicates for each check:
select 1 as CHECKS where
exists (select 1 from T1 where ACCOUNT = 'A' and HOUR_COUNT = 24) and
exists (select 1 from T1 where ACCOUNT = 'B' and HOUR_COUNT = 24) and
exists (select 1 from T1 where ACCOUNT = 'C' and HOUR_COUNT > 22) and
exists (select 1 from T1 where ACCOUNT = 'D' and HOUR_COUNT > 22)
;
Note that with the test data this will not return a row. That's because it fails on the check for account D. You can change the check to >= or change the value for D to see the effect of passing the exists checks.

If you just need it to return 1 or 0, it gets easier
select iff(count(distinct account)=4,1,0)
from t
where (account in ('A','B') and hour_count = 24) or
(account in ('C','D') and hour_count > 22)

Related

Compare two columns in SQL

I'm new to SQL and have very basic queries in GCP.
Let's consider this table below:
Name
B
C
Arun
1234-5678
1234
Tara
6789 - 7654
6789
Arun
4567
4324
Here, I want to compare column B and C and if they match then give 1 else 0 in column same and else different (which we have to create).
So here the catch:
if column B has 1234-5678 and column C has 1234, then the column should match considering only the number before the "-" in the value.
The output should be :
Name
B
C
same
different
Arun
1234-5678
1234
1
0
Tara
6789 - 7654
6789
1
0
Arun
4567
4324
0
1
Also, I want to count the values of 1 for each values in Name for same and different columns.
So far I've tried this:
SELECT
name,
b,
c ,
if(b = c, 1, 0) as same,
if (b!=c,1,0) as different,
count(same),
count(different)
From Table
using "MySQL" (will work almost same with SQL server as well) here's the possible solution.
Step 1) Setup table
CREATE TABLE Users (
Name varchar(50),
B varchar(50),
C varchar(50)
);
INSERT INTO Users
VALUES
('Arun', '1234-5678', '1234'),
('Tara', '6789-7654', '6789'),
('Arun', '4567', '4324');
Step 2) same & different columns
SELECT
Name, B, C,
CASE WHEN SUBSTRING_INDEX(B, "-", 1) = C THEN 1 ELSE 0 END as same,
CASE WHEN SUBSTRING_INDEX(B, "-", 1) <> C THEN 1 ELSE 0 END as different
FROM
Users
Step 3) Join both results to get total_same & total_different for each user
SELECT
Name,
SUM(CASE WHEN SUBSTRING_INDEX(B, "-", 1) = C THEN 1 ELSE 0 END) as total_same,
SUM(CASE WHEN SUBSTRING_INDEX(B, "-", 1) <> C THEN 1 ELSE 0 END) as total_different
FROM
Users
GROUP BY Name
Reference: SQL Fiddle
For the first step, you will need to SUBSTR the column b.
We start at position 1 and we want 4 characters (only works if there's only 4 characters before the '-').
With table2 as (
select name, b,c, same, different from (select name, b, c, case when (SUBSTR(b,1,4) = c)
then '1' else '0' end as same, case when(SUBSTR(b,1,4)!= c) then '1' else '0' end as different
from Table1
group by name, b,c))
The WITH clause can be used when you have complex query, and if you want to create a temporary table in order to use it after.
The Table2 give you this :
After the WITH clause, you will have the second step, the count of same / different per name :
Select table1.name,count(table2.same+table2.different) as total from table1
join table2 on (table2.name = table1.name and table2.b = table1.b)
group by table1.name;
The output give you the total per name (the name are group by, so in your example you will only have 2 rows, one for Arun with a total of 2 (same + different) and the other one with a total of 1)
So here's the entire code :
with table2 as (
select name, b,c, same, different from (select name, b, c, case when (SUBSTR(b,1,4) = c) then '1' else '0' end as same, case when(SUBSTR(b,1,4)!= c) then '1' else '0' end as different
From Table1
group by name, b,c))
select table1.name, table1.b, table1.c, count(table2.same+table2.different) as total from table1
join table2 on (table2.name = table1.name and table2.b = table1.b)
group by table1.name;

Get a particular record based on a condition in SQL

My requirement is to get id for missing status from SQL table. I will get a list of status for each id, say A,B,C,D. In a scenario, I have to check status B exists or not. Table gets updated everyday and each time new Id will be created
Conditions,
If status A exists and other statuses such as C and D does not
exists, then don't need to get id.
If status A and B exists and other statuses such as C or D does not exists, then don't need to get id .
If status A exists and B not exists, other
statuses such as C or D exists, then I should get the id of that
record
If status A and B exists, other
statuses such as C or D exists (all status exists), then I don't need to get the id of that
record
Table1:
Id StatusCode
1 A
1 C
2 A
2 B
2 C
3 A
3 C
3 D
How do I get Id 1 and 3 using SQL query?, Seems simple but as I am new to SQL I could not able to get it in SQL.
select statement in this screenshot works fine when there is only one id, it fails on multiple id. I tried many other way, but no use
Try this
SELECT DISTINCT ID
FROM T1
WHERE Statuscode = 'A' AND ID NOT IN (SELECT ID FROM T1 WHERE Statuscode = 'B' )
AND (ID IN (SELECT ID FROM T1 WHERE Statuscode = 'C' ) OR ID IN (SELECT ID FROM T1 WHERE Statuscode = 'D' ))
FIDDLE DEMO
Also, To correct Gordon Linoff's answer, we need to add one more where criteria there
SELECT Id
FROM T1
GROUP BY Id
HAVING SUM(CASE WHEN Statuscode = 'A' THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN Statuscode = 'B' THEN 1 ELSE 0 END) = 0 AND
SUM(CASE WHEN Statuscode IN ('C', 'D') THEN 1 ELSE 0 END) > 0;
FIDDLE DEMO
This answers the original version of the question.
I think you can use aggregation:
select id
from t
group by id
having sum(case when status = 'A' then 1 else 0 end) > 0 and
sum(case when status in ('C', 'D') then 1 else 0 end) > 0;
SELECT id
FROM t
GROUP BY
Id
HAVING MAX(status) = CHAR(64 + COUNT(*))
--char(64+1) = A, char(64+2) = B etc
The logic behind this is that it will take all count the same types of id. So if you have 3 rows you will need abc. If you have an id with 4 rows you will have ABCD. Generally the max status should always be the same as the number of rows.
This is true of course if you have no duplicate between id and status code.
select distinct id from t where t.statuscode = 'C' or t.statuscode = 'D' group by t.id

SQL check link between two records (CASE, WHEN, THEN)

I Have in table some records:
ID Services
2 A
2 C
2 C1
2 D2
I`m trying make query that will be select a link between services.
For example: If for ID 2 exists Services C then check if exist Service C1, result Yes or No.
SELECT a. ID, a.service,
CASE
WHEN (a.service ='C') = (a.service = 'C1') THEN 'Yes'
ELSE 'No'
END
FROM t1 a
Try this query:
SELECT *
FROM yourTable t1
WHERE NOT EXISTS (SELECT 1 FROM yourTable t2
WHERE (t2.Services LIKE t1.Services + '%' OR
t1.Services LIKE t2.Services + '%') AND
t1.ID = t2.ID AND t1.Services <> t2.Services);
This returns A and D2 only.
Demo
Hmm... what about this? But I now have problem with checking relationship for each ID independently...
SELECT a. ID, a.service,
CASE
WHEN a.service IN ('C','C1') THEN 'Yes'
ELSE 'No'
END
FROM t1 a
If I understand correctly, you can use aggregation:
SELECT ID,
(CASE WHEN SUM(CASE WHEN service = 'C' THEN 1 ELSE 0 END) > 0 AND
SUM(CASE WHEN service = 'C1' THEN 1 ELSE 0 END) > 0
THEN 'Yes' ELSE 'No'
END) as c_c1_flag
FROM t1
GROUP BY ID;
The SUM(CASE . . . ) counts the number of rows that match the conditions. The > 0 simply says that at least one row exists.

Is there any built-in function to calculate percentage in SQL Server

I m facing a problem to write a SQL query to get result in % mode, I am familer with SUM() and COUNT() functions of SQL Server but facing problem to implement logic inside query I want result in below form:-
UserName--- % of AccepectResult---- % of RejectResult
My table structure is like this with two columns Name (UserName) and Result :
NAME Result
---------------
USer1 A
USer1 A
USer1 A
USer1 R
USer1 R
USer1 A
USer2 A
USer2 A
USer2 A
USer2 A
USer2 R
A - Accepted Result
R - Rejected Result
I'm trying to write this query like this..
select * into #t1 from
(
select UserName , count(Result) as Acc
from Test where result = 'A'
group by UserName
) as tab1
select * into #t2 from
(
select UserName , count(Result) as Rej
from Test where result = 'R'
group by UserName
) as tab2
select #t1.UserName ,
#t1.Acc ,
#t2.Rej ,
(#t1.Acc)*100/(#t1.Acc + #t2.Rej) as AccPercentage,
(#t2.Rej)*100/(#t1.Acc + #t2.Rej) as RejPercentage
from #t1
inner join #t2 on #t1.UserName = #t2.UserName
drop table #t1
drop table #t2
Is there any other way to write this query and any built-in function for calculating percentage in SQL Server?
You do not require to join table. Instead you can use SUM or COUNT function like this:
Using SUM Function:
SELECT Name, 100 *
SUM(CASE WHEN Result = 'A' THEN 1 ELSE 0 END)/COUNT(result)
AS Accept_percent
,100 *
SUM(CASE WHEN Result = 'R' THEN 1 ELSE 0 END)/COUNT(result)
AS Reject_percent
FROM t
Group by Name;
Or using COUNT Function:
SELECT Name, 100 *
COUNT(CASE WHEN Result = 'A' THEN 1 ELSE NULL END)/COUNT(result)
AS Accept_percent
,100 *
COUNT(CASE WHEN Result = 'R' THEN 1 ELSE NULL END)/COUNT(result)
AS Reject_percent
FROM t
Group by Name;
Or using SubQuery:
SELECT Name, 100 *
(SELECT COUNT(result) FROM t WHERE result='A' And Name = main.Name)/COUNT(result)
AS Accept_percent
, 100 *
(SELECT COUNT(result) FROM t WHERE result='R' And Name = main.Name)/COUNT(result)
AS Reject_percent
FROM t main
Group by Name;
See this SQLFiddle
No there isn't. You will have to multiply by 100 and divide your two numbers explicitly.
Try something like this:
select username, (100 * sum(case result when 'A' then 1 else 0 end) / count(*)) as accepted,
(100 * sum(case result when 'R' then 1 else 0 end) / count(*)) as rejected
from test
group by username

Union of two tables but show which table the data came from

I have two tables:
TABLE_A TABLE_B
Fields: Trans Amend Trans Amend
data: 100 0 100 0
100 1
110 0
120 0
120 1
130 0 130 0
130 1
140 0 140 0
150 0 150 0
150 1 150 1
150 2
What I want is a table (view) that will combine (union) these to tables but will only show the highest Amend for each Trans
Looking for this as the answer:
Fields: Trans Amend
data: 100 1
110 0
120 1
130 1
140 0
150 2
Then to make it harder, I would like to know if there is a way I can tell from which table the data is coming from. Table A always wins when Record A and Record B are equal
Looking for this as the answer:
Fields: Trans Amend WhichTBL
data: 100 1 Table_A
110 0 Table_A
120 1 Table_B
130 1 Table_B
140 0 Table_A
150 2 Table_A
I know a UNION can't be done to get this result.
What if you added a string in your select and aliased it as a column?
SELECT Trans, Amend, 'Table_A' as WhichTBL
FROM (your 1st select query)
UNION
SELECT Trans, Amend, 'Table_B' as WhichTBL
FROM (your 2nd select query)
ORDER BY Trans
In Teradata SQL you would do the following, not sure about SQL Server:
select trans,amend,WhichTBL from
(
select trans,amend,'Table_A' WhichTBL from Table_A
union
select trans,amend,'Table_B' WhichTBL from Table_B
) X
qualify row_number() over(partition by trans order by amend desc, WhichTBL) = 1
order by trans;
A version using Lucero's suggestion if your SQL doesn't have a QUALIFY clause:
select trans,amend,WhichTBL from
(
select x.*,row_number() over(partition by trans order by amend desc, WhichTBL) as rn
(
select trans,amend,'Table_A' as WhichTBL from Table_A
union
select trans,amend,'Table_B' as WhichTBL from Table_B
) Derived1 as X
) Derived2
where rn = 1
order by trans;
would this work?
SELECT
trans, MAX(max_amend) as max_max_amend
FROM
(SELECT
'a' AS src, trans, MAX(amend) AS max_amend
FROM
table_a
GROUP BY
trans
UNION ALL
SELECT
'b' AS src, trans, MAX(amend) AS max_amend
FROM
table_b
GROUP BY
trans) m
GROUP BY
trans
Lucero's point below is correct, the min(src) would be on the global set, not the related max()
I think you'd have to combine the source and table values into one column you can max. In your example, adding 1 to the value is all you need to distinguish the sources, like:
SELECT trans, Max(amend) AS MaxOfamend, 1+[amend] AS isa, 0 AS isb
FROM TableA
GROUP BY trans
but you could add 100, or multiply by a big value, or whatever works with your data. The idea is to combine the two pieces of information, the amend value and the source, into one column.
Then, after the information is combined, you get the max of that value, then strip off the source flag by uncombining them (subtracting 1, dividing by 100, whatever)
OK, here's what I got:
CREATE VIEW [dbo].[viewA] AS
SELECT trans, MAX(amend + .20) AS srcIsA, 0 AS srcIsb
FROM dbo.tableA
GROUP BY trans
CREATE VIEW [dbo].[viewB] AS
SELECT trans, 0 AS srcIsA, MAX(amend + .10) AS srcIsB
FROM dbo.tableB
GROUP BY trans
CREATE VIEW [dbo].[viewU] AS
SELECT * from viewA
union all
select *
FROM viewb
CREATE VIEW [dbo].[viewv] AS
SELECT trans, srcIsA, srcIsb, srcIsA + srcIsb AS total
FROM dbo.viewU
CREATE VIEW [dbo].[vieww] AS
SELECT trans, MAX(total) AS max_total
FROM dbo.viewv
GROUP BY trans
CREATE VIEW [dbo].[viewx] AS
SELECT trans,
max_total,
CAST(max_total AS int) AS maxval,
CASE WHEN (max_total - CAST(max_total AS int)) = .1 THEN 'a' ELSE 'b' END AS src
FROM dbo.vieww
I want to offer that you can do this with a join and aggregation, using standard SQL:
select coalesce(a.trans, b.trans) as trans,
(case when coalesce(max(b.amend), -1) > coalesce(max(a.amend), -1)
then max(b.amend)
else max(a.amend)
end) as amend,
(case when coalesce(max(b.amend), -1) > coalesce(max(a.amend) , -1)
then 'B' else 'A'
end) as whichTable
from Table_A a full outer join
Table_B b
on a.trans = b.trans
group by coalesce(a.trans, b.trans)
If Amend only has value 1 and 0
then the first question can be done
select Trans,sum(Amend) AmendMax from (select Trans,Amend from TABLE_A
union select Trans,Amend from TABLE_B) C group by Trans
the secound question would be
select Trans,max(Amend) Amend,case when sum(s)=1 or sum(s)=2 or sum(s)=21
then 'Table-A' when sum(s)=10 or sum(s)=12 or sum(s)=20 then 'Table-B'
when sum(s)=11 or sum(s)=22 then 'Table-A and B' end s from
(select case when max(Amend)=1 then 1 else 2 end s,Trans,max(Amend) Amend from TABLE_A
group by Trans union select case when max(Amend)=1
then 10 else 20 end s,Trans,max(Amend) Amend from TABLE_B group by Trans) C group by Trans