Different record count values in one query - sql

My goal is to achieve a result set like the following
CODE | TOTAL1 | TOTAL2
1 | 56 | 34
2 | 12 | 15
3 | 90 | 3
There are 2 tables e.g tableA and tableB
The counts are different by tableB.type
SELECT code, COUNT (*) AS total1
FROM tableA a
WHERE a.ID IN (select ID from tableB
where type = 'XYZ')
GROUP BY code
SELECT code, COUNT (*) AS total2
FROM tableA a
WHERE a.ID IN (select ID from tableB
where type = 'ABC')
GROUP BY code
I'd like to display the count for each code per type in the same query
Thanks in advance

No subquery
SELECT a.code,
sum(decode(b.type,'ABC',1,0)) AS total1,sum(decode(b.type,'XYZ',1,0)) AS total2
FROM tableA a
join tableB b on a.ID = b.ID
GROUP BY a.code
Regards
K

Subqueries :
SELECT code, (select COUNT (*) AS total1
FROM tableA a1
WHERE a.ID IN (select ID from tableB
where type = 'XYZ')
and a1.code = tableA.code) as Total1,
(select COUNT (*) AS total2
FROM tableA a2
WHERE a.ID IN (select ID from tableB
where type = 'ABC')
and a2.code = tableA.code) as Total2)
from tableA
group by Code

Probably one of many ways to skin it is to UNION the two in an in-line view and then select the sum of the counts, like this:
SELECT code, SUM(total1) total1, SUM(total2) total2 FROM
(
SELECT code, COUNT() total1, 0 total2
FROM tableA a
WHERE a.ID IN (select ID from tableB
where type = 'XYZ')
GROUP BY code
UNION
SELECT code, 0, COUNT ()
FROM tableA a
WHERE a.ID IN (select ID from tableB
where type = 'ABC')
GROUP BY code
)
GROUP BY code;

Related

Query to find the count of total IDs, and IDs having null in any column and the percentage of the two (i.e. Count of ID having null /total ID count)

There are two tables.
Tables A has following structure
ID
Flag
Name
1X
1
Y
2Y
0
Null
3Z
1
Null
4A
1
Y
Table B has the following structure
B_ID
City
State
1X
Y
Null
2Y
Null
Null
3Z
Null
Y
4A
Y
Y
I want to get the count of all the IDs and the count of IDs that have Null in any of the columns (name, city, state), for e.g from the tables above only the ID 4A has non null value in all the three columns across both the tables, so the output should be like
Total_Count
Ids having null
Percentage missing
4
3
0.75%
Total_count is 4 as there are total of four IDs, ID having NULL is 3 because there are 3 IDs that have null in any of the three columns (viz. name,city,state), and Percentage missing is just IDs having null / Total_Count.
I tried using a query along the following lines
select (count/total) * 100 pct, count,total
from (select sum(count) count
from(select count(*) count from tableA T1
where T1.name is null
union all
select count(*) count from tableA T1
join tableB T2 on T1.ID = T2.B_ID
where T2.city is null
union all
select count(*) count from tableA T1
join tableB T2 on T1.ID = T2.B_ID
where T2.state is null)),
select count(ID) total from tableA);
But the query is not returning the desired output, can you please suggest me a better way?
Thank You
Use conditional aggregation:
SELECT COUNT(*) Total_Count,
COUNT(CASE WHEN t1.Name IS NULL OR t2.City IS NULL OR t2.State IS NULL THEN 1 END) Ids_having_null,
AVG(CASE WHEN COALESCE(t1.Name, t2.City, t2.State) IS NOT NULL THEN 1 ELSE 0 END) Percentage_missing
FROM Table1 t1 INNER JOIN Table2 t2
ON t2.B_ID = t1.ID;
See the demo.
If you don't know if either table has all the ID's?
Then I suggest a full join with some conditional aggregation in a sub-query.
For example:
select
Total as "Total_Count"
, TotalMissing as "Ids having null"
, (TotalMissing / Total)*100||'%' as "Percentage missing"
from
(
select
count(distinct coalesce(a.ID, b.B_ID)) as Total
, count(distinct case
when a.name is null
or b.city is null
or b.state is null
then coalesce(a.ID, b.B_ID)
end) as TotalMissing
from TableA a
full outer join TableB b
on a.ID = b.B_ID
) q
Total_Count | Ids having null | Percentage missing
----------: | --------------: | :-----------------
4 | 3 | 75%
db<>fiddle here
Try this ->
select total_count, (total_count - cn) as ids_having_null,
(total_count - cn) *100 / total_count as Percentage_missing
FROM
(select count(t1.ID) as cn , t1.total_count
FROM ( select ID,Name, sum(tmp_col) over ( order by tmp_col) as total_count from (select ID,Name, 1 as tmp_col from tableA ) ) t1
JOIN TableB t2
ON t1.ID = t2.B_ID
WHERE t1.Name is not null and t2.City is not null and t2.State is not null );
Based on your requirement for percentage or ratio, you can alter the logic for Percentage_missing column

How to select rows from another table based on a table containing min and max values

TableA:
Userid sessionid domain_value tag
---------------------------------
1 20 amex bank
1 40 visa bank
2 10 citibank bank
2 20 amex bank
2 30 amex bank
TableB:
Userid sessionid(min) sessionid(max)
------------------------------------
1 20 40
2 10 30
3
4
5
How to retrieve all the rows from TableA based on values in TableB?
select *
from TableA a
inner join TableB b on a.userid = b.userid
where a.sessionid between (select b.[sessionid(min)] from TableB b)
and (select b.sessionid(max)] from TableB b)
assumin table B with these column name
Userid, sessionid_min, sessionid_max,
try using just between column_fom_min and column_for_max
select *
from TableA a
inner join TableB b on a.userid = b.userid
where a.sessionid between b.sessionid_min
and b.sessionid_max
How to retrieve all the rows from TableA based on values in TableB?
In order to retrieve rows from TableA, select from TableA. If you want to filter based on values in TableB, place an according WHEREclause in the query. There is no need to join here.
select *
from tablea a
where exists
(
select null
from tableb b
where a.sessionid between b.sessionid_min and b.sessionid_max
)
order by userid, sessionid;
(You can achieve the same with a join, but the intention would not be as clear from reading the query.)
You may try this
select t.Userid ,
(select min(sessionid) from TableA as tb on tb.Userid =t.Userid ) as Min,
(select max(sessionid) from TableA as tb on tb.Userid =t.Userid ) as Max
from TableA as t group by t.Userid

Tips for Creating Summary Count of Value in other Tables

I have multiple tables with a status column in each. I want to display a summary of the counts of each status per table. Something like this:
=============================================
Status | Table A | Table B | Table C |
Status A | 3 | 8 | 2 |
Status B | 5 | 7 | 4 |
==============================================
I need help getting started as I'm not sure how to approach this issue. I can do simple COUNT functions like:
SELECT status, count(status) from TABLE_A group by status
But I'm not sure how to populate the data in the form I want or how to, if possible, use the table names as the column headers. I'd appreciate a point in the right direction. Thanks!
May be try doing left joins after you have calculated counts for each table separately.Something like:
select distinct t1.status,
count(t1.status) as [tableA],
t2.TableB,
t3.TableC from Table A t1
left join (
select distinct status,
count(status) as [TableB] from Table B
group by status
) t2 on t1.status=t2.status
left join (
select distinct status,
count(status) as [TableC] from Table C
group by status
) t3 on t1.status=t3.status
group by t1.Status
I would use union all and aggregation:
select status, sum(a) as a, sum(b) as b, sum(c) as c
from ((select status, count(*) as a, 0 as b, 0 as c
from tablea
group by status
) union all
(select status, 0, count(*), 0
from tableb
group by status
) union all
(select status, 0, 0, count(*)
from tablea
group by status
)
) abc
group by status;
This ensures that all rows appear, even when one or more tables are missing some values of status.
could be using left join
select t.status, a.cnt A, b.cnt B,c.cnt C
from(
select status
from tableA
union
select status
from tableB
select status
from tableC
) t
left join (
select status, count(*) cnt
from tableA
group by status
) a ON on t.status = a.status
left join (
select status, count(*) cnt
from tableB
group by status
) b ON on t.status = b.status
left join (
select status, count(*) cnt
from tableC
group by status
) c ON on t.status = c.status

Count similar values from table by combining two tables

I have two table
table A
name id
ABC 1
PQR 2
XYZ 1
QWE 2
DFG 3
Another table
table B
id idname
1 stuart
2 bob
3 alex
expected output
id idname count
1 stuart 2
2 bob 2
3 alex 1
Iam using oracle 9i, Is it possible to obtain the expected result?
I have tried using distinct keyword but its not helping as it provides only the total count
That's simple. Join and count:
select b.id,
b.idname,
count(*) as cnt
from table_a a
join table_b b on a.id = b.id
group by b.id, b.idname;
If you need all the record from table b even if there is no corresponding row in table a, you can use an outer join:
select b.id,
b.idname,
count(a.id) as cnt
from table_a a
right join table_b b on a.id = b.id
group by b.id, b.idname;
Same can be achieved by using a left join:
select b.id,
b.idname,
count(a.id) as cnt
from table_b b
left join table_a a on a.id = b.id
group by b.id, b.idname;
Use JOIN to get data from both tables and use the aggregate function COUNT with GROUP BY.
Query
select t1.id, t1.idname, count(t2.name) as count
from TableB t1
left join TableA t2
on t1.id = t2.id
group by t1.id, t1.idname
order by count(t2.name) desc, t1.id;;

SQL aggregation query, grouping by entries in junction table

I have TableA in a many-to-many relationship with TableC via TableB. That is,
TableA TableB TableC
id | val fkeyA | fkeyC id | data
I wish the do select sum(val) on TableA, grouping by the relationship(s) to TableC. Every entry in TableA has at least one relationship with TableC. For example,
TableA
1 | 25
2 | 30
3 | 50
TableB
1 | 1
1 | 2
2 | 1
2 | 2
2 | 3
3 | 1
3 | 2
should output
75
30
since rows 1 and 3 in Table have the same relationships to TableC, but row 2 in TableA has a different relationship to TableC.
How can I write a SQL query for this?
SELECT
sum(tableA.val) as sumVal,
tableC.data
FROM
tableA
inner join tableB ON tableA.id = tableB.fkeyA
INNER JOIN tableC ON tableB.fkeyC = tableC.id
GROUP by tableC.data
edit
Ah ha - I now see what you're getting at. Let me try again:
SELECT
sum(val) as sumVal,
tableCGroup
FROM
(
SELECT
tableA.val,
(
SELECT cast(tableB.fkeyC as varchar) + ','
FROM tableB WHERE tableB.fKeyA = tableA.id
ORDER BY tableB.fkeyC
FOR XML PATH('')
) as tableCGroup
FROM
tableA
) tmp
GROUP BY
tableCGroup
Hm, in MySQL it could be written like this:
SELECT
SUM(val) AS sumVal
FROM
( SELECT
fkeyA
, GROUP_CONCAT(fkeyC ORDER BY fkeyC) AS grpC
FROM
TableB
GROUP BY
fkeyA
) AS g
JOIN
TableA a
ON a.id = g.fkeyA
GROUP BY
grpC
SELECT sum(a.val)
FROM tablea a
INNER JOIN tableb b ON (b.fKeyA = a.id)
GROUP BY b.fKeyC
It seems that is it needed to create a key_list in orther to allow group by:
75 -> key list = "1 2"
30 -> key list = "1 2 3"
Because GROUP_CONCAT don't exists in T-SQL:
WITH CTE ( Id, key_list )
AS ( SELECT TableA.id, CAST( '' AS VARCHAR(8000) )
FROM TableA
GROUP BY TableA.id
UNION ALL
SELECT TableA.id, CAST( key_list + ' ' + str(TableB.id) AS VARCHAR(8000) )
FROM CTE c
INNER JOIN TableA A
ON c.Id = A.id
INNER join TableB B
ON B.Id = A.id
WHERE A.id > c.id --avoid infinite loop
)
Select
sum( val )
from
TableA inner join
CTE on (tableA.id = CTE.id)
group by
CTE.key_list