I have next tables:
CREATE TABLE IF NOT EXISTS `Customers` (
`id` INT AUTO_INCREMENT,
`name` VARCHAR(20) NOT NULL,
PRIMARY KEY(`id`)
);
CREATE TABLE IF NOT EXISTS `Orders` (
`id` INT AUTO_INCREMENT,
`id_cust` INT NOT NULL,
`descr` VARCHAR(40),
`price` INT NOT NULL,
PRIMARY KEY(`id`),
FOREIGN KEY(`id_cust`) REFERENCES `Customers`(`id`)
);
One customer can have many orders. I want to get id_cust and sum of the orders of who paid the most(one person).
My query:
SELECT cust, max_orders_sum
FROM
(
(
SELECT MAX(orders_sum) AS max_orders_sum
FROM (
SELECT o.id_cust AS cust, SUM(o.price) AS orders_sum
FROM Orders AS o
GROUP BY o.id_cust
) AS same_query0
) AS step1
INNER JOIN
(
SELECT o.id_cust AS cust, SUM(o.price) AS orders_sum
FROM Orders AS o
GROUP BY o.id_cust
) AS same_query1
ON (step1.max_orders_sum = same_query1.orders_sum)
);
Main problem:
as you can see, it has the same parts: same_query0 and same_query1. Is there any way to get rid of them?
Or if you know the better way to reach my goal, please share.
I found one simple solution:
SELECT o.id_cust AS cust, SUM(o.price) AS orders_sum
FROM Orders AS o
GROUP BY o.id_cust
ORDER BY orders_sum DESC LIMIT 1;
But this is not a direct way to solve the problem.
I don't think you can do much better than what you've already done, unless you create a view like
create view v_cust_tot as
select id_cust, sum(price) as cust_tot
from Orders
group by id_cust
With that you'd be able to rewrite your query like this
select id_cust, cust_tot
from v_cust_tot
where cust_tot = (select max(cust_tot) from v_cust_tot)
This would be an improvement just in the compactness of the query, because I think performances would be the same as the execution plan would be almost identical
Another one nice solution:
select id_cust, sum(price) from orders group by id_cust having sum(price) =
(select max(prc) from
(select sum(price) as prc from orders group by id_cust) as tb);
Related
I have two tables in my database :
select * from marks;
select * from subjects;
I need to find the id of the students who got the highest marks in each subject along with the subject name, i.e., Resultset should have 3 columns:
student_id
subject_name
maximum_marks
1
PHYSICS
97.5
2
CHEMSITRY
98.5
Please help me write the query for the above result set
This is what I've tried so far
select m.student_id, s.subject_name, max(m.marks) as maximum_marks from
marks m inner join subjects s
on m.subject_id=s.subject_id
group by m.subject_id;
OUTPUT:
SQL Fiddle Demo
select m.student_id, s.subject_name, m.max_marks
from subjects s join (
select student_id,subject_id, max(marks) as max_marks
from marks
group by student_id,subject_id
order by 3 desc
) as m
on s.subject_id = m.subject_id
group by s.subject_id
Schema & sample & ONLY_FULL_GROUP_BY disabled
CREATE TABLE IF NOT EXISTS `marks` (
`student_id` int(6) NOT NULL,
`subject_id` int(6) NOT NULL,
`marks` float NOT NULL
) DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `subjects` (
`subject_id` int(6) NOT NULL,
`subject_name` varchar(10) NOT NULL
) DEFAULT CHARSET=utf8;
INSERT INTO `marks` (`student_id`, `subject_id`, `marks`) VALUES
(1,1,97.5),(1,2,92.5),
(2,1,90.5),(2,2,98.5),
(3,1,90.5),(3,2,67.5),
(4,1,80.5),(4,2,97.5);
INSERT INTO `subjects` (`subject_id`, `subject_name`) VALUES
(2,"Chemistry"),(1,"Physics");
I've found a little bit better solution, this is a common use-case of correlated sub-queries, the output can be achieved without a group-by.
select m1.student_id, m1.subject_id, m1.marks, s.subject_name
from marks m1 inner join subjects s
on m1.subject_id=s.subject_id
where m1.marks=
(select max(marks) from marks m2 where m1.subject_id=m2.subject_id);
We're trying to calculate the rank of contestants for a specific contest, using the following select query. The GROUP_CONCAT workaround is actually a solution that was offered here on SO for a similar question.
However, as we added more conditions the query got long, untidy and is not DRY, I think as we have to repeat the same conditions for the GROUP_CONCAT subquery.
How can it be optimized? Or would a solution like programmatically calculate ranks and check conditions then populate the database, be the best solution in this case?
SELECT *,
-- Get the rank of contestants
FIND_IN_SET(`id`, (
SELECT GROUP_CONCAT(`id` ORDER BY `points` DESC, `created`)
FROM `contestants` `c2`
-- The following query is exactly the same as the on in the main query bellow.
WHERE `contest_id`=:contest_id
AND EXISTS (
SELECT `user_id`
FROM `item`
WHERE `user_id`=`c2`.`user_id`
AND `product_id` IN (
SELECT `id`
FROM `product`
WHERE `price`<=:max_price
AND `available`=:available
)
)
AND `state`=:state
-- ---------------------------------------------------------------------------
)
) AS `rank`
FROM `contestants`
-- ---------------------------------------------------------------------------
WHERE `contest_id`=:contest_id
AND EXISTS (
SELECT `user_id`
FROM `item`
WHERE `user_id`=`c2`.`user_id`
AND `product_id` IN (
SELECT `id`
FROM `product`
WHERE `price`<=:max_price
AND `available`=:available
)
)
AND `state`=:state
-- ---------------------------------------------------------------------------
ORDER BY `rank` ASC
LIMIT 10
I have created two temporary tables:
create temporary table temp.PromoCust (
CustId int(10)
);
create temporary table temp.Inv (
CustId int(10),
InvId int(10)
);
Insert some data:
insert into temp.PromoCust
select pl.CustomerId
from promoline pl
where pl.PromoId = 3008;
select * from temp.PromoCust; -- count is 234.995 customers
insert into temp.Inv
select i.CustomerId, i.InvoiceId
from invoice i
where i.InvoiceDate between '2016-12-21' and '2017-01-03';
select * from temp.Inv; -- count is 86.684 customers and invoices
And finally I run a left join query:
select pc.CustId, i.InvId
from temp.PromoCust pc left join temp.Inv i
on pc.CustId = i.CustId;
For some reason this takes forever. Is it something wrong with the query? I cannot really get any results from this.
I need help with a MySQL query. I have three tables:
`product_category` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
);
`order_products` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(255) NOT NULL,
`qty` int(11) NOT NULL,
`unit_price` decimal(11,2) NOT NULL,
`category` int(11) NOT NULL,
`order_id` int (11) NOT NULL,
PRIMARY KEY (`id`)
);
`orders` (
`id` int(11) NOT NULL auto_increment,
`date` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
);
I had a query that calculated subtotal of all orders by product category (in a certain date range). It looked like this:
SELECT
SUM(op.unit_price * op.qty) as amt,
c.name as category_name
FROM
order_products op,
product_category c,
orders o
WHERE
op.category = c.id
AND
op.order_id = o.id
AND
o.date > 'xxxxxxx'
GROUP BY
c.id
This works great, but now I want to add the individual products and their subtotals to each row so that I get a result like this:
c.name|amt|op.name (1) - op.subtotal (1), op.name (2), op.subtotal (2), etc....
I figured out using GROUP_CONCAT that I could get the names to show up pretty easily by adding:
GROUP_CONCAT(op.name) as product_name
to the SELECT clause, but for the life of me I can't figure out how to get the subtotal for each product to show up next to the product name. I have a feeling it involves a combination of joins or CONCAT nested inside GROUP_CONCAT, but nothing I've tried has worked. Any ideas?
#piotrm had an idea that seemed like it should work (below) but for some reason it returns the following:
SELECT `subtotals`
FROM `product_category`
WHERE `c`.`category_name` = 'Fragrance'AND.`amt` = '23164.50'AND.`subtotals` = CAST( 0x6c6f76656c696c7920454454202d203631302e30302c20466f72657665726c696c79202e313235206f7a2045445020726f6c6c657262616c6c202d20313831372e35302c20666f72657665726c696c79206361636865706f74202d2039302e30302c20666f72657665726c696c7920312f38206f756e63652070617266756d206f696c20726f6c6c657262616c6c202d20313833302e30302c20666f72657665726c696c792070657266756d696e6720626f6479206c6f74696f6e202d203938312e30302c20666f72657665726c696c7920332e34206f756e6365206561752064652070617266756d207370726179202009202d203535382e30302c20666f72657665726c696c79205363656e74656420566f746976652043616e646c65202d203132302e30302c20454450202620426f6f6b20736574202d203334332e30302c20666f72657665726c696c7920332e34206f756e6365206561752064652070617266756d207370726179202d2031363831352e3030 AS
BINARY ) ;
As soon as I take out s.subtotal from the original SELECT clause it pulls the correct product names. The JOIN query pulls the products out correctly with their associated category_id and subtotals. I just can't get the two to CONCAT together without creating this mess here. Any other thoughts?
Solution
#piotrm's query was basically right, except GROUP_CONCAT is looking for a collection of strings. So the final query looked like this:
SELECT c.name AS category_name,
SUM( s.subtotal ) AS amt,
GROUP_CONCAT( CONCAT(s.name, ' - ', cast(s.subtotal as char) ) SEPARATOR ', ') AS subtotals
FROM
product_category c
JOIN
(SELECT op.category, op.name, sum(op.qty*op.unit_price) AS subtotal
FROM order_products op
JOIN orders o ON o.id = op.order_id
WHERE o.date > '0'
GROUP BY op.category, op.name ) s
ON s.category = c.id
GROUP BY c.name
Guessing from your query there is also order_id field in your order_products table you didn't mention in the table definition. Your query should then look like:
SELECT c.name AS category_name,
SUM( s.subtotal ) AS amt,
GROUP_CONCAT( CONCAT(s.name, ' - ', s.subtotal ) SEPARATOR ', ' ) AS subtotals
FROM
product_category c
JOIN
( SELECT op.category, op.name, sum(op.qty*op.unit_price) AS subtotal
FROM order_products op
JOIN orders o ON o.id = op.order_id
WHERE o.date > '2012-03-31'
GROUP BY op.category, op.name ) s
ON s.category = c.id
GROUP BY c.name
Your db schema is quite weird though, orders table looks like it could be removed and that date moved to order_products, because for every order_products row you have reference to orders table. Usually it is the other way - there are many orders for every product referenced by product_id field in the orders table. Also date column in orders is of type varchar - why not date or datetime?
Try:
SELECT
SUM(op.unit_price * op.qty) as amt,
c.name as category_name,
group_concat(concat(op.name, '-', op.qty, ',' ,
op.unit_price*op.qty) separator '|')
...
Having some fun with MySQL by asking it difficult questions.
Essentially i have a table full of transactions, and from that i want to determine out of all the available products (productid), who (userid) has bought the most of each? The type in the where clause refers to transaction type, 1 being a purchase.
I have a subquery that on its own returns a list of the summed products bought for each person, and it works well by itself. From this i am trying to then pick the max of the summed quantities and group by product, which is a pretty straight forward aggregate. Unfortunately it's giving me funny results! The userid does not correspond correctly to the reported max productid sales.
select
`userid`, `productid`, max(`sumqty`)
from
(select
`userid`, `productid`, sum(`qty`) as `sumqty`
from
`txarchive`
where
`type` = 1
group by `userid`,`productid`) as `t1`
group by `productid`
I have removed all the inner joins to give more verbal results as they don't change the logic of it all.
Here is the structure of tx if you are interested.
id bigint(20) #transaction id
UserID bigint(20) #user id, links to another table.
ProductID bigint(20) #product id, links to another table.
DTG datetime #date and time of transaction
Price decimal(19,4) #price per unit for this transaction
QTY int(11) #QTY of products for this transaction
Type int(11) #transaction type, from purchase to payment etc.
info bigint(20) #information string id, links to another table.
*edit
Working final query: (Its biggish)
select
`username`, `productname`, max(`sumqty`)
from
(select
concat(`users`.`firstname`, ' ', `users`.`lastname`) as `username`,
`products`.`name` as `productname`,
sum(`txarchive`.`qty`) as `sumqty`
from
`txarchive`
inner join `users` ON `txarchive`.`userid` = `users`.`id`
inner join `products` ON `txarchive`.`productid` = `products`.`id`
where
`type` = 1
group by `productname`,`username`
order by `productname`,`sumqty` DESC) as `t1`
group by `productname`
order by `sumqty` desc
Not the best solution (not even guaranteed to work 100% of the times):
select
`userid`, `productid`, max(`sumqty`)
from
( select
`userid`, `productid`, sum(`qty`) as `sumqty`
from
`txarchive`
where
`type` = 1
group by
`productid`
, `userid`
order by
`productid`
, `sumqty` DESC
) as `t1`
group by
`productid`