I have two tables:
data
id[int] balance[float] category[id]
1 10.2 1
2 0.12 2
3 112.42 1
4 2.3 3
categories
id[int] name[varchar] start_at[float]
1 high 10.5
2 low 105.2
3 mid 0.7
I want to query the categories and join the data. For each categorie I want the sum of all data balances added to the start_at value of categories:
This is where I started with:
select sum(d.balance) as balancesum, c.name
from data d
left join categories c on c.id = d.category
group by d.category
What I want to know is, how can I add the start_at value of categories to the balancesum value?
SELECT c.name, c.start_at + SUM(d.balance) as balancesum
FROM categories c
JOIN data d ON c.id = d.category
GROUP BY c.name, c.start_at
You can use next approach:
select
c.name, balancesum, ifnull(balancesum, 0) + start_at
from categories c
left join (
-- calculate sum of balances per category
-- and join sums to data table
select category, sum(d.balance) as balancesum
from data d
group by d.category
) b on b.category = c.id;
Here you can play with live query
Related
I'm trying to sort through my table to find the frequent categories in my orders. After conducting this query
SELECT
ccd.cart_id,
mp.category_name,
ccd.quantity
FROM
customer_orders co
JOIN customer_cart_dtls ccd
ON co.order_cart = ccd.cart_id
JOIN merchant_products mp
ON ccd.product_id = mp.product_id
which yields this result
So from that query Cart #2006........63 has 9 items. 1 from eatables, 3 From fruits, 2 From cleaning, and 3 from Snacks. All of them quantity 1 except for the second entry of cleaning which has two. How can I alter my query so that I get 10 items all with quantity 1?
Which would look like this
You want to split the individual rows into multiple rows. One method uses recursive CTEs:
WITH RECURSIVE t as (
SELECT ccd.cart_id, mp.category_name, ccd.quantity
FROM customer_orders co JOIN
customer_cart_dtls ccd
ON co.order_cart = ccd.cart_id JOIN
merchant_products mp
ON ccd.product_id = mp.product_id
),
cte as (
SELECT cart_id, category_name, quantity, 1 as n
FROM t
UNION ALL
SELECT cart_id, category_name, quantity, n + 1
FROM cte
WHERE n < quantity
)
SELECT cart_id, category_name, 1 as quantity
FROM cte;
Here is a db<>fiddle.
EDIT:
You can join in a list of quantities -- easier if you have a tally table of some sort:
SELECT ccd.cart_id, mp.category_name, 1 as quantity
FROM customer_orders co JOIN
customer_cart_dtls ccd
ON co.order_cart = ccd.cart_id JOIN
merchant_products mp
ON ccd.product_id = mp.product_id JOIN
(SELECT 1 as n UNION ALL
SELECT 2 as n UNION ALL
SELECT 3 as n UNION ALL
SELECT 4 as n UNION ALL
SELECT 5 as n
) n
ON n.n <= ccd.quantity;
You can also construct the table using variables from an existing table (if it is big enough):
(select (#rn := #rn + 1) as n
from customer_orders cross join
(select #rn := 0) params
limit 100 -- say that 100 is big enough
) n
Are you trying to count how many items come from each category by splitting every item into an individual row and then using COUNT? If so, I don't think you necessarily need to go down that route. It will likely be a lot easier to simply use the SUM aggregate function after grouping by category_name. It might look something like this:
SELECT mp.category_name, SUM(ccd.quantity)
FROM customer_orders AS co
JOIN customer_card_dtls AS ccd ON co.order_cart = ccd.cart_id
JOIN merchant_products AS mp ON ccd.product_id = mp.product_id
GROUP BY mp.category_name
If you want to also see cart IDs then just add the appropriate columns to your SELECT and GROUP BY statements
My tables looks like this
sales
----------------------------------------------------------
id ordernumber quantity category_id price
1 402-9182243-8008368 1 3 22.95
2 406-3666671-8627555 2 3 6.95
3 303-1935495-5532309 1 1 7.95
4 171-5799800-1198702 1 2 159.95
5 403-2398078-4901169 2 2 18.95
category
--------------
id name
1 bikes
2 shoes
3 planes
returns
--------------
id ordernumber quantity costs
1 402-9182243-8008368 1 22.95
2 402-9182243-8008368 5.95 // return shipping fee
And here is my query
SELECT c.name,
SUM(v.quantity) AS sold, # wrong
SUM(s.quantity * s.price) AS turnover, # wrong
SUM(r.costs) AS returncosts,
FROM sales AS s
INNER JOIN categories AS c ON c.id = s.category_id
LEFT JOIN returns AS r ON r.ordernumber = s.ordernumber
GROUP BY c.name
I have some inner joins with aggregate functions.
But I also need "return" with a "Left Join" (I think).
And with Left Join, my aggregate functions dont work anymore.
Left Join adds additional rows. Additional data, for sum().
I need a single query, so every column is sortable later.
I would be happy about any help. Best Regards
It's a semi cartesian product because ordernumber is not unique in returns table.
We can see what's happening if we remove the aggregate functions and return the detail rows.
One possible approach is to pre-aggregate returns in an inline view, so that unique values of ordernumber are returned.
Assuming ordernumber is unique in sales table, then something like this:
SELECT c.name
, SUM(s.quantity) AS sold
, SUM(s.quantity * s.price) AS turnover
, SUM(r.returncosts) AS returncosts
FROM sales s
JOIN categories c
ON c.id = s.category_id
LEFT
JOIN ( SELECT t.ordernumber
, SUM(t.costs) AS returncosts
FROM returns t
GROUP
BY t.ordernumber
) r
ON r.ordernumber = s.ordernumber
GROUP
BY c.name
You can sum the quantity separately from the LEFT JOIN in a sub query as follows:
SELECT t1.name, t1.sold, t1.turnover, SUM(r.costs) AS returncosts
FROM(
SELECT c.name,
SUM(s.quantity) AS sold,
SUM(s.quantity * s.price) AS turnover
FROM sales AS s
INNER JOIN categories AS c ON c.id = s.category_id
GROUP BY name
) t1
LEFT JOIN returns AS r ON r.ordernumber = s.ordernumber
GROUP BY t1.name, t1.sold, t1.turnover
I have 3 tables :
I need to retrieve the name of the sellers that have sold during January AT LEAST one product where the total amount of sales of this said product during January is greater than 1000.
I thought about starting like this :
SELECT c.nom, c.prenom
FROM Commerciaux c
LEFT JOIN Ventes v ON c.id_commerciaux = v.id_commerciaux
WHERE EXISTS (SELECT 1
FROM Produits p, Ventes v
WHERE p.id_produits = v.id_produits
AND MONTH(v.date) = 1
GROUP BY p.nom
HAVING SUM(v.montant) > 200)
AND MONTH(v.date) = 1
GROUP BY c.nom, c.prenom
The SELECT in the WHERE EXISTS seems to be working, but when I have to get link the table Sellers, I don't know how to write it.
Any help would be great !
You can use something like this:
select distinct s.name
from Sellers s,
( -- get all those products that qualify (more than 1000 sales)
select product_id, sum(amount) as total
from Sales
where Month(date) = 1
group by product_id
having total > 1000
) vp,
Sales sa
where
s.id = sa.commercial_id and
sa.product_id = vp.id and
Month(sa.date) = 1
#Leo answer would return only those Sellers that have sold more than 100 of the product in January instead of returning all Sellers that have sold any amount of the product that has been sold in an amount greater than 1000 across the board.
You could use a nested query:
SELECT s.name FROM
(Sellers as s JOIN Sales as sp on sp.commercial_id = s.id) JOIN
Product as p on p.id = sp.product_id
WHERE MONTH(sp.date) = 1 AND YEAR(sp.date) = 2017 AND
(SELECT SUM(Amount) FROM Sales as sp2 WHERE sp2.commercial_id = s.id
AND sp2.product_id = p.id
AND MONTH(sp2.date) = 1 AND YEAR(sp2.date) = 2017) > 1000
First, join the three tables on foreign keys, and then run a nested query to compute amount of the selected seller on a specific product to check to pass the amount limitation.
try this
SELECT C.name
FROM Sales A
JOIN Product B ON A.produit_id = B.id
JOIN Seller C ON A.commercial_id = C.id
WHERE MONTH(A.date) = 1
HAVING SUM(A.nAmount) > 100
GROUP BY C.name
I'm trying to query
number of courses passed,
the earliest course passed
time taken to pass first course, for each student who is not currently expelled.
The tricky part here is 2). I constructed a sub-query by mapping the course table onto itself but restricting matches only to datepassed=min(datepassed). The query appears to work for a very sample, but when I try to apply it to my full data set (which would return ~1 million records) the query takes impossibly long to execute (left it for >2 hours and still wouldn't complete).
Is there a more efficient way to do this? Appreciate all your help!
Query:
SELECT
S.id,
COUNT(C.course) as course_count,
C2.course as first_course,
DATEDIFF(MIN(C.datepassed),S.dateenrolled) as days_to_first
FROM student S
LEFT JOIN course C
ON C.studentid = S.id
LEFT JOIN (SELECT * FROM course GROUP BY studentid HAVING datepassed IN (MIN(datepassed))) C2
ON C2.studentid = C.studentid
WHERE YEAR(S.dateenrolled)=2013
AND U.id NOT IN (SELECT id FROM expelled)
GROUP BY S.id
ORDER BY S.id
Student table
id status dateenrolled
1 graduated 1/1/2013
3 graduated 1/1/2013
Expelled table
id dateexpelled
2 5/1/2013
Course table
studentid course datepassed
1 courseA 5/1/2014
1 courseB 1/1/2014
1 courseC 2/1/2014
1 courseD 3/1/2014
3 courseA 1/1/2014
3 couseB 2/1/2014
3 courseC 3/1/2014
3 courseD 4/1/2014
3 courseE 5/1/2014
SELECT id, course_count, days_to_first, C2.course first_course
FROM (
SELECT S.id, COUNT(C.course) course_count,
DATEDIFF(MIN(datepassed),S.dateenrolled) as days_to_first,
MIN(datepassed) min_datepassed
FROM student S
LEFT JOIN course C ON C.studentid = S.id
WHERE S.dateenrolled BETWEEN '2013-01-01' AND '2013-12-31'
AND S.id NOT IN (SELECT id FROM expelled)
GROUP BY S.id
) t1 LEFT JOIN course C2
ON C2.studentid = t1.id
AND C2.datepassed = t1.min_datepassed
ORDER BY id
I would try something like:
SELECT s.id, f.course,
COALESCE( DATEDIFF( c.first_pass,s.dateenrolled), 0 ) AS days_to_pass,
COALESCE( c.num_courses, 0 ) AS courses
FROM student s
LEFT JOIN
( SELECT studentid, MIN(datepassed) AS first_pass, COUNT(*) AS num_courses
FROM course
GROUP BY studentid ) c
ON s.id = c.studentid
JOIN course f
ON c.studentid = f.studentid AND c.first_pass = f.datepassed
LEFT JOIN expelled e
ON s.id = e.id
WHERE s.dateenrolled BETWEEN '2013-01-01' AND '2013-12-31'
AND e.id IS NULL
This query assumes a student can pass only one course on a given day, otherwise you can get more than one row for a student as its possible to have many first courses.
For performance it would help to have an index on dateenrolled in student table and a composite index on (studentid,datepassed) in courses table.
I have six tables- Project,Equipment,Fish,Staff and the junction tables - Project_Equipment,Project_Fish and Project_Staff.I want to retrieve Project total cost.
So, I wrote the statement like follows,
SELECT P.ProjectID, (SUM(E.EquipPrice*PE.EQuantity)+SUM(F.FishPrice*PF.FQuantity)+SUM(PS.Salary)) as ProjectCost
FROM Equipment as E INNER JOIN Project_Equipment as PE
ON E.EquipID=PE.EquipID
INNER JOIN Project as P
ON PE.ProjectID=P.ProjectID
INNER JOIN Project_Fish as PF
ON P.ProjectID=PF.ProjectID
INNER JOIN Fish as F
ON PF.FishID=F.FishID
INNER JOIN Project_Staff as PS
ON P.ProjectID=PS.ProjectID
INNER JOIN Staff as S
PS.StaffID=S.StaffID
GROUP BY ProjectID
But, I got the price with twice of correct amount.
Your query will end up with a lot of duplicate results. Consider the following simpler case:
Table: Project
ProjectID EQuantity
1 1
2 1
Table: Equipment
EquipID EPrice
1 1
2 1
Table: Fish
FishID
1
2
Table: Project_Equipment
ProjectID EquipID
1 1
1 2
2 1
2 2
Table: Project_Fish
ProjectID FishID
1 1
1 2
2 1
2 2
Now, let's look at the results of a Project_Equipment query only:
SELECT p.projectid, e.eprice, pe.equantity FROM project p
INNER JOIN project_equipment pe ON pe.projectid=p.projectid
INNER JOIN equipment e ON e.equipid=pe.equipid
ProjectID EPrice EQuantity
1 1 1 // a
1 1 1 // b
2 1 1 // c
2 1 1 // d
That's as expected; a list of the price and quantity of each piece of equipment used by each project. But what do you think will happen when we INNER JOIN that to project_fish? That first result has project 1 in it twice, and project 2 in it twice, so we end up with every combination of that result and project_fish!
SELECT p.projectid, e.eprice, pe.equantity, f.fishid FROM project p
INNER JOIN project_equipment pe ON pe.projectid=p.projectid
INNER JOIN equipment e ON e.equipid=pe.equipid
INNER JOIN project_fish pf ON pf.projectid=p.projectid
ProjectID EPrice EQuantity FishID
1 1 1 1 // from a above
1 1 1 2 // from a above
1 1 1 1 // from b above
1 1 1 2 // from b above
2 1 1 1 // from c above
2 1 1 2 // from c above
2 1 1 1 // from d above
2 1 1 2 // from d above
This duplication will continue with every inner join. The amount your price will be off won't always be 2x, it will actually depend on the number of combinations of all your joins.
So, you can't really do what you are trying to do with this particular query. Instead you'll have to calculate the cost of each relationship separately. Then you sum all those together. You can do this by selecting each one separately and calculating the cost into a ProjectID and ProjectCost column, using UNION to concatenate those altogether, then once again grouping the results by ProjectID and summing the individual ProjectCost subtotals.
I explained that poorly, but think of it as subtotaling equipment, fish, and salary costs, then sticking all those subtotals into one table and summing that. E.g.:
SELECT x.projectid, SUM(x.ProjectCost) FROM
(
SELECT p.projectid, SUM(e.eprice * pe.equantity) ProjectCost FROM project p
INNER JOIN project_equipment pe ON pe.projectid=p.projectid
INNER JOIN equipment e ON e.equipid=pe.equipid
GROUP BY p.projectid
UNION
SELECT p.projectid, SUM(f.fprice * pf.fquantity) ProjectCost FROM project p
INNER JOIN project_fish pf ON pf.projectid=p.projectid
INNER JOIN fish f ON f.fishid=pf.fishid
GROUP BY p.projectid
UNION
SELECT p.projectid, SUM(s.salary) ProjectCost FROM project p
INNER JOIN project_staff ps ON ps.staffid=p.projectid
INNER JOIN staff s ON s.staffid=ps.staffid
GROUP BY p.projectid
) x
GROUP BY x.projectid
Each of the subqueries produces a projectid column and a ProjectCost column. Run the subquery (between the parens) by itself to see the results. The outer query then adds the subtotals for the projects.
Sorry, btw, I renamed your EquipPrice and FishPrice columns to EPrice and FPrice when I was testing.