Sum sales from different tables - mysql
I have 5 tables related to sales. Three of them are like this:
Product_table_image
They are called product_a, product_b, product_c
The other tables are the time_id table which contains the reference for the date_id and the customers' table, which contains the details of customers.
time_id table
and
customer_table
The 3 tables refer to the sales of different products, but the products are not important in this context, because what I need is to sum up all the values per month per customer. There are cases when one or more customers might not have made a purchase of a certain product, which means not all customers ids will be in all products tables and that's what I can't figure out how to solve. It seems that my code is only able to fetch and sum when the clients have made purchases in all 3 tables.
So this is what I was able to come up with:
SELECT C.customer_id, ROUND((A.pa + B.pa + C.pc)* 1, 2) AS total,C.month_id
FROM (SELECT customer.customer_id, SUM(product_a.amount) AS pa , time_id.month_id FROM customer
INNER JOIN product_a on customer.customer_id = product_a.customer_id
INNER JOIN time_id on product_a.date_id = time_id.date_id
GROUP BY customer.customer_id, time_id.month_id) AS A
CROSS JOIN
(SELECT customer.customer_id, SUM(product_a.amount) AS pb , time_id.month_id FROM customer
INNER JOIN product_b on customer.customer_id = product_b.customer_id
INNER JOIN time_id on product_b.date_id = time_id.date_id
GROUP BY customer.customer_id, time_id.month_id) AS B
CROSS JOIN
(SELECT customer.customer_id, SUM(product_a.amount) AS pc , time_id.month_id FROM customer
INNER JOIN product_c on customer.customer_id = product_c.customer_id
INNER JOIN time_id on product_c.date_id = time_id.date_id
GROUP BY customer.customer_id, time_id.month_id) AS C
GROUP BY C.month_id, C.customer_id
ORDER BY C.month_id;
I've been stuck in it for a while, so any help is appreciated!
I have setup the tables and some sample data to make it more real.
create table month(id int, name varchar(20), primary key (id));
insert into month (id, name) values (1, 'January'),(2,'February'),(3,'March'),(4,'April');
create table year(id int, name varchar(4), primary key (id));
insert into year(id, name) values (2019, '2019'),(2020,'2020'),(2021,'2021');
create table time (id int, month_id int, year_id int, primary key (id));
alter table time add constraint fk_month FOREIGN KEY (month_id) REFERENCES month (id);
alter table time add constraint fk_year FOREIGN KEY (year_id) REFERENCES year (id);
insert into time (id, year_id, month_id) values
(1, 2019, 1),(2, 2019, 2),(3,2019,3),(4,2019,4),
(5, 2020, 1),(6, 2020, 2),(7,2020,3),(8,2020,4),
(9, 2021, 1),(10, 2021, 2),(11,2021,3),(12,2021,4);
create table customers (id int, name varchar(100), city varchar(100), country varchar(100), primary key (id));
insert into customers (id, name, city, country) values
(1, 'Google', 'San Francisco', 'US'),
(2, 'Ambev', 'São Paulo', 'BR'),
(3, 'Merck', 'Darmstadt', 'GE');
create table sales_of_product_a (id int, customer_id int, date_id int, amount decimal(10,2), primary key (id));
alter table sales_of_product_a add constraint fk_pa_time FOREIGN KEY (date_id) REFERENCES time (id);
-- only customer 1 - Google and 3 - Merck purchased product A
insert into sales_of_product_a (id, customer_id, date_id, amount) values
(1, 1, 1, 100.10),(2,1,2,200.20),(3,1,3,300.30),(4,1,4,400.40),
(5, 1, 5, 500.50),(6,1,6,600.60),(7,1,7,700.70),(8,1,8,800.80),
(9, 3, 1, 130.10),(10,3,2,230.20),(11,3,3,330.30),(12,3,4,430.40),
(13, 3, 5, 530.50),(14,3,6,630.60),(15,3,7,730.70),(16,3,8,830.80);
create table sales_of_product_b (id int, customer_id int, date_id int, amount decimal(10,2), primary key (id));
alter table sales_of_product_b add constraint fk_pb_time FOREIGN KEY (date_id) REFERENCES time (id);
-- only customer 1 - Google purchased product B
insert into sales_of_product_b (id, customer_id, date_id, amount) values
(1, 1, 1, 100.10),(2,1,2,200.20),(3,1,3,300.30),(4,1,4,400.40),
(5, 1, 5, 500.50),(6,1,6,600.60),(7,1,7,700.70),(8,1,8,800.80),
(9, 1, 9, 900.90),(10,1,10,1000.01),(11,1,11,1100.11),(12,1,12,1200.12);
create table sales_of_product_c (id int, customer_id int, date_id int, amount decimal(10,2), primary key (id));
alter table sales_of_product_c add constraint fk_pc_time FOREIGN KEY (date_id) REFERENCES time (id);
-- only customer 3 - Merck purchased product C
insert into sales_of_product_c (id, customer_id, date_id, amount) values
(1, 3, 1, 130.10),(2,3,2,230.20),(3,3,3,330.30),(4,3,4,430.40),
(5, 3, 5, 530.50),(6,3,6,630.60),(7,3,7,730.70),(8,3,8,830.80),
(9, 3, 9, 930.90),(10,3,10,1030.01),(11,3,11,1130.11),(12,3,12,1230.12);
The SQL you might be looking for would be something like.
with all_sales as (
select pa.customer_id, tt.month_id, sum(pa.amount) as amount from sales_of_product_a pa inner join time tt on (pa.date_id = tt.id) where tt.year_id = 2019 group by pa.customer_id, tt.month_id
union all
select pb.customer_id, tt.month_id, sum(pb.amount) as amount from sales_of_product_b pb inner join time tt on (pb.date_id = tt.id) where tt.year_id = 2019 group by pb.customer_id, tt.month_id
union all
select pc.customer_id, tt.month_id, sum(pc.amount) as amount from sales_of_product_c pc inner join time tt on (pc.date_id = tt.id) where tt.year_id = 2019 group by pc.customer_id, tt.month_id
),
sales_per_customer_per_month as (
-- summary of all sales of all products per customer per month
select customer_id, month_id, sum(amount) as amount from all_sales group by customer_id, month_id
),
customers_month as (
select c.id, c.name, c.city, c.country, m.id as month_id, m.name as month_name from customers c inner join month m on true
)
select c.id,c.name,c.city,c.country,c.month_id, coalesce(s.amount,0) as amount, sum(coalesce(s.amount,0)) over (partition by c.id order by c.id,c.month_id) as total
from customers_month c
left join sales_per_customer_per_month s on (s.customer_id = c.id and s.month_id = c.month_id)
order by c.id,c.month_id;
The result of query above is following.
The concepts used are linked below.
Window Functions:
https://dev.mysql.com/doc/refman/8.0/en/window-functions.html
Common Table Expressions (CTE):
https://www.mysqltutorial.org/mysql-cte/
Related
MySQL select row from one table with multiple rows in a second table and get array of multi row in selected row
i have one table containing "Client" information, and another including "Tickets" information for each client. int-------| varchar -------| varchar client_id | client_name | client_tickets ----------+----------------+-------------- 1 | Title one | 1,2 2 | Title two | 2,3 Simplified tickets table int--------| varchar -------| varchar ticket_id | ticket_name | ticket_price -----------+-------------+-------------- 1 | ticketone | 30 2 | tickettwo | 40 3 | ticketthree | 50 4 | ticketfour | 60 5 | ticketfive | 70 With the above two tables, I want to produce a single table with a single query with all the pertinent information to generate a search grid So as to give the following output : client_id | client_name | client_tickets | ticket_names | ticket_prices ----------+----------------+----------------+-----------------------+-- 1 | Title one | 1,2 | ticketone,tickettwo | 30,40 2 | Title two | 2,3 | tickettwo,ticketthree | 40,50 ticket_names,ticket_ids,client_name are varchar I want to receive the final 5 columns with one request for example : SELECT s.*, (SELECT GROUP_CONCAT(ticket_name SEPARATOR ',') FROM tickets_table WHERE ticket_id IN(s.client_tickets)) AS ticket_names, (SELECT GROUP_CONCAT(ticket_price SEPARATOR ',') FROM tickets_table WHERE ticket_id IN(s.client_tickets)) AS ticket_prices FROM client_table s where s.client_id=1 Which seems to have a problem Do you have a better suggestion? Please make your suggestions Update : To clean the result I want The following code has two querys, I want this code to be done with a query $client_result = $conn->query("SELECT * FROM client_table where client_id=1"); while($client_row = $client_result->fetch_assoc()) { $ticket_result = $conn->query("SELECT * FROM tickets_table where ticket_id IN ($client_row['client_tickets'])"); while($ticket_row = ticket_result->fetch_assoc()) { echo $ticket_row['ticket_name']."<br>"; } } update 2 i use suggest #raxi , but my mariadb is 10.4.17-MariaDB and don't support JSON_ARRAYAGG , for resolve it according to the reference Creating an aggregate function , Using SQL DELIMITER // DROP FUNCTION IF EXISTS JSON_ARRAYAGG// CREATE AGGREGATE FUNCTION IF NOT EXISTS JSON_ARRAYAGG(next_value TEXT) RETURNS TEXT BEGIN DECLARE json TEXT DEFAULT '[""]'; DECLARE CONTINUE HANDLER FOR NOT FOUND RETURN json_remove(json, '$[0]'); LOOP FETCH GROUP NEXT ROW; SET json = json_array_append(json, '$', next_value); END LOOP; END // DELIMITER ;
What you want a fairly straightforward SELECT query with some LEFT/INNER JOIN(s). This website has some good examples/explanations which seem very close to your need: https://www.mysqltutorial.org/mysql-inner-join.aspx I would give you a quick working example, but it is not really clear to me what datatype the relevant columns are. Both tables' _id-columns are likely some variant of INTEGER, are they also both primary keys (or otherwise atleast indexed ?), the client_name/ticket_name are likely VARCHAR/TEXT/STRING types, but how exactly is the remaining column stored? as json or array or ? (+details) Also you tagged your post with PHP, are you just after the SQL query ? or looking for PHP code with the SQL inside it. updated Improved version of the schema CREATE TABLE clients ( client_id SERIAL, client_name VARCHAR(255) NOT NULL, PRIMARY KEY (client_id) ); CREATE TABLE tickets ( ticket_id SERIAL, ticket_name VARCHAR(255) NOT NULL, ticket_price DECIMAL(10,2) NOT NULL, PRIMARY KEY (ticket_id) ); -- A junction table to glue those 2 tables together (N to N relationship) CREATE TABLE client_tickets ( client_id BIGINT UNSIGNED NOT NULL, ticket_id BIGINT UNSIGNED NOT NULL, PRIMARY KEY (client_id, ticket_id) ); I have changed the datatypes. client_name and ticket_name are still VARCHARS. I've flagged them as NOT NULL (eg: required fields), but you can remove that part if you don't like that. client_id/ticket_id/ticket_price are also NOT NULL but changing that has negative side-effects. ticket_price is now a DECIMAL field, which can store numbers such as 1299.50 or 50.00 The (10,2) bit means it covers every possible number up to 8 whole digits (dollars/euros/whatever), and 2 decimals (cents). so you can store anything from $ -99.999.999,99 to $ 99.999.999,99 . in SQL always write numbers (like lets say 70k) in this notation: 70000.00 (eg: a dot, not a comma; and no thousandseperators). client_id and ticket_id are both SERIALs now, which is shorthand for BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE and theyre both PRIMARY KEYs on top of that. That probably sounds complicated but they're still just ordinary INTEGERs with values like 4 or 12 etc. The UNIQUE bit prevents you from having 2 clients with the same ID number, and the AUTO_INCREMENT means that when you add a new client, you dont have to specify an ID (though you are allowed to); you can just do: INSERT INTO clients (client_name) values ('Fantastic Mr Fox'); and the client_id will automatically be set (incrementing over time). And the same goes for ticket_id in the other table. . I've replaced your original client_tickets column, into a separate junction table. Records in there store the client_id of a client and the ticket_id that belongs to them. A client can have multiple records in the junction table (one record for each ticket they own). Likewise, a ticket can be mentioned on any number of rows. It's possible for a certain client_id to not have any records in the junction table. Likewise, it's possible for a certain ticket_id to not have any records in the junction table. Identical records cannot exist in this table (enforced by PRIMARY KEY). Testdata Next, we can put some data in there to be able to test it: -- Create some tickets INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (1, 'ticketone', '30' ); INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (2, 'tickettwo', '40' ); INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (3, 'ticketthree', '50' ); INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (4, 'ticketfour', '60' ); INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (5, 'ticketfive', '70' ); INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (6, 'ticketsix', '4' ); INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (7, 'ticketseven', '9' ); INSERT INTO tickets (ticket_id, ticket_name, ticket_price) values (8, 'ticketeight', '500' ); -- Create some users, and link them to some of these tickets INSERT INTO clients (client_id, client_name) values (1, 'John'); INSERT INTO client_tickets (client_id, ticket_id) values (1, 3); INSERT INTO client_tickets (client_id, ticket_id) values (1, 7); INSERT INTO client_tickets (client_id, ticket_id) values (1, 1); INSERT INTO clients (client_id, client_name) values (2, 'Peter'); INSERT INTO client_tickets (client_id, ticket_id) values (2, 5); INSERT INTO client_tickets (client_id, ticket_id) values (2, 2); INSERT INTO client_tickets (client_id, ticket_id) values (2, 3); INSERT INTO clients (client_id, client_name) values (3, 'Eddie'); INSERT INTO client_tickets (client_id, ticket_id) values (3, 8); INSERT INTO clients (client_id, client_name) values (9, 'Fred'); -- Note: ticket #3 is owned by both client #1/#2; -- Note: ticket #4 and #6 are unused; -- Note: client #9 (Fred) has no tickets; Queries Get all the existing relationships (ticket-less clients are left out & owner-less tickets are left out) SELECT clients.* , tickets.* FROM client_tickets AS ct INNER JOIN clients ON ct.client_id = clients.client_id INNER JOIN tickets ON ct.ticket_id = tickets.ticket_id ORDER BY clients.client_id ASC , tickets.ticket_id ASC ; Get all the tickets that are still free (owner-less) SELECT tickets.* FROM tickets WHERE tickets.ticket_id NOT IN ( SELECT ct.ticket_id FROM client_tickets AS ct ) ORDER BY tickets.ticket_id ASC ; Get a list of ALL clients (even ticketless ones), and include how many tickets each has and the total price of their tickets. SELECT clients.* , COALESCE(COUNT(tickets.ticket_id), 0) AS amount_of_tickets , COALESCE(SUM(tickets.ticket_price), 0.00) AS total_price FROM clients LEFT JOIN client_tickets AS ct ON ct.client_id = clients.client_id LEFT JOIN tickets ON ct.ticket_id = tickets.ticket_id GROUP BY clients.client_id ORDER BY clients.client_id ASC ; Put all the juicy info together (owner-less tickets are left out) SELECT clients.* , COALESCE(COUNT(sub.ticket_id), 0) AS amount_of_tickets , COALESCE(SUM(sub.ticket_price), 0.00) AS total_price , JSON_ARRAYAGG(sub.js_tickets_row) AS js_tickets_rows FROM clients LEFT JOIN client_tickets AS ct ON ct.client_id = clients.client_id LEFT JOIN ( SELECT tickets.* , JSON_OBJECT( 'ticket_id', tickets.ticket_id , 'ticket_name', tickets.ticket_name , 'ticket_price', tickets.ticket_price ) AS js_tickets_row FROM tickets ) AS sub ON ct.ticket_id = sub.ticket_id GROUP BY clients.client_id ORDER BY clients.client_id ASC ; -- sidenote: output column `js_tickets_rows` (a json array) may contain NULL values An list of all tickets with some aggregate data SELECT tickets.* , IF(COALESCE(COUNT(clients.client_id), 0) > 0 , TRUE, FALSE) AS active , COALESCE( COUNT(clients.client_id), 0) AS amount_of_clients , IF(COALESCE( COUNT(clients.client_id), 0) > 0 , GROUP_CONCAT(clients.client_name SEPARATOR ', ') , NULL) AS client_names FROM tickets LEFT JOIN client_tickets AS ct ON ct.ticket_id = tickets.ticket_id LEFT JOIN clients ON ct.client_id = clients.client_id GROUP BY tickets.ticket_id ORDER BY tickets.ticket_id ASC , clients.client_id ASC ;
MySQL query to get the total from three tables
Using MySQL, how do I get the total items and total revenue for each manager's team? Suppose I have these 3 different tables (parent-child-grandchild): Employee1 is under Supervisor1, and they are both under Manager1, and so on, but real data are in random arrangement. Color-coded the numbers to visualize which gets added. I want my query to output the total items and total revenue of each manager's team like: To easily create the table: DROP TABLE IF EXISTS manager; CREATE TABLE manager (id int, name varchar(55), no_of_items int, revenue int); INSERT INTO manager (id, name, no_of_items, revenue) VALUES (1 , 'Manager1' , 10 , 100), (2 , 'Manager2' , 20 , 200), (3 , 'Manager3' , 30 , 300); DROP TABLE IF EXISTS supervisor; CREATE TABLE supervisor (id int, name varchar(55), manager_id int, no_of_items int, revenue int); INSERT INTO supervisor (id, name, manager_id, no_of_items, revenue) VALUES (4 , 'Sup1' , 1, 100 , 1000), (5 , 'Sup2' , 2, 200 , 2000), (6 , 'Sup3' , 3, 300 , 3000); DROP TABLE IF EXISTS employee; CREATE TABLE employee (id int, name varchar(55), supervisor_id int, no_of_items int, revenue int); INSERT INTO employee (id, name, supervisor_id, no_of_items, revenue) VALUES (7 , 'Emp1' , 4, 400 , 4000), (8 , 'Emp2' , 5, 500 , 5000), (9 , 'Emp3' , 4, 600 , 6000);
SQL Fiddle Utilizing a combination of Nested Subqueries and UNION ALL, you can use the following: SELECT inner_nest.manager_id, inner_nest.name, SUM(inner_nest.total_items) AS total_items, SUM(inner_nest.total_revenue) AS total_revenue FROM ( SELECT id as manager_id, name, SUM(no_of_items) AS total_items, SUM(revenue) AS total_revenue FROM manager GROUP BY id UNION ALL SELECT m.id as manager_id, m.name, SUM(s.no_of_items) AS total_items, SUM(s.revenue) AS total_revenue FROM manager m INNER JOIN supervisor s ON s.manager_id = m.id GROUP BY m.id UNION ALL SELECT m.id as manager_id, m.name, SUM(e.no_of_items) AS total_items, SUM(e.revenue) AS total_revenue FROM manager m INNER JOIN supervisor s ON s.manager_id = m.id INNER JOIN employee e ON e.supervisor_id = s.id GROUP BY m.id ) AS inner_nest GROUP BY inner_nest.manager_id
Try this: http://sqlfiddle.com/#!9/7a0aef/20 select id,name,COALESCE(sum(distinct m),0)+COALESCE(sum(distinct s),0)+COALESCE(sum(e),0) as total_item,COALESCE(sum(distinct mv),0)+COALESCE(sum(distinct sv),0)+COALESCE(sum(ev),0) as total_revenue from ( select m.id,m.name,m.no_of_items as m,s.no_of_items as s,e.no_of_items as e, m.revenue as mv,s.revenue as sv,e.revenue as ev from manager m left join supervisor s on m.id=s.manager_id left join employee e on s.id=e.supervisor_id)a group by id,name
A single query with the appropriate joins does the trick... In pseudo code (sorry, I'm typing on my phone) Select a.manager_id, (a.total_items+b.total_items+c.total_items) as totalitems, (a.total_revenue+b.total_revenue+c.total_revenue) as totalrevenue From parent_table a Join child_table b on a.manager_id=b.manager.id Join grandchild_table c on b.sup_id=c.sup_id Group by a.manager_id Some adjustments may be needed, but this should definitely point you in your way
mysql max per group by date
I have two mysql greatest-n-per-group, greatest-by-date problems: Considering one students table and one grades table, I want to have all students displayed with their most recent grade. The schema script: CREATE TABLE student ( id int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; INSERT INTO student VALUES(1, 'jim'); INSERT INTO student VALUES(2, 'mark '); INSERT INTO student VALUES(3, 'john'); CREATE TABLE grades ( id int(11) NOT NULL AUTO_INCREMENT, student_id int(11) NOT NULL, grade int(11) NOT NULL, `date` date DEFAULT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; INSERT INTO grades VALUES(1, 1, 6, NULL); INSERT INTO grades VALUES(2, 1, 8, NULL); INSERT INTO grades VALUES(3, 1, 10, NULL); INSERT INTO grades VALUES(4, 2, 9, '2016-05-10'); INSERT INTO grades VALUES(5, 2, 8, NULL); INSERT INTO grades VALUES(6, 3, 6, '2016-05-26'); INSERT INTO grades VALUES(7, 3, 7, '2016-05-27'); A) I want to find out if this is a valid solution for getting the most recent record by a date field (date) from a secondary table (grades) grouped for each row in a main table (student). My query is: SELECT s.id, s.name, g.grade, g.date FROM student AS s LEFT JOIN ( SELECT student_id, grade, DATE FROM grades AS gr WHERE DATE = ( SELECT MAX(DATE) FROM grades WHERE student_id = gr.student_id ) GROUP BY student_id ) AS g ON s.id = g.student_id Sql Fiddle: http://sqlfiddle.com/#!9/a84171/2 This query displays the desired (almost) results. But I have doubts that this is the best approach because it looks ugly, so I am very curious about the alternatives. B) The second problem is the reason for the (almost) above, For the first row, name=Jim it finds no grade though we have grades for Jim. So just in case the query above would be valid only for NOT NULL date fields. The question would be: How to get the most recent grade for all the students who have grades, including Jim even that his grades has no date specified (NULL). In this case the most recent grouping will be given by the latest row inserted (MAX(id)) or just random. Doesn't work with replacing date = (SELECT... with date IN (SELECT .... Any help would be much appreciated, Thanks! [UPDATE #1]: For B) I found adding this to the sub-query, OR date IS NULL, produces the desired result: SELECT s.id, s.name, g.grade, g.date FROM student AS s LEFT JOIN ( SELECT id, student_id, grade, DATE FROM grades AS gr WHERE DATE = ( SELECT MAX(DATE) FROM grades WHERE student_id = gr.student_id ) OR date IS NULL GROUP BY student_id ) AS g ON s.id = g.student_id [UPDATE #2] Seems the previous update worked if the first grade has a date for a student. It doesn't if the first grade is null. I would have linked a fiddle but it seems sqlfiddle doesn't work now. So this is what I came up until now that seems to solve the B) problem: SELECT s.id, s.name, g.grade, g.date FROM student AS s LEFT JOIN ( SELECT id, student_id, grade, DATE FROM grades AS gr WHERE ( `date` = ( SELECT MAX(DATE) FROM grades WHERE student_id = gr.student_id ) ) OR ( ( SELECT MAX(DATE) FROM grades WHERE student_id = gr.student_id ) IS NULL AND date IS NULL ) ) AS g ON s.id = g.student_id GROUP BY student_id I still would like to know if you guys know better alternatives to this ugly thing. Thanks! [UPDATE #3] #Strawberry The desired results would be: id name grade date 1 jim 10 NULL 2 mark 9 2016-05-10 3 john 7 2016-05-27 each student with one corresponding grade if a date exists for a grade, then get the most recent one.
The complexity of this problem stems from the logical impossibility of a grade without an associated date, so obviously the solution is to fix that. But here's a workaround... E.g.: SELECT a.* FROM grades a JOIN ( SELECT student_id , MAX(COALESCE(UNIX_TIMESTAMP(date),id)) date FROM grades GROUP BY student_id ) b ON b.student_id = a.student_id AND b.date = COALESCE(UNIX_TIMESTAMP(a.date),id);
http://sqlfiddle.com/#!9/ecec43/4 SELECT s.id, s.name, g.grade, g.date FROM student AS s LEFT JOIN ( SELECT gr.student_id, gr.grade, gr.DATE FROM grades AS gr LEFT JOIN grades grm ON grm.student_id = gr.student_id AND grm.date>gr.date WHERE grm.student_id IS NULL AND gr.date IS NOT NULL GROUP BY gr.student_id ) AS g ON s.id = g.student_id;
Equations in SQL (MySQL)
I have a table with a cost_maintence column that has cost for the entire year(52) weeks. I also have a table of renters, and a table of renter_units where there is a week_owned column that has the week number the renter rented. I am trying to figure out how I could calculate the cost for each renter. The equation I came up with is: what each person owes = (cost_maintence/52) * #weeks each renter rented Is there any way I could get the value from a query? create table renters( id, lname, fname, phone_num); create table unit( id, unit_name, unit_num, cost_maintence); create table renters_unit( renters_id, unit_id, week_owned); This is the query I came up with but I have no way of testing it out select r.lname, r.fname, count(ru.week_owned), sum(u.cost_maintence/52*count(ru.week_owned)) from renters r, renters_units ru, units u where r.id = ru.renter_id and ru.unit_id = u.id and u.unit_name =unitname and u.unit_num = unitnum group by lname order by lname,fname asc;
Here's an example. The inner query will get you amount owed per item, and the outer query sums that to find the total owed per person. SELECT fname, SUM(owes) AS total_due FROM ( SELECT r.fname, r.id, u.unit_name, u.cost_maintence/52*COUNT(ru.week_owned) AS owes FROM renters AS r INNER JOIN renters_unit AS ru ON r.id = ru.renters_id INNER JOIN unit AS u ON u.id = ru.unit_id GROUP BY r.id, u.id ) AS t GROUP BY id Try it out with a SQLFiddle demo Example Schema: create table renters( id int, lname varchar(20), fname varchar(20), phone_num varchar(20)); create table unit( id int, unit_name varchar(30), unit_num int, cost_maintence int); create table renters_unit( renters_id int, unit_id int, week_owned int); INSERT INTO renters VALUES (1, 'Peterson', 'Chaz', '8675309'); INSERT INTO unit VALUES (1, 'Skateboard', 1337, 52); INSERT INTO unit VALUES (2, 'Flamethrower', 5432, 104); INSERT INTO renters_unit VALUES (1, 1, 1); INSERT INTO renters_unit VALUES (1, 1, 2); INSERT INTO renters_unit VALUES (1, 1, 4); INSERT INTO renters_unit VALUES (1, 2, 4); INSERT INTO renters_unit VALUES (1, 2, 5); By this, we can see that Chaz should owe $7 for the year (had a skateboard for 3 weeks at $1 per week, and a flamethrower for 2 weeks at $2 per week). The inner query gives the following: FNAME UNIT_NAME OWES Chaz Skateboard 3 Chaz Flamethrower 4 And the outer: FNAME TOTAL_DUE Chaz 7
SELECT t.renters_id, SUM(u.cost_maintence)/52 FROM unit u JOIN renters_unit t ON t.unit_id = u.id GROUP BY t.renters_id
Select columns other than the one specified in GROUP BY clause
Is there a way to select columns other the one specified in the group by clause? Let's say I have the following schema: Student(id, name, age), Course(id, name, credit), Enrollment(student_id, course_id, grade) I want to query for each course the following: course's name, student_count. I came up with workaround, but I was wondering if there's a cleaner way to do this: SELECT MAX(c.name), COUNT(distinct e.student_id) FROM Enrollment e INNER JOIN Course c ON c.id = e.course_id GROUP BY e.course_id;
You might want to copy this DDL, adjust it to match your schema, and paste it into your question. create table Student( student_id integer primary key, student_name varchar(35) not null, age int not null default 20 ); create table Course( course_id integer primary key, course_name varchar(35) not null, credit integer not null default 3 ); create table Enrollment( student_id integer not null references Student (student_id), course_id integer not null references Course (course_id), primary key (student_id, course_id), grade char(1) not null ); insert into student values (1, 'a', 20), (2, 'b', 20), (3, 'c', 20); insert into course values (1, 'course 1', 3), (2, 'course 2', 3), (3, 'course 3', 3); insert into enrollment values (1, 1, 'b'), (2, 1, 'b'), (3, 1, 'b'), (1, 2, 'b'), (2, 2, 'b'), (3, 3, 'b'); Now, you can get the number of students enrolled in each course by querying only the "enrollment" table. select course_id, count(student_id) num_students from enrollment group by course_id order by course_id; course_id num_students -- 1 3 2 2 3 1 All that remains is to get the corresponding course name. To do that, you just join the table "Course" with the query we just wrote. select course.course_name, course_enrollment.num_students from course inner join (select course_id, count(student_id) num_students from enrollment group by course_id) course_enrollment on course.course_id = course_enrollment.course_id; course_name num_students -- course 1 3 course 3 1 course 2 2
No, you can't. But you can extend GROUP BY with c.name: SELECT MAX(c.name), COUNT(distinct e.student_id) FROM Enrollment e INNER JOIN Course c ON c.id = e.course_id GROUP BY e.course_id, c.name Because e.course_id is unique, it won't change results.