Get Count of records - Oracle - sql

I'm trying to get count of records from table by joining 2 or more table. Suppose I have 3 table like follows,
Table A
Column 1 Column 2
121 XX
123 XX
124 A0
125 A2
126 XX
Table B
Column 1
A0
A1
A2
A3
Table C
Column 1 Column 2
121 A0
122 A1
123 A0
124 A0
125 A2
126 A3
From these I need count result as follows,
Column 1 Column 2
XX,A0 2
XX,A1 0
XX,A2 0
XX,A3 1
A0,A0 1
A1,A1 0
A2,A2 1
A3,A3 0
Here I have 121 and 123 with XX in table A and same 121,123 in table C with A0, so count should be 2. Similarly 124 have A0 in table A and A0 in table C, so count should 1 and if no record matched with any column 2 it should have 0.
I tried with below query it is not returing as expected,
select b.column2 ||','|| c.column2 column , count(a.column1) count
from table A a
join table c c
on a.column1=c.column1
join table b b
on b.column1=c.column2
group by b.column2 ||','|| c.column2
order by b.column2 ||','|| c.column2

first you would want a cartesian product to get the set of all possible combinations betweeen tablea and tableb. I changed the column names to something more readable.
create table tablea(id int, name varchar2(10));
create table tableb(name varchar2(10));
create table tablec(id int, name varchar2(10));
insert into tablea
select 121,'XX' from dual union all
select 123,'XX' from dual union all
select 124,'A0' from dual union all
select 125,'A2' from dual union all
select 126,'XX' from dual
insert into tableb
select 'A0' from dual union all
select 'A1' from dual union all
select 'A2' from dual union all
select 'A3' from dual
insert into tablec
select 121,'A0' from dual union all
select 122,'A1' from dual union all
select 123,'A0' from dual union all
select 124,'A0' from dual union all
select 125,'A2' from dual union all
select 126,'A3' from dual
--Gets all possible combination of tablea and tableb
select distinct a.name as a_name,b.name as b_name
from tablea a
join tableb b
on 1=1
Then you would do a group by on these combination fields as follows
with data
as (select distinct a.name as a_name,b.name as b_name
from tablea a
join tableb b
on 1=1
)
,tablec_data
as (select c.name c_name,a.name a_name
from tablec c
join tablea a
on c.id=a.id
)
select max(m.a_name||','||m.b_name) as val_name,count(n.c_name) as cnt
from data m
left join tablec_data n
on m.a_name=n.a_name
and m.b_name=n.c_name
group by m.a_name,m.b_name
order by m.a_name desc,m.b_name
Here is a db fiddle link
https://dbfiddle.uk/?rdbms=oracle_18&fiddle=9e573822ecc1087e5871c80b586d1116
+----------+-----+
| VAL_NAME | CNT |
+----------+-----+
| XX,A0 | 2 |
| XX,A1 | 0 |
| XX,A2 | 0 |
| XX,A3 | 1 |
| A2,A0 | 0 |
| A2,A1 | 0 |
| A2,A2 | 1 |
| A2,A3 | 0 |
| A0,A0 | 1 |
| A0,A1 | 0 |
| A0,A2 | 0 |
| A0,A3 | 0 |
+----------+-----+

Table A should be used instead of Table B for combining the values. Table B doesn't have column Colunn2.
Also, Remember alias name for a.column2 ||','|| c.column2 should not be column as this is reserved keyword in oracle. So you can use column2 or something else.
EDIT: Following will give 0 count for not matched records.
select t1.column2,
case when t2.count1 is not null then t2.count1 else t1.count1 end as count1
from (
(select distinct a.column2||','||b.column1 as column2,
0 as count1 from tableA a join tableB b on a.column2='XX'
union
select distinct c.column2||','||b.column1 as column2,
0 as count1 from tableC c join tableB b on c.column2=b.column1
)t1
left join
(select a.Column2 ||','|| c.Column2 as column2,
count(*) count1 from TableA a join TableC c on a.column1=c.column1
join TableB B on b.column1=c.column2 group by a.column2 ||','|| c.column2
)t2
on t1.column2=t2.column2
)
order by
t1.column2
Output:
A0,A0 1
A1,A1 0
A2,A2 1
A3,A3 0
XX,A0 2
XX,A1 0
XX,A2 0
XX,A3 1

At first prepare key columns, use simple union. Then use this keys to construct join. Left join is used because we need zeros too:
with keys as (select 'XX' ka, column1 kb from b union all select column1, column1 from b)
select ka, kb, sum(case when c.column2 is not null then 1 else 0 end) cnt
from keys
left join a on ka = a.column2
left join c on kb = c.column2 and a.column1 = c.column1
group by ka, kb
order by ka, kb
dbfiddle demo

Related

How to select first x records from second table

I would like to get all records from first table and only x records from second table.
How many records from second table I have info in first table :
My tables are
table1 :
WITH table1(a,b) AS
(
SELECT 'aa',3 FROM dual UNION ALL
SELECT 'bb',2 FROM dual UNION ALL
SELECT 'cc',4 FROM dual
)
SELECT *
FROM table1;
a | b (number of records from table2 (x))
------
aa | 3
bb | 2
cc | 4
table2 :
WITH table2(a,b) AS
(
SELECT 'aa','1xx' FROM dual UNION ALL
SELECT 'aa','2yy' FROM dual UNION ALL
SELECT 'aa','3ww' FROM dual UNION ALL
SELECT 'aa','4zz' FROM dual UNION ALL
SELECT 'aa','5qq' FROM dual UNION ALL
SELECT 'bb','1aa' FROM dual UNION ALL
SELECT 'bb','2bb' FROM dual UNION ALL
SELECT 'bb','3cc' FROM dual UNION ALL
SELECT 'cc','1oo' FROM dual UNION ALL
SELECT 'cc','2uu' FROM dual UNION ALL
SELECT 'cc','3tt' FROM dual UNION ALL
SELECT 'cc','4zz' FROM dual UNION ALL
SELECT 'cc','5rr' FROM dual
)
SELECT *
FROM table2;
a | b
--------
aa | 1xx
aa | 2yy
aa | 3ww
aa | 4zz
aa | 5qq
bb | 1aa
bb | 2bb
bb | 3cc
bb | 4dd
bb | 5ee
cc | 1oo
cc | 2uu
cc | 3tt
cc | 4zz
cc | 5rr
Expected Result:
a | b
--------
aa | 1xx
aa | 2yy
aa | 3ww
bb | 1aa
bb | 2bb
cc | 1oo
cc | 2uu
cc | 3tt
cc | 4zz
You can use ROW_NUMBER() analytic function with LEFT/RIGHT OUTER JOIN among the tables :
WITH t2 AS
(
SELECT t2.a,t2.b, ROW_NUMBER() OVER (PARTITION BY t2.a ORDER BY t2.b) AS rn
FROM table2 t2
)
SELECT t2.a, t2.b
FROM t2
LEFT JOIN table1 t1
ON t1.a = t2.a
WHERE rn <= t1.b
Demo
You need to write something like:
SELECT a,
b
FROM Table2 T,
( SELECT LEVEL L FROM DUAL
CONNECT BY LEVEL <= (SELECT MAX(b) FROM Table1)
) A
WHERE T.b>= A.L
ORDER BY T.a;
Ideally you should have a ordering column in table2. When you say first X rows it does not make any sense unless you have something like an id or date field to order the records.
Anyway, assuming the number part of the column b in table 2 for ordering and assuming the number would be followed by 2 characters only such as xx,yy etc you can use the logic below
Select Tb1.a, Tb1.b
from
(Select t.*, row_number() over (partition by a order by substr(b,1,length(b)-2)) as seq
from Table2 t) Tb1
join Table1 Tb2
on Tb1.a = Tb2.a
Where Tb1.seq <= Tb2.b;
Demo - https://dbfiddle.uk/?rdbms=oracle_11.2&fiddle=3030b2372bcbb007606bbb6481af9884
Again it's just a job for lateral:
WITH prep AS
(
SELECT *
FROM tab1,
LATERAL
(
SELECT LEVEL AS lvl
FROM dual
CONNECT BY LEVEL <= b
)
)
SELECT p.a, t2.b
FROM prep p
JOIN tab2 t2
ON p.lvl = regexp_substr(t2.b,'^\d+')
AND p.a = t2.a
ORDER BY p.a, p.lvl

Getting all the values in one query that aren't in another with a group by

Given that I am using Redshift, how would I get the counts for a query that asks:
Given table A and table B, give me all the count of values in Table A for that grouping that aren't in table B;
So if table A and B look like:
Table A
Id | Value
==========
1 | "A"
1 | "B"
2 | "C"
And table B:
Id | Value
==========
1 | "A"
1 | "D"
2 | "C"
I would want:
Id | Count
==========
1 | 1
2 | 0
You can use left join and group by:
select a.id, sum( (b.id is null)::int )
from a left join
b
on a.id = b.id and a.value = b.value
group by a.id;
Use except and subquery
with a as
(
select 1 as id, 'A' as v
union all
select 1,'B'
union all
select 2,'C'
),b as
(
select 1 as id, 'A' as v
union all
select 1,'D'
union all
select 2,'C'
), c as
(
select id,v from a except select id,v from b
)
select id,sum ( (select count(*) from c where c.id=a.id and c.v=a.v))
from a group by id
output
id cnt
1 1
2 0
online demo which will work in redshift

how to combine multiple column of different table into one table

I have three tables a, b and c and need to arrange these table data as target table and all of these tables (a, b, c) are not in database they are fetched from from single table using queries as alias and need to arrange these tables into target table using query. How to do that
table a | table b | table c
| |
id | a_vals | id | b_vals | id | c_vals
------------ | -------------- | -------------
1 | 123 | 1 | 123 | 1 | 123
2 | 124 | 2 | 142 | 2 | 142
3 | 234 | 4 | 234 | 5 | 234
target table
id | a_val| b_val| c_val
1 | 123 | 123 | 123
2 | 124 | 142 | 142
3 | 234 | - | -
4 | - | 234 | -
5 | - | | 234
Since a, b and c share the same name for the column you want to join, you could use "USING" to avoid duplicate keys in the resulting table:
SELECT *
FROM a
FULL OUTER JOIN b USING (id)
FULL OUTER JOIN c USING (id);
Alternativly, since a, b and c's value column all have distinct names you could use NATURAL JOIN:
SELECT *
FROM a
NATURAL FULL OUTER JOIN b
NATURAL FULL OUTER JOIN c;
Be careful not to accidentally rename any of the other columns tho, since natural join removes any duplicate columns.
You can also omit the "OUTER" keyword if you like, but i would leave it for clarity, (since LEFT, RIGHT, and FULL imply an outer join).
See https://www.postgresql.org/docs/10/static/queries-table-expressions.html for details
Please try this:
select aa.id, a_val, b_val, c_val from
(select distinct id as id from table_a
union
select distinct id as id from table_b
union
select distinct id as id from table_c)aa
left join (select id, a_val from table_a)bb on aa.id = bb.id
left join (select id, b_val from table_b)cc on aa.id = cc.id
left join (select id, c_val from table_c)dd on aa.id = dd.id order by aa.id;
Try this code
SELECT
CASE
WHEN t1.id IS not null THEN t1.id
WHEN t2.id IS not null THEN t2.id
ELSE t3.id
END
AS id,
t1.a_vals AS a_val,
t2.b_vals as b_val,
t3.c_vals as c_val
FROM a t1 FULL OUTER JOIN b t2 ON t1.id=t2.id FULL OUTER JOIN c t3 ON
CASE
WHEN t1.id IS not null THEN t1.id
ELSE t2.id
END = t3.id
OR
SELECT COALESCE(t1.id, t2.id, t3.id) as id ,
t1.a_vals AS a_val,
t2.b_vals as b_val,
t3.c_vals as c_val
FROM a t1 FULL OUTER JOIN b t2 ON t1.id=t2.id
FULL OUTER JOIN c t3 ON COALESCE(t1.id, t2.id) = t3.id
You are looking for the ANSI-standard FULL OUTER JOIN:
select coalesce(a.id, b.id, c.id) as id, a.val, b.val, c.val
from a full join
b
on a.id = b.id full join
c
on c.id = coalesce(a.id, b.id);
You can also implement this with union all/group by:
select id, max(a_val) as a_val, max(b_val) as b_val, max(c_val) as c_val
from ((select id, val as a_val, null as b_val, null as c_val
from a
) union all
(select id, null as a_val, val as b_val, null as c_val
from b
) union all
(select id, null as a_val, null as b_val, val as c_val
from c
)
) abc
group by id;
This is probably better done in the 'front end' e.g. this is the kind of thing a reporting tool is designed for.
Avoiding nulls and outer joins (because they by definition produce nulls):
SELECT a_val, b_val, c_val
FROM a
NATURAL JOIN b
NATURAL JOIN c
UNION
SELECT a_val, '-' AS b_val, '-' AS c_val
FROM a
WHERE id NOT IN ( SELECT id FROM b )
AND id NOT IN ( SELECT id FROM c )
UNION
SELECT '-' AS a_val, b_val, '-' AS c_val
FROM b
WHERE id NOT IN ( SELECT id FROM a )
AND id NOT IN ( SELECT id FROM c )
UNION
SELECT '-' AS a_val, '-' AS b_val, c_val
FROM c
WHERE id NOT IN ( SELECT id FROM a )
AND id NOT IN ( SELECT id FROM b );

How to join two tables when there's no coincidence?

I have two tables that I want to join. I've tried an usual left and right join but neither gives the result I want.
TABLE A
ID_A VALUE_A
-----------------
A 1
B 2
TABLE B
ID_B ID_A VALUE_B
-------------------------
90 A 1
90 C 1
90 E 1
91 A 1
91 B 1
92 B 1
92 E 1
92 F 1
I want to get this result:
ID_A VALUE_A ID_B ID_A VALUE_B
-------------------------------------------------
A 1 90 A 1
B 2 90 NULL NULL
A 1 91 A 1
B 2 91 B 1
A 1 92 NULL NULL
B 2 92 B 1
If I understand correctly, you want all combinations of id_a and value_a from the first table along with all distinct id_b from the second table. If so:
select iv.id_a, iv.value_a, ib.id_b, b.id_a, b.value_b
from (select distinct id_a, value_a from a) iv cross join
(select distinct id_b from b) ib left join
b
on b.id_b = ib.id_b and b.id_a = iv.id_a;
The cross join generates the rows. The left join brings in the additional columns.
I usually break things like this down into CTEs:
DDL
use tempdb
CREATE TABLE Table1
([ID_A] varchar(1), [VALUE_A] int)
;
INSERT INTO Table1
([ID_A], [VALUE_A])
VALUES
('A', 1),
('B', 2)
;
CREATE TABLE Table2
([ID_B] int, [ID_A] varchar(1), [VALUE_B] int)
;
INSERT INTO Table2
([ID_B], [ID_A], [VALUE_B])
VALUES
(90, 'A', 1),
(90, 'C', 1),
(90, 'E', 1),
(91, 'A', 1),
(91, 'B', 1),
(92, 'B', 1),
(92, 'E', 1),
(92, 'F', 1)
;
Answer
with a as (
select distinct id_b
from Table2
),
b as (
select id_a, value_a, id_b
from Table1 cross join a
)
select b.id_a, b.value_a, b.id_b, t2.id_a, t2.value_b
from b left join Table2 t2
on b.id_a = t2.id_a
and b.id_b = t2.id_b
Results
+------+---------+------+------+---------+
| id_a | value_a | id_b | id_a | value_b |
+------+---------+------+------+---------+
| A | 1 | 90 | A | 1 |
| B | 2 | 90 | NULL | NULL |
| A | 1 | 91 | A | 1 |
| B | 2 | 91 | B | 1 |
| A | 1 | 92 | NULL | NULL |
| B | 2 | 92 | B | 1 |
+------+---------+------+------+---------+
I couldn't resolve the exact logic and couldn't match the results exactly as desired , but presume you'd like to get something like :
SELECT a.ID_A, COALESCE(a.VALUE_A,b.VALUE_B) VALUE_A, b.ID_B, a.ID_A,
(CASE WHEN a.ID_A IS NULL THEN a.ID_A ELSE CAST(b.VALUE_B as VARCHAR(1)) END)
as VALUE_B
FROM TABLE_A a FULL OUTER JOIN TABLE_B b
ON ( a.ID_A = b.ID_A )
GROUP BY a.ID_A, a.VALUE_A, b.ID_B, a.ID_A, b.VALUE_B
ORDER BY 3, 2, 1;
SQL Fiddle Demo
Try this:
SELECT A.ID_A , A.VALUE_A , B.ID_B , B.ID_A , B.VALUE_B
FROM TABLE_A A
LEFT OUTER JOIN TABLE_B B
ON A.ID_A = B.ID_A ;
EDIT: Typos corrected following sticky bit note (thanks!!).

insert multiple rows from select statement in sql

I have a table that need to be migrate to new table.
Table A migrates to Table B
Table A
| ID | type |
| A1 | A |
| A2 | B |
| A3 | A |
| A4 | both |
| A5 | A |
and I hope the table B will look like this
Table B
| ID | FKA | TYPE |
| B1 | A1 | Aa |
| B2 | A2 | Bb |
| B3 | A3 | Aa |
| B4 | A4 | Aa |
| B5 | A4 | Bb |
| B6 | A5 | Aa |
If you realized that if type is both, it will insert two times in table B from table A.
**FKA is the foreign key from table A
Currently I do 3 queries
query 1:
insert into tableB
select sequence1.nextVal, ID,
(case
when type = 'A' then 'Aa'
when type = 'B' then 'Bb'
when type = 'both' then 'Aa'
else NULL
end
) from tableA
query 2
insert into tableB
select sequence1.nextVal, ID,
(case
when type = 'both' then 'Bb'
else 'laterdelete'
end
) from tableA
query 3
delete from tableB where type = 'laterdelete'
thanks guys
I assume the rdbms is Oracle. You might want to create a table (e.g. "tablemapping") with these values
FieldFrom FieldTo
A Aa
B Bb
both Aa
both Bb
So you could do just:
Insert into tableB
select sequence1.nextval, ID, FieldTo
FROM tableA a
join tablemapping m
on a.type=m.fieldFrom
If you don't want to have a mapping table you can simulate one.
Insert into tableb
select sequence1.nextval, ID, FieldTo
FROM tableA a
join (
select 'both' as FieldFrom, 'Ab' as FieldTo from dual
Union all
select 'both' as FieldFrom, 'Bb' as FieldTo from dual
Union all
select 'A' as FieldFrom, 'Aa' as FieldTo from dual
Union all
select 'B' as FieldFrom, 'Bb' as FieldTo from dual
) tablemapping m
on a.type=m.fieldFrom
You can use union in your first query to achieve the desired result in tableB.
Query would be as given below:
insert into tableB
select sequence1.nextVal, ID,
(case
when type = 'A' then 'Aa'
when type = 'B' then 'Bb'
when type = 'both' then 'Aa'
else NULL
end
) from tableA
UNION
select sequence1.nextVal, ID,
(case
when type = 'A' then 'Aa'
when type = 'B' then 'Bb'
when type = 'both' then 'Bb'
else NULL
end
) from tableA
The above query will insert Aa and Bb in tableB when type is both in tableA.
Hope this helps.