Summarize count of multi table in single SQL query - sql

I have three table with below details:
Table 1: worklog
+-----------+------------+-------------+
| worklogid | technician | description |
+-----------+------------+-------------+
| 1 | john | some text |
+-----------+------------+-------------+
| 2 | jack | some text |
+-----------+------------+-------------+
| 3 | john | some text |
+-----------+------------+-------------+
| 4 | jenifer | some text |
+-----------+------------+-------------+
Table 2: task
+--------+-------+-------------+
| taskid | owner | description |
+--------+-------+-------------+
| 1 | john | some text |
+--------+-------+-------------+
| 2 | john | some text |
+--------+-------+-------------+
| 3 | john | some text |
+--------+-------+-------------+
| 4 | jack | some text |
+--------+-------+-------------+
Table 3: request
+-----------+------------+-----------+-------------+
| requestid | technician | title | description |
+-----------+------------+-----------+-------------+
| 1 | john | some text | ... |
+-----------+------------+-----------+-------------+
| 2 | sara | some text | ... |
+-----------+------------+-----------+-------------+
| 3 | john | some text | ... |
+-----------+------------+-----------+-------------+
| 4 | jack | some text | ... |
+-----------+------------+-----------+-------------+
Now I need to SQL query for this result:
+------------+------------------+---------------+------------------+
| technician | count(worklogid) | count(taskid) | count(requestid) |
+------------+------------------+---------------+------------------+
| john | 2 | 3 | 2 |
+------------+------------------+---------------+------------------+
| jack | 1 | 1 | 1 |
+------------+------------------+---------------+------------------+
| jenifer | 1 | 0 | 0 |
+------------+------------------+---------------+------------------+
| sara | 0 | 0 | 1 |
+------------+------------------+---------------+------------------+
What should I do?

One method is to just use union all and aggregation:
select techician, sum(is_workid), sum(is_taskid), sum(is_requestid)
from ((select technician, 1 as is_workid, 0 as is_taskid, 0 as is_requestid
from worklog
) union all
(select owner, 0, 1, 0
from task
) union all
(select technician, 0, 0, 1
from request
)
) t
group by technician;
In Postgres, you can also aggregate before joining:
select *
from (select technician, count(*) as num_workid
from worklog
group by technician
) w full join
(select owner as technician, count(*) as num_task
from task
group by owner
) t
using (technician) full join
(select technician, count(*) as num_request
from request
group by technician
) w
using (technician);
With a full join, I find that using is simpler than on clauses. But the name needs to be the same in all the tables.

Related

SQL Count depending on certain conditions

I have two tables.
One have userid and email (users table). The other have payments information (payments table) from the userid in users.
users
+--------+------------+
| Userid | Name |
+--------+------------+
| 1 | Alex T |
| 2 | Jeremy T |
| 3 | Frederic A |
+--------+------------+
payments
+--------+-----------+------------+----------+
| Userid | ValuePaid | PaidMonths | Refunded |
+--------+-----------+------------+----------+
| 1 | 1 | 12 | null |
| 1 | 20 | 12 | null |
| 1 | 20 | 12 | null |
| 1 | 20 | 1 | null |
| 2 | 1 | 1 | null |
| 2 | 20 | 12 | 1 |
| 2 | 20 | 12 | null |
| 2 | 20 | 1 | null |
| 3 | 1 | 12 | null |
| 3 | 20 | 1 | 1 |
| 3 | 20 | 1 | null |
+--------+-----------+------------+----------+
I want to count the PaidMonths taking in consideration the following rules:
If ValuePaid < 10 PaidMonths should be = 0.23 (even if in the column the value seen is any other mumber).
If Refund=1 the PaidMonths should be = 0.
Based on this when i join both tables by userid, and sum the PaidMonths based in the previousrules, i expect to see as result:
+--------+------------+------------+
| userid | Name | paidMonths |
+--------+------------+------------+
| 1 | Alex T | 25.23 |
| 2 | Jeremy T | 13.23 |
| 3 | Frederic A | 1.23 |
+--------+------------+------------+
Can you help me to achieve this in the most elegant way? Should a temporary table be used?
The following gives your desired results, using apply with case expression to map your values:
select u.UserID, u.Name, Sum(pm) PaidMonths
from users u join payments p on p.userid=u.userid
cross apply (values(
case
when valuepaid <10 then 0.23
when Refunded=1 then 0
else PaidMonths end
))x(pm)
group by u.UserID, u.Name
See Working Fiddle

How to select 2 data from 1 column based on other relations in other table

Say, I have a table like the following called "name":
| nid | name |
----------------
| 1 | john |
| 2 | mike |
| 3 | tom |
| 4 | jack |
| 5 | will |
| 6 | david | ...
and another table like the following called "relation_father_son":
| rid | fnid | snid |
---------------------
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 4 | 5 |
| 4 | 2 | 6 | ...
then I would like a result like the following:
| father | son |
------------------
| john | mike |
| john | tom |
| jack | will |
| mike | david | ...
What the should the SQL query be?
The query would be:
SELECT
f.name AS father,
s.name AS son
FROM relation_father_son
INNER JOIN name AS f
ON (nid = fnid)
INNER JOIN name AS s
ON (nid = snid)
First of all, it is confusing that the first table is named as name. You should rename it to a more distinguished name, such as family_names. Read more at: Is name a reserved word in MySQL?
For the desired result, you can use the following query:
SELECT
(SELECT `name` FROM `family_names` WHERE nid=fnid) AS father,
(SELECT `name` FROM `family_names` WHERE nid=snid) AS son
FROM relation_father_son

How to remove duplicate values from oracle join?

I want to create a view that present only the results and not present the duplicates, I have 3 tables in oracle database:
The first table contain general information about a person
+-----------+-------+-------------+
| ID | Name | Birtday_date|
+-----------+-------+-------------+
| 1 | Byron | 12/10/1998 |
| 2 | Peter | 01/11/1973 |
| 4 | Jose | 05/02/2008 |
+-----------+-------+-------------+
The second table contain information about a telephone of the people in the first table.
+-------+----------+----------+----------+
| ID |ID_Person |CELL_TYPE | NUMBER |
+-------+- --------+----------+----------+
| 1221 | 1 | 3 | 099141021|
| 2221 | 1 | 2 | 099091925|
| 3222 | 1 | 1 | 098041013|
| 4321 | 2 | 1 | 088043153|
| 4561 | 2 | 2 | 090044313|
| 5678 | 4 | 1 | 092049013|
| 8990 | 4 | 2 | 098090233|
+----- -+----------+----------+----------+
The Third table contain information about a email of the people in the first table.
+------+----------+----------+---------------+
| ID |ID_Person |MAIL_TYPE | Email |
+------+- --------+----------+---------------+
| 221 | 1 | 1 |jdoe#aol.com |
| 222 | 1 | 2 |jdoe1#aol.com |
| 421 | 2 | 1 |xx12#yahoo.com |
| 451 | 2 | 2 |dsdsa#gmail.com|
| 578 | 4 | 1 |sasaw1#sdas.com|
| 899 | 4 | 2 |cvcvsd#wew.es |
+------+----------+----------+---------------+
if i do a inner join with this tables the result will do something like that
+-----+-------+-------------+----------+----------+----------+----------------+
| ID | Name | Birtday_date| CELL_TYPE| NUMBER |MAIL_TYPE|Email |
+-----+-------+-------------+----------+----------+----------+----------------+
| 1 | Byron | 12/10/1998 | 3 | 099141021|1 |jdoe#aol.com |
| 1 | Byron | 12/10/1998 | 3 | 099141021|2 |jdoe1#aol.com |
| 1 | Byron | 12/10/1998 | 2 | 099091925|1 |jdoe#aol.com |
| 1 | Byron | 12/10/1998 | 2 | 099091925|2 |jdoe1#aol.com |
| 1 | Byron | 12/10/1998 | 1 | 098041013|1 |jdoe#aol.com |
| 1 | Byron | 12/10/1998 | 1 | 098041013|2 |jdoe1#aol.com |
| 2 | Peter | 01/11/1973 | 1 | 088043153|1 |xx12#yahoo.com |
| 2 | Peter | 01/11/1973 | 1 | 088043153|2 |dsdsa#gmail.com |
| 2 | Peter | 01/11/1973 | 2 | 090044313|1 |xx12#yahoo.com |
| 2 | Peter | 01/11/1973 | 2 | 090044313|2 |dsdsa#gmail.com |
| 4 | Jose | 05/02/2008 | 1 | 088043153|1 |sasaw1#sdas.com |
| 4 | Jose | 05/02/2008 | 1 | 088043153|2 |cvcvsd#wew.es |
| 4 | Jose | 05/02/2008 | 2 | 088043153|1 |sasaw1#sdas.com |
| 4 | Jose | 05/02/2008 | 2 | 088043153|2 |cvcvsd#wew.es |
+-----+-------+-------------+----------+----------+----------+----------------+
So the result that i will to present in a view is the next
+-----+-------+-------------+----------+----------+----------+----------------+
| ID | Name | Birtday_date| CELL_TYPE| NUMBER |MAIL_TYPE|Email |
+-----+-------+-------------+----------+----------+----------+----------------+
| 1 | Byron | 12/10/1998 | 3 | 099141021|1 |jdoe#aol.com |
| 1 | Byron | 12/10/1998 | | |2 |jdoe1#aol.com |
| 1 | Byron | 12/10/1998 | 2 | 099091925| | |
| 1 | Byron | 12/10/1998 | 1 | 098041013| | |
| 2 | Peter | 01/11/1973 | 1 | 088043153|1 |xx12#yahoo.com |
| 2 | Peter | 01/11/1973 | | |2 |dsdsa#gmail.com |
| 2 | Peter | 01/11/1973 | 2 | 090044313| | |
| 4 | Jose | 05/02/2008 | 1 | 092049013|1 |sasaw1#sdas.com |
| 4 | Jose | 05/02/2008 | | |2 |cvcvsd#wew.es |
| 4 | Jose | 05/02/2008 | 2 | 098090233| | |
+-----+-------+-------------+----------+----------+----------+----------------+
I tried to achieve a similar output using
case
when row_number() over (partition by table1.id order by table2.type) = 1
then table1.value
end
as "VALUE"
But the result is nothing that I expect and some rows they repeats
What you need to do is enumerate the rows and then join on those enumerations. This is tricky, because you don't know how many are in each list. Well, there is another method using conditional aggregation:
select p.id, p.name, p.birthday,
max(cell_type) as cell_type, max(number) as number,
max(mail_type) as mail_type, max(email) as email
from person p left join
((select id_person, cell_type, number,
null as mail_type, null as email,
row_number() over (partition by id_person order by number) as seqnum
from phones
) union all
(select id_person, null as cell_type, null as number,
mail_type, email,
row_number() over (partition by id_person order by email) as seqnum
from emails
)
) pe
on pe.id_person = p.id_person
group by p.id, p.name, p.birthday, pe.seqnum
Hope this helps.
Create table person(ID int ,Name varchar(20), Birtday_date date)
Insert into person values
(1,'Byron' ,'12/10/1998'),
(2,'Peter' ,'01/11/1973'),
(4,'Jose ' ,'05/02/2008')
Create table phones (ID int,ID_Person int,CELL_TYPE int,NUMBER float)
Insert into phones values
(1221, 1 , 3,099141021),
(2221, 1 , 2,099091925),
(3222, 1 , 1,098041013),
(4321, 2 , 1,088043153),
(4561, 2 , 2,090044313),
(5678, 4 , 1,092049013),
(8990, 4 , 2,098090233)
Create table emails(ID int,ID_Person int, MAIL_TYPE int, Email varchar(100))
Insert into emails values
(221, 1 , 1, 'jdoe#aol.com '),
(222, 1 , 2, 'jdoe1#aol.com '),
(421, 2 , 1, 'xx12#yahoo.com '),
(451, 2 , 2, 'dsdsa#gmail.com'),
(578, 4 , 1, 'sasaw1#sdas.com'),
(899, 4 , 2, 'cvcvsd#wew.es ')
select p.id, p.name, p.Birtday_date,
case when Lag(number) over(partition by p.id order by p.id,pe.id) = number then null else cell_type end as cell_type,
case when Lag(number) over(partition by p.id order by p.id,pe.id) = number then null else number end as number,
mail_type as mail_type, email as email
from person p left join
(select pp.ID_Person, cell_type, number, mail_type, email,pp.id from
(select ID_Person, cell_type, number,id,
row_number() over (partition by ID_Person order by id ) as seqnum
from phones
) pp left join
(select ID_Person,
mail_type, email, 1 as seqnum
from emails
)e on pp.ID_Person = e.ID_Person and pp.seqnum = e.seqnum
) pe
on pe.ID_Person = p.Id
order by p.id, pe.id

SQL compare multiple rows or partitions to find matches

The database I'm working on is DB2 and I have a problem similar to the following scenario:
Table Structure
-------------------------------
| Teacher Seating Arrangement |
-------------------------------
| PK | seat_argmt_id |
| | teacher_id |
-------------------------------
-----------------------------
| Seating Arrangement |
-----------------------------
|PK FK | seat_argmt_id |
|PK | Row_num |
|PK | seat_num |
|PK | child_name |
-----------------------------
Table Data
------------------------------
| Teacher Seating Arrangement|
------------------------------
| seat_argmt_id | teacher_id |
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 2 |
------------------------------
---------------------------------------------------
| Seating Arrangement |
---------------------------------------------------
| seat_argmt_id | row_num | seat_num | child_name |
| 1 | 1 | 1 | Abe |
| 1 | 1 | 2 | Bob |
| 1 | 1 | 3 | Cat |
| | | | |
| 2 | 1 | 1 | Abe |
| 2 | 1 | 2 | Bob |
| 2 | 1 | 3 | Cat |
| | | | |
| 3 | 1 | 1 | Abe |
| 3 | 1 | 2 | Cat |
| 3 | 1 | 3 | Bob |
| | | | |
| 4 | 1 | 1 | Abe |
| 4 | 1 | 2 | Bob |
| 4 | 1 | 3 | Cat |
| 4 | 2 | 2 | Dan |
---------------------------------------------------
I want to see where there are duplicate seating arrangements for a teacher. And by duplicates I mean where the row_num, seat_num, and child_name are the same among different seat_argmt_id for one teacher_id. So with the data provided above, only seat id 1 and 2 are what I would want to pull back, as they are duplicates on everything but the seat id. If all the children on the 2nd table are exact (sans the primary & foreign key, which is seat_argmt_id in this case), I want to see that.
My initial thought was to do a count(*) group by row#, seat#, and child. Everything with a count of > 1 would mean it's a dupe and = 1 would mean it's unique. That logic only works if you are comparing single rows though. I need to compare multiple rows. I cannot figure out a way to do it via SQL. The solution I have involves going outside of SQL and works (probably). I'm just wondering if there is a way to do it in DB2.
Does this do what you want?
select d.teacher_id, sa.row_num, sa.seat_num, sa.child_name
from seatingarrangement sa join
data d
on sa.seat_argmt_id = d.seat_argmt_id
group by d.teacher_id, sa.row_num, sa.seat_num, sa.child_name
having count(*) > 1;
EDIT:
If you want to find two arrangements that are the same:
select sa1.seat_argmt_id, sa2.seat_argmt_id
from seatingarrangement sa1 join
seatingarrangement sa2
on sa1.seat_argmt_id < sa2.seat_argmt_id and
sa1.row_num = sa2.row_num and
sa1.seat_num = sa2.seat_num and
sa1.child_name = sa2.child_name
group by sa1.seat_argmt_id, sa2.seat_argmt_id
having count(*) = (select count(*) from seatingarrangement sa where sa.seat_argmt_id = sa1.seat_argmt_id) and
count(*) = (select count(*) from seatingarrangement sa where sa.seat_argmt_id = sa2.seat_argmt_id);
This finds the matches between two arrangements and then verifies that the counts are correct.

SQL Query in MANY- MANY RELATIONSHIP exactly one record with matching criteria

I have 3 like with many - many relationship
As:
TABLE 1 : select * from student;
| id | name |
| 1 | sone |
| 2 | stwo |
| 3 | sthree |
| 4 | sfour |
| 6 | ssix |
TABLE 2 : select * from course;
| id | name |
| 100 | CSE |
| 101 | ECE |
| 102 | ITI |
RELATION_SHIP TABLE : select * from student_course
| id | stu_id | cou_id |
| 1 | 1 | 101 |
| 2 | 2 | 102 |
| 3 | 2 | 100 |
| 4 | 3 | 100 |
| 5 | 3 | 101 |
| 6 | 1 | 101 |
| 1 | 6 | 101 |
I need to write a query to select a student with exactly one course 'CSE' and he should not have any other courses.
Thanks in advance
Use query:
SELECT
sc.`stu_id`,
COUNT(sc.`cou_id`) AS cnt
FROM
student_course sc
GROUP BY sc.`stu_id`
HAVING cnt = 1
AND GROUP_CONCAT(cou_id) LIKE
(SELECT
id
FROM
course
WHERE NAME = 'CSE')