Oracle SQL with Join and count - sql

I would like to join two tables, A and B, with a count function.
Table A has the followings:
SQL> select a.book_id, count(a.book_id)
from
a
group by a.book_id ;
BOOK_ID COUNT(A.BOOK_ID)
--------- ----------------
1 2
2 2
3 2
4 2
5 2
6 3
and table B has the followings:
SQL> select b.book_id, count(b.book_id)
from
b
group by b.book_id ;
BOOK_ID COUNT(B.BOOK_ID)
--------- ----------------
6 2
So I would like to have a query which gives me the following result:
BOOK_ID COUNT(A.BOOK_ID) COUNT(B.BOOK_ID)
--------- ---------------- ----------------
1 2 0
2 2 0
3 2 0
4 2 0
5 2 0
6 3 2
I tried this :
SQL> select b.book_id, count(b.book_id),a.book_id, count(a.book_id)
from
b , a
where
b.book_id(+) = a.book_id
group by b.book_id, a.book_id ;
but the results were like this :
BOOK_ID COUNT(B.BOOK_ID) BOOK_ID COUNT(A.BOOK_ID)
--------- ---------------- --------- ----------------
0 1 2
0 2 2
0 3 2
0 4 2
0 5 2
6 6 6 6

Something like this perhaps:
select a.book_id as id_a,
(select count(1) from a a2 where a2.book_id = a.book_id) as count_a,
b.book_id as id_b,
(select count(1) from b b2 where b2.book_id = a.book_id) as count_b
from a
left join b on b.book_id = a.book_id
group by a.book_id;

Another way to do it:
WITH total_list AS (
SELECT a.Book_id, 'a' AS a_cnt, NULL AS b_cnt FROM a
UNION ALL
SELECT b.Book_id, NULL, 'b')
SELECT Book_id, COUNT(a_cnt) AS a_total, COUNT(b_cnt) AS b_total
GROUP BY Book_id
ORDER BY Book_id
What this does is it uses a subquery to union together both of your book tables and attaches a flag to it to signify if it belonged to table a or table b. From there, I just selected from the subquery.

Related

Get the sum of (count(column1) + count(column2))

I have a table A:
entity_id name
------------------
1 Test1
2 Test2
3 Test3
4 Test4
5 Test5
6 Test6
I have a table B:
entity_id value1 value2
-----------------------------
1 10 20
1 15 30
2 10 25
1 9 45
3 null 1
2 45 50
3 20 null
I need to write a single query to select the entity_id and name from Table A and count the total occurrences for an entity_id of columns value1 and value2 from Table B and then the total of those column counts (null doesn't count).
So my output table would be:
entity_id name value1_count value2_count total_count
----------------------------------------------------------------------
1 Test1 3 3 6
2 Test2 1 2 3
3 Test3 1 1 2
4 Test4 0 0 0
5 Test5 0 0 0
6 Test6 0 0 0
I am having trouble summing the count of value1 and count of value2 and outputting that value in the total_count per unique entity_it.
This is the query I have so far:
SELECT DISTINCT a.entity_id, a.name
, count(b.value1) AS value1_count, count(b.value2) AS value2_count, sum(2) AS total_count
FROM a
LEFT JOIN b ON a.entity_id = b.entity_id
GROUP BY a.entity_id, a.name
I know that the sum(2) as total_count is incorrect and doesn't get me what I want.
SELECT entity_id, a.name
, COALESCE(b.v1_ct, 0) AS value1_count
, COALESCE(b.v2_ct, 0) AS value2_count
, COALESCE(b.v1_ct + b.v2_ct, 0) AS total_count
FROM a
LEFT JOIN (
SELECT entity_id, count(value1) AS v1_ct, count(value2) AS v2_ct
FROM b
GROUP BY 1
) b USING (entity_id);
db<>fiddle here
Aggregate first, join later. That's simpler and faster. See:
Query with LEFT JOIN not returning rows for count of 0
count() never produces NULL. Only the LEFT JOIN can introduce NULL values for counts in this query, so v1_ct and v2_ct are either both NULL or both NOT NULL. Hence COALESCE(v1_ct + v2_ct, 0) is ok. (Else, one NULL would nullify the other summand in the addition.)
try this :
WITH list AS
(
SELECT b.entity_id
, count(*) FILTER (WHERE b.value1 IS NOT NULL) OVER () AS value1_count
, count(*) FILTER (WHERE b.value2 IS NOT NULL) OVER () AS value2_count
FROM Table_B AS b
GROUP BY b.entity_id
)
SELECT a.entity_id, a.name
, COALESCE(l.value1_count, 0)
, COALESCE(l.value2_count,0)
, COALESCE(l.value1_count + l.value2_count, 0) AS total_count
FROM Table_A AS a
LEFT JOIN list AS l
ON a.entity_id = l.entity_id

Having problem with joining condition while joining 3 tables

I am having following structure of the tables:
Table A:
SSN a_id b_id. Date Sent
123 1 2 12/11/2020 1
Table B:
SSN a_id b_id Date. OPen
123 1 2 13/11/2020 1
123. 1 2. 14/11/2020 1
Table C:
SSN a_id b_id Date. Clicks
123 1 2 13/11/2020 1
123 1 2 14/11/2020 1
123 1 2 14/11/2020 1
123 1 2 14/11/2020 1
123 1 2 15/11/2020 1
I am using:
select *
from Table A
left join Table B on A.SSN = B.SSN and A.a_id = B.a_id and A.b_id = B.b_id
left join Table C on A.SSN = C.SSN and A.a_id = C.a_id and A.b_id = C.b_id
I want the following output:
Table Ans
SSN a_id b_id Date. Sent Open Clicks
123 1 2 12/11/2020 1 0 0
123 1 2 12/11/2020 0 1 1
123 1 2 12/11/2020 0 1 1
123 1 2 12/11/2020 0 0 1
123 1 2 12/11/2020 0 0 1
123 1 2 12/11/2020 0 0 1
The order of 1 and 0 in each column doesn't matter. But the count of it should be same as there in Original tables. How can I achieve this?
I assume that, this is the result set you actually wanted to get or something similar to it.
with a as (select * from (values (123,1,2,'2020-11-12',1)) a(ssn,a_id,b_id,"date",sent))
,b as(
select * from (values (123,1,2,'2020-11-13',1)
,(123,1,2,'2020-11-14',1)
) a(ssn,a_id,b_id,"date",open))
,c as
(select * from (values (123,1,2,'2020-11-13',1)
,(123,1,2,'2020-11-14',1)
,(123,1,2,'2020-11-14',1)
,(123,1,2,'2020-11-14',1)
,(123,1,2,'2020-11-15',1)
) a(ssn,a_id,b_id,"date",clicks)
)
select ssn, a_id, b_id,"date", sum(sent) as sent, sum(open) as open, sum(clicks) as clicks
from (
select ssn, a_id, b_id,"date",sent,0 as open,0 as clicks,"date" as hidendate from a
union all
select a.ssn,a.a_id,a.b_id,a."date",0 as sent,open,0 as clicks,b."date" as hidendate from a,b where a.a_id = b.a_id and a.b_id = b.b_id
union all
select a.ssn,a.a_id,a.b_id,a."date",0 as sent,0 as open,clicks,c."date" as hidendate from a,c where a.a_id = c.a_id and a.b_id = c.b_id
) q1
group by ssn,a_id,b_id,"date",hidendate
order by date
I think you want full join:
select *
from a full join
b
using (ssn, a_id, b_id, date) full join
c
using (ssn, a_id, b_id, date);
This returns the 0s as NULLs.
If you want 0s, use:
select ssn, a_id, b_id, date,
coalesce(a.sent, 0) as sent,
coalesce(b.open, 0) as open,
coalesce(c.click, 0) as click
from a full join
b
using (ssn, a_id, b_id, date) full join
c
using (ssn, a_id, b_id, date);

Query to select category ids, in which at least all subcategory ids are present. (SQL Server)

I have two tables, #table1 (subCatId) and #table2 (categoryId, subCatId).
#table1:
subCatId
---------
1
2
3
4
#table2:
categoryId subCatId
---------------------
1 1
1 2
1 3
2 1
2 2
2 3
2 4
2 5
3 1
3 2
3 5
3 4
4 1
4 2
4 3
4 4
Output:
categoryId
-----------
2
4
As at least all subCatId's (1, 2, 3, 4) are present in both categories.
You can use group by and having:
select t2.categoryId
from #table2 t2
group by t2.categoryId
having count(*) = (select count(*) from #table1);
This assumes that there is a proper foreign key relationship between #table2.subCatId and #table1.subCatId and that there are no duplicates.
If these were not the case, the more general solution would be:
select t2.categoryId
from #table2 t2 join
#table1 t1
on t2.subCatId = t1.subCatId -- filter to be sure only matching subCatId are counted
group by t2.categoryId
having count(distinct t2.subCatId) = (select count(*) from #table1);

Hive: count matches between INNER JOIN on unique values of a column

I am trying to count matches between columns resulting from an INNER JOIN of two tables on unique values of a single column in one of the two tables. An example may make things more clear:
If I had the following two tables:
Table A
-------
id_A: info_A
1 'a'
2 'b'
3 'c'
3 'd'
Table B
-------
id_B: info_B
1 'a'
3 'c'
5 'b'
I want to find the unique id_A: [1,2,3] and the info_A associated with them: ['a','b','c','d'].
I want to create a table that looks like the following:
Table join of A+B
-----------------
id_A: info_A id_B info_B match_cnt
1 'a' 1 'a' 1
3 'c','d' 3 'c' 0.5
where match_cnt is the number of matches between info_A and info_B for a given id_A. FYI, the actual tables I'm working with have billions of rows.
A code chunk demonstrates what I've tried, plus variations (not shown below):
SELECT z.id_A, z.info_A, z.id_B, z.info_B
FROM(
SELECT u.id_A AS id_A, u.info_A AS info_A, y.id_B AS true_id_B, y.info_B AS true_info_B
FROM db.table_A u
WHERE EXISTS
( SELECT id_B, info_B
FROM table_B l
where l.id_B= u.id_A)
INNER JOIN table_B y
ON u.id_A = y.id_B
) z
You may use some thing like below :-
WITH T1 AS ( select ID_A ,count(1) as cnt from tableA inner join tableB on tableA.ID_A=tableB.ID_B and tableA.INFO_A=tableB.INFO_B group by ID_A,INFO_A)
select distinct tmp.ID_A,tmp.a,tmp.ID_B,tmp.b, (cnt/size(a)) from
(select ID_A ,collect_set(INFO_A) as a,ID_B,collect_set(INFO_B) as b from tableA inner join tableB on tableA.a=tableB.a group by tableA.a,tableB.a)
tmp join T1 on T1.ID_A=tmp.ID_A
select id
,collect_list (case when a=1 then info end) as info_a
,collect_list (case when b=1 then info end) as info_b
,count (case when a=1 and b=1 then 1 end) / count(*) as match_cnt
from (select id
,info
,min (case when tab = 'A' then 1 end) as a
,min (case when tab = 'B' then 1 end) as b
from ( select 'A' as tab ,id_A as id ,info_A as info from A
union all select 'B' as tab ,id_B as id ,info_B as info from B
) t
group by id
,info
) t
group by id
having min(a) = 1
and min(b) = 1
;
+----+-----------+--------+-----------+
| id | info_a | info_b | match_cnt |
+----+-----------+--------+-----------+
| 1 | ["a"] | ["a"] | 1.0 |
| 3 | ["c","d"] | ["c"] | 0.5 |
+----+-----------+--------+-----------+

How do I return the sum for this query?

I have the following tables I need to find out the sum.
Table A
ID Name
1 Jason
2 Peter
3 Ravi
Table B
ID ID_SEC
1 11
1 12
1 13
2 21
2 22
2 23
3 31
3 32
3 33
Table C
ID_SEC Value Include_Ind
11 100 Y
12 200 Y
13 300 N
21 10 Y
22 20 N
23 30 N
31 1000 N
32 2000 N
33 3000 N
Output
ID Name Total Include_Ind_count [only count when Y]
1 Jason 600 2
2 Peter 60 1
3 Ravi 6000 0
Use:
SELECT a.id,
a.name,
SUM(c.value) AS total
FROM TABLE_A a
JOIN TABLE_B b ON b.id = a.id
JOIN TABLE_C c ON c.id_sec = b.id_sec
GROUP BY a.id, a.name
The trick to counting INCLUDE_IND only when the flag is set to 'Y' is to use CASE() to test its value:
SQL> select a.id
2 , a.name
3 , sum ( c.val) as total
4 , count( case when c.include_ind = 'Y' then 1
5 else null end ) as inc_ind_cnt
6 from a
7 join b on ( b.id = a.id )
8 join c on ( c.id_sec = b.id_sec )
9 group by a.name, a.id
10 order by a.id
11 /
ID NAME TOTAL INC_IND_CNT
---------- ---------- ---------- -----------
1 Jason 600 2
2 Peter 60 1
3 Ravi 6000 0
SQL>
The ORDER BY is necessary to guarantee sort order since Oracle changed the algorithm it uses for GROUP BY operations in 10g.
You can use inner Joins and SUM for getting the result -
Assuming you tableC.Value is int field. Else you need to cast it.
SELECT tabA.id, tabA.name, SUM(tabC.value)
FROM TABLE_A tabA
INNER JOIN TABLE_B tabB ON tabB.id = tabA.id
INNER JOIN TABLE_C tabc ON tabC.id_sec = tabB.id_sec
GROUP BY tabA.id, tabA.name