I don't know how to ask this correctly - it was probably asked many times.
I have two tables. Companies and budgets.
Companies [id,name,address]
Budgets [id,company_id,year,budget]
I want to create a select where I would get the result for each company and budgets for each year in the same row.
budgets table:
id | company_id | year | budget
1 | 1 | 2018 | 1500
2 | 1 | 2019 | 2500
3 | 2 | 2018 | 700
4 | 2 | 2019 | 6000
So I would like to get the budgets in year columns
SELECT
a.id,
a.`name`,
IF(b.season_id = 1,b.budget,'') AS "budget 2018",
IF(b.season_id = 2,b.budget,'') AS "budget 2019"
FROM companies AS a
INNER JOIN budgets AS b ON a.id = b.company_id
This of course returns double rows. :)
company name | budget 2018 | budget 2019
company one | 1500 |
company one | | 2500
company two | 700 |
company two | | 6000
How can I get the budgets in the same row?
company name | budget 2018 | budget 2019
company one | 1500 | 2500
company two | 700 | 6000
Or how is this procedure called/described so I can ask uncle google? :)
Edit:
I got suggestions for solving this task with outer joins. Truthfully I haven't had the time to solve the problem with outer joins - not yet confident if I can trust the result. The other thing is that I find the title of the suggested solution somewhat misleading. I ended solving this problem with the use of "fake" aggregates. Because this is a task I need once a year for a custom export I won't be pursuing a "better" solution. Thank you for your input.
With conditional aggregation:
SELECT
c.id,
c.name,
MAX(CASE WHEN b.season_id = 1 THEN b.budget END) AS `budget 2018`,
MAX(CASE WHEN b.season_id = 2 THEN b.budget END) AS `budget 2019`
FROM companies AS c INNER JOIN budgets AS b
ON c.id = b.company_id
GROUP BY c.id, c.name
I use MAX() because in your sample data there is only 1 row for each company/year.
If this is not the case and you want the total for each company/year then use SUM().
You could use a (fake) aggrgation function
SELECT
a.id,
a.`name`,
sum(IF(b.season_id = 1,b.budget,'')) AS `budget 2018`,
sum(IF(b.season_id = 2,b.budget,'')) AS `budget 2019`
FROM companies AS a
INNER JOIN budgets AS b ON a.id = b.company_id
GROUP BY a.id, a.name
probably this is what you wanted to know.
SQL - Select rows from two different tables
use outer join
INNER JOIN budgets AS b ON a.id = b.company_id
to
outer join budgets AS b ON a.id = b.comapny_id
Related
I've been trying for two days, without luck.
I have the following simplified tables in my database:
customers:
| id | name |
| 1 | andrea |
| 2 | marco |
| 3 | giovanni |
access:
| id | name_id | date |
| 1 | 1 | 5000 |
| 2 | 1 | 4000 |
| 3 | 2 | 1500 |
| 4 | 2 | 3000 |
| 5 | 2 | 1000 |
| 6 | 3 | 6000 |
| 7 | 3 | 2000 |
I want to return all the names with their last access date.
At first I tried simply with
SELECT * FROM customers LEFT JOIN access ON customers.id =
access.name_id
But I got 7 rows instead of 3 as expected. So I understood I need to use GROUP BY statemet as the following:
SELECT * FROM customers LEFT JOIN access ON customers.id =
access.name_id GROUP BY customers.id
As far I know, GROUP BY combines using a random row. In fact I got unordered access dates with several tests.
Instead I need to group every customer id with its corresponding latest access! How this can be done?
You have to get the latest date from the access table with a group by on the the name_id, then join this result with the customer table. Here is the query:
select c.id, c.name, a.last_access_date from customers c left join
(select id, name_id, max(access_date) last_access_date from access group by name_id) a
on c.id=a.name_id;
Here is a DEMO on sqlfiddle.
I think this is what you'd like to achieve:
SELECT c.id, c.name, max(a.date) last_access
FROM customers c
LEFT JOIN access a ON c.id = a.name_id
GROUP BY c.id, c.name
The LEFT join will return all entries in table customers regardless if the join criteria (c.id = a.name_id) is satisfied. This means that you might get some NULL entries.
Example:
Simply add a new row in the customers table (id: 4, name: manuela). The output will have 4 rows and the newest row will be (id: 4, last_access: null)
I would do this using a correlated subquery in the ON clause:
SELECT a.*, c.*
FROM customers c LEFT JOIN
access a
ON c.id = a.name_id AND
a.DATE = (SELECT MAX(a2.date) FROM access a2 WHERE a2.name_id = a.name_id);
If this statement is true:
I need to group every customer id with its corresponding latest access! How this can be done?
Then you can simply do:
select a.name_id, max(a2.date)
from access a
group by a.name_id;
You do not need the customers table because:
All customers are in access, so the left join is not necessary.
You need no columns from customers.
I have the following tables schemas and I want to get the sum of amount column for each category and the count of employees in the corresponding categories.
employee
id | name | category
1 | SC | G 1.2
2 | BK | G 2.2
3 | LM | G 2.2
payroll_histories
id | employee_id | amount
1 | 1 | 1000
2 | 1 | 500
3 | 2 | 200
4 | 2 | 100
5 | 3 | 300
Output table should look like this:
category | total | count
G 1.2 | 1500 | 1
G 2.2 | 600 | 2
I have tried this query below its summing up and grouping but I cannot get the count to work.
SELECT
employee_id,
category,
SUM(amount) from payroll_histories,employees
WHERE employees.id=payroll_histories.employee_id
GROUP BY category;
I have tried the COUNT(category) but that one too is not working.
You are, I believe, seeking two different summaries of your data. One is a sum of salaries by category, and the other is a count of employees, also by category.
You need to use, and then join, separate aggregate queries to get this.
SELECT a.category, a.amount, b.cnt
FROM (
SELECT e.category, SUM(p.amount) amount
FROM employees e
JOIN payroll_histories p ON e.id = p.employee_id
GROUP BY e.category
) a
JOIN (
SELECT category, COUNT(*) cnt
FROM employees
GROUP BY category
) b ON a.category = b.category
The general principle here is to avoid trying to use just one aggregate query to aggregate more than one kind of detail entity. Your amount aggregates payroll totals, whereas your count aggregates employees.
Alternatively for your specific case, this query will also work. But it doesn't generalize well or necessary perform well.
SELECT e.category, SUM(p.amount) amount, COUNT(DISTINCT e.id) cnt
FROM employees e
JOIN payroll_histories p ON e.id = p.employee_id
GROUP BY e.category
The COUNT(DISTINCT....) will fix the combinatorial explosion that comes from the join.
(Pro tip: use the explicit join rather than the outmoded table,table WHERE form of the join. It's easier to read.)
I am stuck with an this SQL problem: I need to find all members who did not pay their annual fees for 2014. Here is a sample of the database:
Table 'members'
| ID | name |
---------------
| 1 | Franck |
| 2 | Andy |
| 3 | Jack |
Table 'payements'
| ID | memberID | year | amount |
------------------------------------
| 1 | 1 | 2013 | 100 |
| 2 | 1 | 2014 | 100 |
| 3 | 2 | 2013 | 100 |
And I tried something like this:
SELECT members.name FROM members
LEFT JOIN payements ON (payements.memberID = members.ID)
WHERE (payements.year = 2014 AND payements.amount < 100) OR payements.memberID IS NULL
My query correctly finds Jack (who did never pay anything) but fails to find Andy because an entry exists for another year. How can I ask for all members who have no entry specifically for 2014 (or an entry with an amount below 100)?
Consider this data in terms of sets
Set 1 everyone who should have paid
Set 2 people who is paid up correctly
We join the sets together as a left join excluding those who have paid in 2014 from the rest,
we add the limits to the join so that only payments for current year in full are listed. we then exclude those from the complete set of users..
Select m.name, p.memberid, p.year, p.amount
from members m
LEFT JOIN payements p
on m.id = p.memberId
and (p.year = 2014 and p.amount >= 100)
WHERE p.year is null
The reason why your's didn't work was because the where clause was making the outer join an inner join. AND because you wanted a set of users who haven't paid. Not the set who has paid. So we needed to setup the second set as those who have paid... changing < to >=.
Another way using sub-querys in WHERE.
In the sub-query you find all members who DID pay their annual fees. So in the outer query you keep only the members not inside the sub-query, those are the ones you want.
SELECT name
FROM members
WHERE ID NOT IN (SELECT memberID
FROM payements
WHERE year = 2014 AND amount < 100)
BTW, do you mean amount <= 100 ?...
EDIT:
For members who paid their fees in 2014, the amount must be greater or equal than 100, so here is a corrected version:
SELECT name
FROM members
WHERE ID NOT IN (SELECT memberID
FROM payements
WHERE year = 2014 AND amount >= 100)
Added a new member "Amy" in your test, who only paid an amount of 80 in 2014, she is listed with Andy and Jack:
Andy
Jack
Amy
SQL FIDDLE
SELECT * FROM members WHERE ID NOT IN(SELECT memberID FROM payments WHERE year='2014')
This question already has answers here:
Sum total of table with two related tables
(2 answers)
Closed 9 years ago.
I have 4 tables, with the relevant columns summarized here:
customers:
id
name
credits:
id
customer_id # ie customers.id
amount
sales:
id
customer_id # ie customers.id
sales_items:
id
sale_id # ie sales.id
price
discount
The idea is that customers lists all of our customers, credits lists each time they have paid us, sales lists each time they have bought things from us (but not what things they bought) and sales_items lists all of the items they bought at each of those sales. So you can see that credits and sales both relate back to customers, but sales_items only relates back to sales.
As an example dataset, consider:
customers:
id | name
5 | Carter
credits:
id | customer_id | amount
1 | 5 | 100
sales:
id | customer_id
3 | 5
sales_items:
id | sale_id | price | discount
7 | 3 | 5 | 0
8 | 3 | 0 | 0
9 | 3 | 10 | 0
I have tried this in MySQL:
SELECT c.*,
SUM( cr.amount ) AS paid,
SUM( i.price + i.discount ) AS bought
FROM customers AS c
LEFT JOIN sales AS s ON s.customer_id = c.id
LEFT JOIN sales_items AS i ON i.sale_id = s.id
LEFT JOIN credits AS cr ON cr.customer_id = c.id
WHERE c.id = 5
But it returns:
id | name | paid | bought
5 | Carter | 300 | 15
If I omit the SUM() functions, it returns:
id | name | paid | bought
5 | Carter | 100 | 5
5 | Carter | 100 | 0
5 | Carter | 100 | 15
So it looks like it's returning one row for every record matched in sales_items, but it's filling in the amount column with same value from credits each time. I see that this is happening, but I'm not understanding why it's happening.
So, two questions:
1. What is happening that it's smearing that one value through all of the rows?
2. What SQL can I throw at MySQL so that I can get this back:
id | name | paid | bought
5 | Carter | 100 | 15
I know that I could break it all up in subqueries, but is there a away to do it just with joins? I was hoping to learn a thing or two about joins as I tackled this problem. Thank you.
Edit: I created an SQL Fiddle for this: http://sqlfiddle.com/#!2/0051b/1/0
select distinct (c.id, c.name), sum(i.price+i.discount) AS bought, cr.amount AS paid
from customer c, credits cr, sales s, sales_items i
where s.customer_id = c.id
and i.sale_id = s.id
and cr.customer_id = c.id and c.id = 5
group by c.id, c.name;
I'm not very sure, but try this. Use group by; that is surely the solution.
Please try this
SELECT c.*,( SELECT SUM( cr.amount ) FROM customer c INNER JOIN credits cr ON
cr.customer_id = c.id WHERE c.id = 5 GROUP BY cr.id ) AS paid
,SUM( i.price + i.discount ) AS bought
FROM customers AS c INNER JOIN sales s ON s.customer_id = c.id
INNER JOIN sales_items i ON i.sale_id = s.id
INNER JOIN credits cr ON cr.customer_id = c.id
WHERE c.id = 5 GROUP BY s.id,cr.id
I have two tables: 'company' and 'order'. The first one contains company info and the second one holds all orders made with a company. (order.company = company.ID).
I am making a query on the first table, for example all companies in the city of New York. I would like to make a join with the order table, so that it immediately shows how many orders for a company was made. I could do this with a simple JOIN query, however, it does not include 0. For example, if a company has no orders yet, it will not show up at all, while it should be in the list with 0 orders.
Desired end result:
----------------------------------------
| ID | Name | ... | Orders |
----------------------------------------
| 105 | Company A | ... | 14 |
| 115 | Company B | ... | 5 |
| 120 | Company C | ... | 0 |
| 121 | Company D | ... | 0 |
----------------------------------------
Thanks in advance!
This is a left join with aggregation:
SELECT c.ID, c.Name, count(o.company) as total
FROM companies c left outer join
orders o
on c.id = o.company
WHERE c.city = 'New York'
GROUP BY c.ID;
In MySQL, it is best to avoid subqueries in the from clause -- where possible -- because the derived table is actually created.
The COUNT() expression is counting the number of matches by counting the number of non-null values in the id field used for the join.
Try this
SELECT com.id,com.name,od.orders FROM compnay AS com
LEFT JOIN orders AS od ON od.company = com.id;
SELECT companies.ID,companies.Name ,orders.total FROM
(SELECT ID,Name FROM company where county ='NEW YORK') companies
LEFT JOIN (SELECT company,COUNT(*) as total FROM order GROUP BY company) orders
ON orders.company = companies.ID