SQL joins on phone_number where duration is less than 10min and returning only name column - postgresql

I have 2 tables; phones and calls. and I am trying to write a SQL query that finds all clients who talked for at least 10 minutes in total. So the 'phone_number' in phones would match with the 'caller' and 'callee' column in calls and we can sum the durations to find which names have at least 10 minute calls.
phones
name phone_number
..................
Jack 1234
Lena 3333
Mark 9999
Anna 7582
calls
id caller callee duration
.............................
25 1234 7582 8
7 9999 7582 1
18 9999 3333 4
2 7582 3333 3
3 3333 1234 1
21 3333 1234 1
The query should return only the name column alphabetically.
name
.....
Anna
Jack
I know I have to use some type of join and aggregate function here but not sure how to start.
Anyone have any ideas?
Thanks!!

Join both tables on the caller column and group by name. Do the same for the callee column and UNION both results together. Then group by name and filter by SUM(duration).
SELECT name
FROM phones JOIN calls ON phones.phone_number = calls.caller
UNION
SELECT name
FROM phones JOIN calls ON phones.phone_number = calls.callee
GROUP BY name
HAVING SUM(duration) > 10;

Hi you can do it by using CTE (result here)
with call_duration as (
select caller as phone_number, sum(duration) as duration from calls group by caller
union all
select callee as phone_number, sum(duration) as duration from calls group by callee
)
SELECT name
FROM phones p join call_duration cd on cd.phone_number = p.phone_number
GROUP BY name
HAVING SUM(duration) >= 10
ORDER BY name

Related

Getting percentage change between selected data within a column in PostgreSQL

I am using PostgreSQL and I am trying to calculate the percentage change for two values in the same column and group them by the name column and I am having trouble.
Suppose I have the following table:
name
day
score
Allen
1
87
Allen
2
89
Allen
3
95
Bob
1
64
Bob
2
68
Bob
3
75
Carl
1
71
Carl
2
77
Carl
3
80
I want the result to be the name and the percentage change for each person between day 3 and day 1. So Allen would be 9.2 because from 87 to 95 is a 9.2 percent increase.
I want the result to be:
name
percent_change
Allen
9.2
Bob
17.2
Carl
12.7
Thanks for your help.
Try this...
with dummy_table as (
select
name,
day,
score as first_day_score,
lag(score, 2) over (partition by name order by day desc) as last_day_score
from YOUR_TABLE_NAME
)
select
name,
(last_day_score - first_day_score) / first_day_score::decimal as percentage_change
from dummy_table where last_day_score is not null
Just replace YOUR_TABLE_NAME. There are likely more performant and fancier solutions, but this works.
You can try with lag function, something like this:
select name, day, score, 100*(score - lag(score, 1) over (partition by name order by day))/(lag(score, 1) over (partition by name order by day)) as growth_percentage

PostgreSQL Query to get the output from same table with same table values

I have one table
id employee leave_days leave_type type
1 ABC 10 sick remove
2 ABC 20 sick add
3 ABC 15 Annual remove
4 ABC 50 Annual add
5 XYZ 10 sick remove
6 XYZ 20 sick add
7 XYZ 15 Annual remove
8 XYZ 50 Annual add
From the above table I will group by the column name called leave_type and then I will merge rows and the output should be as follows.
I have to group by column name leave_type and add new column called leave_allocated . In the leave_allocated column, the column type with value add only will come.
id employee leave_days leave_type leave_allocated
1 ABC 10 sick 20
2 ABC 15 Annual 50
3 XYZ 10 sick 20
4 XYZ 15 Annual 50
I tried with sub query I could not match the inner query with outer query .
This should help
SELECT id,
employee,
leave_dates,
leave type,
(SELECT leave_days
FROM TABLE t2
WHERE t2.id = t1.id
AND t2.type = 'add'
) leave_allocated
FROM TABLE t1
WHERE t1.type = 'remove'

TSQL in a 3 column table, I need the MAX score with the earliest

I have a 3 column table that shows a person's score and the ID representing the record of their "test attempt".
TABLE1
empid score attempt_id
1 10565 10001
1 10700 10010
1 12500 10009
1 13000 10025
1 13000 10021
2 10565 10041
2 10700 10020
2 12500 10029
3 13000 10035
4 13000 10051
I'm trying to pull a recordset that contains the employee id along with their maximum score and smallest attempt_id (if there are multiple records with the same max score).
Result
empid score attempt_id
1 13000 10021
2 12500 10029
3 13000 10035
4 13000 10051
I can't seem to get the right SQL.
Any help?
Give this a whirl.. Get the max score and put it in a subquery, then in the main query join to it and get the min attempt.
SELECT ms.empid, ms.max_score, MIN(attempt_id)
FROM Table1 ma
JOIN (
SELECT empid, Max(score) as max_score
FROM Table1
GROUP BY empid ) ms ON ma.empid = ms.empid AND ma.score = ms.max_score
GROUP BY ms.empid, ms.max_score
ORDER BY ms.empid

Query to match multiple column values with multiple rows

I have the following table in my postgresql 9.1
Table contact:
contact_id phone mobile
1 123 456
2 111 222
3 333 123
4 222 444
Table role:
contact_fk exchange
7 1
8 2
1 4
5 5
2 4
4 5
I need the result like:
contact_id phone mobile exchange
1 123 456 4
3 333 123 4
2 111 222 4
I want all the contact data whose mobile field data is in any other contacts phone field and the user must be in exchange 4, which is available in contact_role table
FYI: the contact table contains around 50k rows so joining the contact table to itself taking a lot time, so we must apply the contact_role condition together.
Joining 50k table with 2 columns (where contact_fk is probably primary) shouldn't take long time.
SELECT t1.contact_id, t1.phone, t1.mobile, t2.exchange
FROM contact as t1
JOIN role as t2 ON t1.contact_id = t2.contact_fk
WHERE t2.exchange = 4
Or if you have index on exchange column, this probably will be faster:
SELECT t1.exchange, t2.contact_id, t2.phone, t2.mobile
FROM role as t1
JOIN contact as t2 ON t1.contact_fk = t2.contact_id
WHERE t1.exchange = 4
Here is the answer
select t1.contact_id,
t1.phone,
t1.mobile,
t2.phone,
t2.exchange
from contact t1
inner join (select contact_id,
mobile,
phone,
exchange
from contact, contact_role
where contact.contact_id = contact_fk
and exchange = 4
) t2
on t1.mobile = t2.phone
and t1.mobile != '';

TSQL Join to get all records from table A for each record in table B?

I have two tables:
PeriodId Period (Periods Table)
-------- -------
1 Week 1
2 Week 2
3 Week 3
EmpId PeriodId ApprovedDate (Worked Table)
----- -------- ------------
1 1 Null
1 2 2/28/2013
2 2 2/28/2013
I am trying to write a query that results in this:
EmpId Period Worked ApprovedDate
----- -------- --------- ------------
1 Week 1 Yes Null
1 Week 2 Yes 2/28/2013
1 Week 3 No Null
2 Week 1 No Null
2 Week 2 Yes 2/28/2013
2 Week 3 No Null
The idea is that I need each Period from the Periods table for each Emp. If there was no record in the Worked table then the 'No' value is placed Worked field.
What does the TSQL look like to get this result?
(Note: if it helps I also have access to an Employee table that has EmpId and LastName for each employee. For performance reasons I'm hoping not to need this but if I do then so be it.)
You should be able to use the following:
select p.empid,
p.period,
case
when w.PeriodId is not null
then 'Yes'
else 'No' End Worked,
w.ApprovedDate
from
(
select p.periodid, p.period, e.empid
from periods p
cross join (select distinct EmpId from worked) e
) p
left join worked w
on p.periodid = w.periodid
and p.empid = w.empid
order by p.empid
See SQL Fiddle with Demo