MySQL: How to select and display ALL rows from one table, and calculate the sum of a where clause on another table? - sql

I'm trying to display all rows from one table and also SUM/AVG the results in one column, which is the result of a where clause. That probably doesn't make much sense, so let me explain.
I need to display a report of all employees...
SELECT Employees.Name, Employees.Extension
FROM Employees;
--------------
| Name | Ext |
--------------
| Joe | 123 |
| Jane | 124 |
| John | 125 |
--------------
...and join some information from the PhoneCalls table...
--------------------------------------------------------------
| PhoneCalls Table |
--------------------------------------------------------------
| Ext | StartTime | EndTime | Duration |
--------------------------------------------------------------
| 123 | 2010-09-05 10:54:22 | 2010-09-05 10:58:22 | 240 |
--------------------------------------------------------------
SELECT Employees.Name,
Employees.Extension,
Count(PhoneCalls.*) AS CallCount,
AVG(PhoneCalls.Duration) AS AverageCallTime,
SUM(PhoneCalls.Duration) AS TotalCallTime
FROM Employees
LEFT JOIN PhoneCalls ON Employees.Extension = PhoneCalls.Extension
GROUP BY Employees.Extension;
------------------------------------------------------------
| Name | Ext | CallCount | AverageCallTime | TotalCallTime |
------------------------------------------------------------
| Joe | 123 | 10 | 200 | 2000 |
| Jane | 124 | 20 | 250 | 5000 |
| John | 125 | 3 | 100 | 300 |
------------------------------------------------------------
Now I want to filter out some of the rows that are included in the SUM and AVG calculations...
WHERE PhoneCalls.StartTime BETWEEN "2010-09-12 09:30:00" AND NOW()
...which will ideally result in a table looking something like this:
------------------------------------------------------------
| Name | Ext | CallCount | AverageCallTime | TotalCallTime |
------------------------------------------------------------
| Joe | 123 | 5 | 200 | 1000 |
| Jane | 124 | 10 | 250 | 2500 |
| John | 125 | 0 | 0 | 0 |
------------------------------------------------------------
Note that John has not made any calls in this date range, so his total CallCount is zero, but he is still in the list of results. I can't seem to figure out how to keep records like John's in the list. When I add the WHERE clause, those records are filtered out.
How can I create a select statement that displays all of the Employees and only SUMs/AVGs the values returned from the WHERE clause?

Use:
SELECT e.Name,
e.Extension,
Count(pc.*) AS CallCount,
AVG(pc.Duration) AS AverageCallTime,
SUM(pc.Duration) AS TotalCallTime
FROM Employees e
LEFT JOIN PhoneCalls pc ON pc.extension = e.extension
AND pc.StartTime BETWEEN "2010-09-12 09:30:00" AND NOW()
GROUP BY e.Name, e.Extension
The issue is when using an OUTER JOIN, specifying criteria in the JOIN section is applied before the JOIN takes place--like a derived table or inline view. The WHERE clause is applied after the OUTER JOIN, which is why when you specified the WHERE clause on the table being LEFT OUTER JOIN'd to that the rows you still wanted to see are being filtered out.

Related

Perform join on two tables when values in one table may or may not be encloses in double quotes

I have two tables
1.Employee Table
+-------+----------+-----------------+
| empno | empname | deptname |
+-------+----------+-----------------+
| 1 | Ross | dept1_aniosbgis |
| 2 | Joey | dept2_aswefbhjk |
| 3 | Chandler | dept3_xwukpgbtr |
| 4 | Monica | dept1_aniosbgis |
| 5 | Phoebe | dept1_aniosbgis |
| 6 | Rachel | dept3_xwukpgbtr |
+-------+----------+-----------------+
2.Bonuses Table
+---------------+-------+
| department | Bonus |
+---------------+-------+
| dept1_finance | 40000 |
| "dept2_hr" | 50000 |
| "dept3_tech" | 60000 |
+---------------+-------+
I want to perform a join on the 2 tables based on the department name column which is the deptname column in employees and department column in bonuses.
My issue is that the department column has some rows where the names are enclosed in double quotes and some are not. I need to take substring of the department name and it can be done something like this -
substr(department,1,5)
But in case of double quotes , it has to be done like this
substr(department,2,5)
I want to write an if-else statement to perform the join. But I'm not able to construct the query.
I would prefer to use joins ( to maintain the same pattern as with the other tables I have ).
The output should look like -
+-------+-------+
| empno | bonus |
+-------+-------+
| 1 | 40000 |
| 2 | 50000 |
| 3 | 60000 |
| 4 | 40000 |
| 5 | 40000 |
| 6 | 60000 |
+-------+-------+
Please help me in framing the query
Use the function TRIM() to remove leading and trailing double quotes (if they exist) from department:
SELECT e.empno, b.bonus
FROM Employee e LEFT JOIN Bonuses b
ON LEFT(TRIM('"' FROM b.department), 5) = LEFT(e.deptname, 5)
This code works for MySql and Postgresql.
one wya is to join on both condition using IN :
select *
from table1 t1
join tbale2 t2
on t1.deptname in (t2.department, substr(t2.department,2,5) )

SQL group column where other column is equal

I'm trying to select some information from a database.
I get a database with columns like:
Ident,Name,Length,Width,Quantity,Planned
Table data is as follow
+-----------+-----------+---------+---------+------------+---------+
| Ident | Name | Length | Width | Quantity | Planned |
+-----------+-----------+---------+---------+------------+---------+
| 12345 | Name1 | 1500 | 1000 | 20 | 5 |
| 23456 | Name1 | 1500 | 1000 | 30 | 13 |
| 34567 | Name1 | 2500 | 1000 | 10 | 2 |
| 45678 | Name1 | 2500 | 1000 | 10 | 4 |
| 56789 | Name1 | 1500 | 1200 | 20 | 3 |
+-----------+-----------+---------+---------+------------+---------+
my desired result, would be to group rows where "Name,Length and Width" are equal, sum the "Quantity" and reduce it by the sum of "Planned"
e.g:
- Name1,1500,1000,32 --- (32 because (20+30)-(5+13))
- Name1,2500,1000,14 --- (14 because (10+10)-(2+4)))
- Name1,1500,1200,17
now I got problems how to group or join these information to get the wished select. may be some you of can help me.. if further information's required, please write it in comment.
You can achieve it by grouping your table and subtract sums of Quantity and Planned.
select
Name
,Length
,Width
,sum(Quantity) - sum(Planned)
from yourTable
group by Name,Length,Width
select
A1.Name,A1.Length,A1.Width,((A1.Quantity + A2.Quantity) -(A1.Planned+A2.Planned))
from `Table` AS A1, `Table` AS A2
where A1.Name = A2.Name and A1.Length = A2.Length and A1.Width = A2.Width
group by (whatever)
So you are comparing these columns form the same table?

SQL select values sum by same ID

here is my table called "Employee"
eID | name |
==============
1 | Mike |
2 | Josh |
3 | Mike |
And table called "Sells"
sID | eID | | price |
=========================
1 | 1 | | 8 |
2 | 3 | | 9 |
3 | 3 | | 5 |
4 | 1 | | 4 |
5 | 1 | | 3 |
This should be my expected result: returns the total income per employee
name | Income |
==================
Mike | 15 |
Josh | 0 |
Mike | 14 |
Actually, I know use the query "SUM...GROUP BY..." to get the incomes of 15 and 14, but I don't know how to get the income of 0 which is not shown on the "Sells" table.
Could someone give me some help? Thanks a lot.
You just need to use a left outer join, so you can get the sum for missing values too. You could use case expression to deal with null values
SELECT e.name,
COALESCE(SUM(price), 0) as Income
FROM employees e
LEFT OUTER JOIN sells s
ON e.eid = s.eid
GROUP BY e.eid, e.name
Edited: case expression is not needed. I put coalesce on the return of sum fuction, in order to deal with missing values (SUM over an empty set returns NULL)

How to join a number with the range of number from another table

I have the two following tables:
| ID | Count |
| --- | ----- |
| 1 | 45 |
| 2 | 5 |
| 3 | 120 |
| 4 | 87 |
| 5 | 60 |
| 6 | 200 |
| 7 | 31 |
| SizeName | LowerLimit | UpperLimit |
| -------- | ---------- | ---------- |
| Small | 0 | 49 |
| Medium | 50 | 99 |
| Large | 100 | 250 |
Basically, one table specifies an unknown number of range names and their associated integer ranges. So a count range of 0 to 49 from the person table gets a small designation. 50-99 gets 'medium' etc. I need it to be dynamic because I do not know the range names or integer values.
Can I do this in a single query or would I have to write a separate function to loop through the possibilities?
One way to do this would be to join the tables, depending on if you want to keep values outside of your "range names", or not, you could use LEFT, or INNER join respectively.
SELECT A.id, A.Count, B.SizeName
FROM tableA A
LEFT JOIN tableB B ON A.id >= B.LowerLimit AND A.id < B.UpperLimit
You can also use the BETWEEN operator in a JOIN like this:
SELECT a.id, a.Count, b.SizeName
FROM tableA a
JOIN tableB b ON a.id BETWEEN b.LowerLimit AND b.UpperLimit

Left Join COUNT on tables

I have 2 tables:
puid | personid | ptitle
----------------------------
1 | 200 | richard
2 | 201 | swiss
suid | personidref | stitle
----------------------------
1 | 200 | alf
2 | 201 | lando
3 | 200 | willis
4 | 201 | luke
5 | 201 | kojak
6 | 200 | r2-d2
7 | 201 | jabba
I am trying to left join with a count of table two. I have tried to figure out to use generate_series or sub selects but I cant noodle the syntax.
In english: show me each unique person in table one with a count of each entry in table two.
example output:
puid | personid | ptitle | count
---------------------------------
1 | 200 | richard | 3
2 | 201 | swiss | 4
Is this is simple subquery, is generate_series the right tool for the job?
select *
from
t1
left join
(
select personidref, count(*) total
from t2
group by personidref
) s using(personidref)
order by puid
Notice that doing the aggregation before joining probably has a performance gain over doing it after.