I want to get the sum of group A and B separately, and divide each by the total sum.
I tried to use this:
select name, sum(qt)
from ntbl
group by name
order_id
name
qt
1
A
12
2
A
20
3
B
33
4
B
45
Result should be as:
name
qt
dv
A
32
0.29
B
78
0.70
You can combine aggregate and window functions together:
select name
, sum(qt) as sum_qt
, sum(qt) / sum(sum(qt)) over () * 100 as pct_qt
from t
group by name
You can crossjoin another subquery that sums up all quantities
CREATE TABLE ntbl (
`order_id` INTEGER,
`name` VARCHAR(1),
`qt` INTEGER
);
INSERT INTO ntbl
(`order_id`, `name`, `qt`)
VALUES
('1', 'A', '12'),
('2', 'A', '20'),
('3', 'B', '33'),
('4', 'B', '45');
SELECT name, sum_name, ROUND(sum_name/sum_qt,2) as dv
FROM
(select name,SUM(qt) sum_name from ntbl group by name) q1 CROSS JOIN (SELECT SUM(`qt`) sum_qt FROM ntbl) q2
name | sum_name | dv
:--- | -------: | ---:
A | 32 | 0.29
B | 78 | 0.71
db<>fiddle here
Related
I'm trying to calculate the days since the last different order so for example let's say I have the following table:
cust_id|Product_id|Order_date|
1 |a |10/02/2020|
2 |b |10/01/2020|
3 |c |09/07/2020|
4 |d |09/02/2020|
1 |a |08/29/2020|
1 |f |08/02/2020|
2 |g |07/01/2020|
3 |t |06/06/2020|
4 |j |05/08/2020|
1 |w |04/20/2020|
I want to find the difference between the most recent date and the previous date that has a product ID that doesn't match the most recent product ID.
So the output should be something like this:
cust_id|latest_Product_id|time_since_last_diff_order_days|
1 |a |30 |
2 |b |92 |
3 |c |91 |
4 |d |123 |
Here's the query that I tried to use but got an error (error code 1064)
SELECT a.cust_id, a.Product_ID as latest_Product_id, DATEDIFF(MAX(a.Order_date),MAX(b.Order_date)) as time_since_last_diff_order_days
FROM database_customers.cust_orders a
INNER JOIN
database_customers.cust_orders b
on
a.cust_id = b.cust_id
WHERE a.product_id =! b.prodcut_id;
Thank you for any help!
It isn't pretty,, but will do the job
CREATE TABLE tab1
(`cust_id` int, `Product_id` varchar(1), `Order_date` datetime)
;
INSERT INTO tab1
(`cust_id`, `Product_id`, `Order_date`)
VALUES
(1, 'a', '2020-10-02 02:00:00'),
(2, 'b', '2020-10-01 02:00:00'),
(3, 'c', '2020-09-07 02:00:00'),
(4, 'd', '2020-09-02 02:00:00'),
(1, 'a', '2020-08-29 02:00:00'),
(1, 'f', '2020-08-02 02:00:00'),
(2, 'g', '2020-07-01 02:00:00'),
(3, 't', '2020-06-06 02:00:00'),
(4, 'j', '2020-05-08 02:00:00'),
(1, 'w', '2020-04-20 02:00:00')
;
WITH CTE AS (SELECT `cust_id`, `Product_id`,`Order_date`,ROW_NUMBER() OVER(PARTITION BY `cust_id` ORDER BY `Order_date` DESC) rn
FROM tab1)
SELECT t1.`cust_id`, t1.`Product_id`, t2.time_since_last_diff_order_days
FROM
(SELECT
`cust_id`, `Product_id`
FROM
CTE
WHERE rn = 1 ) t1
JOIN
( SELECT `cust_id`,DATEDIFF(MAX(`Order_date`), MIN(`Order_date`)) time_since_last_diff_order_days
FROM CTE WHERE rn in (1,2) GROUP BY `cust_id`) t2 ON t1.cust_id = t2.cust_id
cust_id | Product_id | time_since_last_diff_order_days
------: | :--------- | ------------------------------:
1 | a | 34
2 | b | 92
3 | c | 93
4 | d | 117
db<>fiddle here
I want to find the difference between the most recent date and the previous date that has a product ID that doesn't match the most recent product ID.
You can use first_value() to get the last product and then aggregate:
select cust_id, last_product_id, max(order_date),
datediff(max(order_date), max(case when product_id <> last_product_id then order_date end)) as diff_from_last_product
from (select co.*,
first_value(product_id) over (partition by cust_id order by order_date) as last_product_id
from cust_orders co
) co
group by cust_id, last_product_id;
Using MySQL 8.0:
Here's my code:
WITH table1 AS (
SELECT MAX(CASE
WHEN a.A IS NULL THEN 4
WHEN b.code IS NULL THEN 3
ELSE a.A
END
) AS CODE,
CASE
WHEN a.A IS NULL THEN 'BLANK'
WHEN b.code IS NULL THEN 'NOT VALID'
ELSE b.description
END AS SEX,
COUNT(*) AS TOTAL ,
ROUND(100.0 * COUNT(*) / SUM(COUNT(*)) OVER (), 2) AS PERCENT
FROM AA a LEFT JOIN BB b ON b.code = a.A where a.type = 1
GROUP BY SEX
)
SELECT CODE, SEX, TOTAL, PERCENT,
SUM(TOTAL) OVER (ORDER BY CODE) AS CUMULATIVE,
ROUND(100.0 * SUM(TOTAL) OVER (ORDER BY CODE) / SUM(TOTAL) OVER (), 2) AS CUMPERCENT
FROM table1
UNION ALL
SELECT 0, 'TOTAL', COUNT(*), 100.00, 0, 0.00
FROM AA
ORDER BY CODE;
CODE | SEX | TOTAL | PERCENT | CUMULATIVE | CUMPERCENT
0 TOTAL 11 100.00 0 0.00
1 Male 2 33.33 2 33.33
2 Female 2 33.33 4 66.67
3 NOT VALID 1 16.67 5 83.33
4 BLANK 1 16.67 6 100.00
Desired output:
CODE | SEX | TOTAL | PERCENT | CUMULATIVE | CUMPERCENT
0 TOTAL 6 100.00 0 0.00
1 Male 2 33.33 2 33.33
2 Female 2 33.33 4 66.67
3 NOT VALID 1 16.67 5 83.33
4 BLANK 1 16.67 6 100.00
The problem is The total should be 6. How will I filter items with type = 1 only in table AA?
Here's my sample database:
CREATE TABLE AA (
A int,
type int
);
insert into AA (A,type) values (1,1);
insert into AA (A,type) values (1,1);
insert into AA (A,type) values (2,1);
insert into AA (A,type) values (2,1);
insert into AA (A,type) values (3,1);
insert into AA (A,type) values (NULL,1);
insert into AA (A,type) values (1,2);
insert into AA (A,type) values (1,2);
insert into AA (A,type) values (1,2);
insert into AA (A,type) values (2,2);
insert into AA (A,type) values (2,2);
CREATE TABLE BB (
code int,
description varchar(30)
);
insert into BB (code, description) values (1, 'Male');
insert into BB (code, description) values (2, 'Female');
THANK YOU.
Since you want to add a "total"-row depending on the content of your cte, you can just reuse it:
UNION ALL
SELECT 0, 'TOTAL', SUM(TOTAL), 100.00, 0, 0.00
FROM table1
ORDER BY CODE;
sums the totals of the rows of table1 (which are the rows with code 1 to 4 and thus the rows you are showing).
If you, as stated, actually want to filter AA directly, to get and count the same rows as in your cte, you have to redo the filter you did in your cte:
UNION ALL
SELECT 0, 'TOTAL', COUNT(*), 100.00, 0, 0.00
FROM AA a LEFT JOIN BB b ON b.code = a.A where a.type = 1
ORDER BY CODE;
This requires MySQL to evaluate this a second time though, so you may not actually want to do that.
I have transaction data like this example:
Name | Price
George | 20
George | 20
George | 20
George | 30
Paul | 20
Paul | 20
Paul | 30
Paul | 30
Paul | 35
I need to group by user and sort by the number of transactions in general, but within that group of users also make groups by price ordering by the amount of transactions at that price.
I need this result:
Name | Price | Count
Paul | 20 | 2
Paul | 30 | 2
Paul | 35 | 1
George | 20 | 3
George | 30 | 1
UPDATE
It is in a MySQL 5.5 database. I need to make the query for fluent in Laravel but having it in SQL is a great advance.
Thanks in advance.
SELECT t1.*
FROM ( SELECT name,
price,
COUNT(*) cnt
FROM srctable
GROUP BY name, price ) t1
JOIN ( SELECT name,
COUNT(*) tcnt
FROM srctable
GROUP BY name ) t2 USING (name)
ORDER BY tcnt DESC,
cnt DESC;
fiddle
Here you go. It can be done in MySQL 8.x. The double ordering you want requires the use of a couple of table expressions, as shown below:
select
x.name, x.price, x.cnt
from (
select name, price, count(*) as cnt
from t
group by name, price
) x
join (
select name, row_number() over(order by cnt desc) as ob
from (
select name, count(*) as cnt
from t
group by name
) y
) z on x.name = z.name
order by z.ob, x.cnt desc
Result:
name price cnt
------ ----- ---
Paul 20 2
Paul 30 2
Paul 35 1
George 20 3
George 30 1
For reference, the data script I used is:
create table t (
name varchar(10),
price int
);
insert into t (name, price) values
('George', 20),
('George', 20),
('George', 20),
('George', 30),
('Paul', 20),
('Paul', 20),
('Paul', 30),
('Paul', 30),
('Paul', 35);
It seems like I can either count sales and events ("COUNT(DISTINCT event) as SaleCount" in the event-sale join temp table) in a waiter-month or count the number of event attendees served in a waiter-month (in the event table), but not both in the same SQL statement unless I use iteration (which I am trying to avoid as I am scared of the loop overhead)
Events table
CREATE TABLE events (
`event` INTEGER,
`month` VARCHAR(3),
`attendees` INTEGER,
`waiter` VARCHAR(3)
);
INSERT INTO
events
(`event`, `month`, `attendees`, `waiter`) VALUES
('1', 'jan', '5', 'bob'),
('2', 'feb', '2', 'bob'),
('3', 'feb', '1', 'bob'),
('4', 'feb', '2', 'sally');
Sales table
CREATE TABLE sales (
`sale` INTEGER,
`event` INTEGER,
`profit` INTEGER
);
INSERT INTO
sales
(`sale`, `event`, `profit`) VALUES
('1', '1', '10'),
('2', '1', '20'),
('3', '1', '15'),
('4', '2', '25'),
('5', '2', '5'),
('6', '2', '30'),
('7', '3', '25'),
('8', '3', '5'),
('9', '3', '30'),
('10', '4', '25'),
('11', '4', '5'),
('12', '4', '30');
Event-Sales join temp table
sale event month attendees profit ($) waiter
1 1 jan 5 10 bob
2 1 jan 5 20 bob
3 1 jan 5 15 bob
4 2 feb 2 25 bob
5 2 feb 2 5 bob
6 2 feb 2 30 bob
7 3 feb 1 25 bob
8 3 feb 1 5 bob
9 3 feb 1 30 bob
10 4 feb 2 25 sally
11 4 feb 2 5 sally
12 4 feb 2 30 sally
Required output
Productivity for month
waiter-month attendees events sales
bob-jan 5 1 3
bob-feb 3 2 6
sally-feb 2 1 3
Edit:
Above I only now added "waiter", "waiter-month" and replaced sale count with event count as i forgot this is in fact the requirement
It's also possible to join 2 sub-queries.
select
concat(qe.waiter,'-',qe.month) as `waiter-month`
, qe.attendees
, qe.events
, qs.sales
from
(
select e.waiter, e.month
, month(str_to_date(e.month,'%b')) as monthNr
, sum(attendees) as attendees
, count(*) as events
from events e
group by e.month, e.waiter
) qe
left join
(
select e.month, e.waiter
, sum(s.profit) as profits
, count(s.sale) as sales
from events e
join sales s
on s.event = e.event
group by e.month, e.waiter
) qs
on qs.month = qe.month
and qs.waiter = qe.waiter
order by qe.waiter, qe.monthNr
waiter-month | attendees | events | sales
:----------- | --------: | -----: | ----:
bob-jan | 5 | 1 | 3
bob-feb | 3 | 2 | 6
sally-feb | 2 | 1 | 3
db<>fiddle here
Unless I'm missing something, this seems like a straightforward GROUP BY using COUNT(DISTINCT) and SUM():
SELECT
e.event as EventId,
COUNT(DISTINCT s.sales) as SaleCount,
SUM(e.attendees) as TotalAttendees
FROM event e
JOIN sales s
ON e.event = s.event
GROUP BY e.event
You can group by event, month:
select
s.event, e.month,
max(e.attendees) attendees,
count(s.profit) sales
from sales s inner join events e
on e.event = s.event
group by s.event, e.month
See the demo.
Results:
> event | month | attendees | sales
> ----: | :---- | --------: | ----:
> 1 | jan | 5 | 3
> 2 | feb | 2 | 3
Or group by month only:
select
e.month,
max(e.attendees) attendees,
count(s.profit) sales
from sales s inner join events e
on e.event = s.event
group by e.month
I have this table
id | qty_from | qty_to | p_id | price
--------------------------------------
1 | 1 | 10 | 4 | 1000
--------------------------------------
2 | 11 | 20 | 4 | 2000
--------------------------------------
3 | 1 | 10 | 5 | 500
--------------------------------------
4 | 11 | 20 | 5 | 1000
--------------------------------------
5 | 1 | 10 | 6 | 1000
--------------------------------------
6 | 10 | 15 | 6 | 2000
And i tried to get rows by qty_from AND qty_to AND p_id using below code
SELECT * FROM table WHERE p_id IN ('4', '5', '6') AND qty_from <= 8 AND qty_to >= 8
It returns 1st, 3rd and 5th rows.
And when i use this code, it return only 1st and 3rd rows.
SELECT * FROM table WHERE p_id IN ('4', '5', '6') AND qty_from <= 16 AND qty_to >= 16
I want to return 6th row too. because it's the biggest qty in p_id = 6
How can i achieve this?
After discuss here is what you need (first and not tested solution) :
SELECT *
FROM (SELECT *, MAX(qty_to) as max_qty_to FROM `limit`) T
WHERE p_id IN ('4', '5', '6')
AND ( ( qty_from <= 16 AND qty_to >= 16 ) OR qty_to = T.max_qty_to )
You should update your where condition to achieve your desired result. The updated query will be -
SELECT * FROM table WHERE p_id IN ('4', '5', '6') AND qty_from <= 10 AND qty_to >= 8
I just solve this.
Thanks to #Meloman for giving me the clue.
SELECT * FROM table WHERE p_id IN ('4', '5', '6') AND ((qty_from <= 16 AND qty_to >= 16) OR (qty_to, p_id) IN (SELECT dd.qty_to, dd.p_id FROM table dd INNER JOIN (SELECT MAX(qty_to) AS mm, p_id FROM table GROUP BY p_id) ddd ON dd.p_id = ddd.p_id AND dd.qty_to = ddd.mm WHERE dd.p_id IN ('4', '5', '6'))) GROUP BY p_id
I test this multiple times and i think it's the answer
This solution works:
SELECT DISTINCT t3.*
FROM `table` t1
LEFT JOIN `table` t2 ON t2.id=IFNULL(
(SELECT id FROM `table` t3
WHERE t3.p_id=t1.p_id AND 16 BETWEEN t3.qty_from AND t3.qty_to
ORDER BY qty_to DESC LIMIT 1),
(SELECT id FROM `table` t4
WHERE t4.p_id=t1.p_id
ORDER BY qty_to DESC LIMIT 1))
WHERE t1.p_id IN ('4', '5', '6')
Basically, first a table with the given p_ids is fetched and then joined with the rows of the most desired ids per p_id (if 16 is not in range of qty_from and qty_to, the one with the biggest qty_to is taken).
One flaw: If multiple rows match the range condition, only the one with the biggest qty_to is selected, so there is only one result per p_id. I hope that's sufficient for you!