MySQL User Defined Variable for every spesific ID - mysql

i have create a table and insert some dummy data with this query:
CREATE TABLE transaction (
idtransaction INT NOT NULL AUTO_INCREMENT,
idproduct INT NOT NULL,
quantity INT NOT NULL,
PRIMARY KEY (idtransaction));
INSERT INTO transaction (idproduct, quantity) VALUES
(1, 100),
(1, 200),
(2, 120),
(3, 300),
(2, 200),
(2, 200),
(1, 300);
and i want to get a table result like this:
|idtransaction| idproduct | quantity | saldo
1 1 100 100
2 1 100 200
3 2 120 120
4 3 300 300
5 2 200 320
6 2 200 520
7 1 300 500
i have create a query like this:
SELECT idtransaction,
idproduct,
quantity,
(#saldo := #saldo + quantity) as saldo
FROM transaction
LEFT JOIN
(select #saldo :=0) as s on 1=1;
but, the result of saldo history is calculate for all idproduct.
how to get 'saldo' history for every idproduct?

You need to order the data by idproduct, and then reset #saldo whenever idproduct changes.
If you want the final result ordered by idtransaction, put the query with the running total in a sub-query, and apply the new order to that.
SELECT *
FROM (
SELECT idtransaction, idproduct, quantity,
#saldo := IF(idproduct = #lastproduct, #saldo + quantity, quantity) AS saldo,
#lastproduct := idproduct
FROM (SELECT *
FROM transaction
ORDER BY idproduct, idtransaction) AS t
CROSS JOIN (SELECT #saldo := 0, #lastproduct = NULL) AS vars) AS x
ORDER BY idtransaction
DEMO

Related

how to get current streak and longest streak of all users in mysql

I am facing one issue for fetching the current streak of all users and longest streak of all users. I have below table user_bible_trackings
id user_id date_read
1 1 2021-08-21
2 1 2021-08-22
3 1 2021-08-23
4 1 2021-08-26
5 1 2021-08-27 // current_streak 2 and longest streak is 3
6 3 2021-08-21
7 3 2021-08-24
8 3 2021-08-25
9 3 2021-08-26
10 3 2021-08-26 // current_streak 3 and longest streak is 3
My expected output is below :-
user_id current_streak longest_streak
1 2 3
3 3 3
i am able to fetch current streak data for particular user but i want data of all users. i have tried below:-
SELECT *
FROM (
SELECT t.*, IF(#prev + INTERVAL 1 DAY = t.d, #c := #c + 1, #c := 1) AS streak, #prev := t.d as streak_date
FROM (
SELECT date_read AS d, COUNT(*) AS n
FROM user_bible_trackings
where user_id = 1
group by date_read
) AS t
INNER JOIN (SELECT #prev := NULL, #c := 1) AS vars
) AS t
ORDER BY streak_date DESC LIMIT 1
#Akina Thank you for your query i have updated my question. Now see above my user_bible_trackings table. For user_id 3 reads bible 26Aug 2021 two times So current streak must be 3 and longest streak too. Would Appericiate if you can help me in this
#Akina test for this rows
INSERT INTO `test` (`id`, `user_id`, `date_read`) VALUES
(1, 1, '2021-08-21'),
(2, 1, '2021-08-22'),
(3, 1, '2021-08-23'),
(4, 1, '2021-08-26'),
(5, 1, '2021-08-27'),
(6, 3, '2021-08-21'),
(7, 3, '2021-08-24'),
(8, 3, '2021-08-25'),
(9, 3, '2021-08-26'),
(11, 3, '2021-08-26'),
(12, 3, '2021-08-26'),
(13, 3, '2021-08-26')
My expceted Output for above
user_id current_streak longest_streak
1 2 3
3 3 3
But your query returns below output
user_id current_streak longest_streak
1 2 3
3 4 4
Please help me how to resolve this issue.
Not optimal, not compact, but clear solution:
WITH
cte1 AS (
SELECT *,
CASE date_read
WHEN LAG(date_read) OVER (PARTITION BY user_id ORDER BY date_read) + INTERVAL 1 DAY
THEN 0
ELSE 1 END group_start
FROM test
),
cte2 AS (
SELECT *,
SUM(group_start) OVER (PARTITION BY user_id ORDER BY date_read) group_num
FROM cte1
),
cte3 AS (
SELECT user_id, group_num,
COUNT(*) group_count
FROM cte2
GROUP BY user_id, group_num
)
SELECT DISTINCT
user_id,
FIRST_VALUE(group_count) OVER (PARTITION BY user_id ORDER BY group_num DESC) current_streak,
MAX(group_count) OVER (PARTITION BY user_id) longest_streak
FROM cte3
detailed fiddle

In mysql, how will I show all category in table even without data?

I AM USING MYSQL 8.0.
THIS IS MY DATA:
CREATE TABLE AA (
A int
);
insert into AA (A) values (1);
insert into AA (A) values (1);
insert into AA (A) values (2);
insert into AA (A) values (2);
insert into AA (A) values (1);
insert into AA (A) values (9);
insert into AA (A) values (NULL);
CREATE TABLE BB (
code int,
description varchar(30)
);
insert into BB (code, description) values (1, 'Male');
insert into BB (code, description) values (2, 'Female');
insert into BB (code, description) values (3, 'No Response');
THIS IS THE QUERY:
WITH cte AS (
SELECT MAX(CASE
WHEN a.A IS NULL THEN 9
WHEN b.code IS NULL THEN 8
ELSE a.A
END
) CODE,
CASE
WHEN a.A IS NULL THEN 'BLANK'
WHEN b.code IS NULL THEN 'NOT VALID'
ELSE b.description
END SEX,
COUNT(*) TOTAL,
ROUND(100.0 * COUNT(*) / SUM(COUNT(*)) OVER (), 2) PERCENT
FROM AA a LEFT JOIN BB b ON b.code = a.A
GROUP BY SEX
)
SELECT CODE, SEX, TOTAL, PERCENT,
SUM(TOTAL) OVER (ORDER BY CODE) CUMULATIVE,
ROUND(100.0 * SUM(TOTAL) OVER (ORDER BY CODE) / SUM(TOTAL) OVER (), 2) CUMPERCENT
FROM cte
UNION ALL
SELECT 0, 'TOTAL', COUNT(*), 100.00, 0, 0.00
FROM AA
ORDER BY CODE;
THIS IS THE OUTPUT:
CODE | SEX | TOTAL | PERCENT | CUMULATIVE | CUMPERCENT
0 TOTAL 7 100.00 0 0.00
1 MALE 3 42.86 3 42.86
2 FEMALE 2 28.57 5 71.43
8 NOT VALID 1 14.29 6 85.71
9 BLANK 1 14.29 7 100.00
EXPECTED OUTPUT:
CODE | SEX | TOTAL | PERCENT | CUMULATIVE | CUMPERCENT
0 TOTAL 7 100.00 0 0.00
1 Male 3 42.86 3 42.86
2 Female 2 28.57 5 71.43
3 No Response 0 0.00 5 71.43
8 NOT VALID 1 14.29 6 85.71
9 BLANK 1 14.29 7 100.00
This was just an update from my previous question. I just want to show in the output table all the category even if there are no data on it. In my expected output, the 'No Response' is '0' (or blank/NULL) since there is no value on it.
Thank you so much.
WITH
-- adjust table AA
cte1 AS (SELECT COALESCE(A, 8) A
FROM AA),
-- adjust table BB
cte2 AS (SELECT code, description
FROM BB
UNION ALL
SELECT 8, 'NOT VALID'
UNION ALL
SELECT 9, 'BLANK'),
-- join and calculate counts
cte3 AS (SELECT cte2.code CODE,
cte2.description SEX,
COUNT(cte1.A) TOTAL
FROM cte2
LEFT JOIN cte1 ON cte1.A = cte2.code
GROUP BY cte2.code, cte2.description)
-- calculate cumulative values
SELECT CODE,
SEX,
TOTAL,
ROUND(100.0 * TOTAL / SUM(TOTAL) OVER (), 2) PERCENT,
SUM(TOTAL) OVER (ORDER BY CODE) CUMULATIVE ,
ROUND(100.0 * SUM(TOTAL) OVER (ORDER BY CODE) / SUM(TOTAL) OVER (), 2) CUMUPERCENT
FROM cte3
UNION ALL
-- and add totals
SELECT 0,
'TOTAL',
(SELECT SUM(TOTAL) FROM cte3),
100,
0,
0
ORDER BY CODE
fiddle
If you add these 3 records to table BB:
insert into BB (code, description) values (8, 'NOT VALID');
insert into BB (code, description) values (9, 'BLANK');
and update AA like this:
UPDATE AA SET A=8 WHERE A IS NULL;
You could also do this:
WITH cte AS (
SELECT
COALESCE(b.code,0) as CODE,
b.description as SEX,
COUNT(a.A) TOTAL,
ROUND(100.0 * COUNT(a.A) / SUM(COUNT(a.A)) OVER (), 2) PERCENT
FROM AA a RIGHT JOIN BB b ON b.code = a.A
GROUP BY SEX
)
SELECT CODE, SEX, TOTAL, PERCENT,
SUM(TOTAL) OVER (ORDER BY CODE) CUMULATIVE,
ROUND(100.0 * SUM(TOTAL) OVER (ORDER BY CODE) / SUM(TOTAL) OVER (), 2) CUMPERCENT
FROM cte
UNION ALL
SELECT 0, 'TOTAL', COUNT(*), 100.00, 0, 0.00
FROM AA
ORDER BY CODE;
The biggest change is changing the LEFT JOIN to a RIGHT JOIN.

count wthout invalid use group of function mysql

I have a table like this,
CREATE TABLE order_match
(`order_buyer_id` int, `createdby` int, `createdAt` datetime, `quantity` decimal(10,2))
;
INSERT INTO order_match
(`order_buyer_id`, `createdby`, `createdAt`, `quantity`)
VALUES
(19123, 19, '2017-02-02', 5),
(193241, 19, '2017-02-03', 5),
(123123, 20, '2017-02-03', 1),
(32242, 20, '2017-02-04', 4),
(32434, 20, '2017-02-04', 5),
(2132131, 12, '2017-02-02', 6)
;
here's the fiddle
on this table, order_buyer_id is id of the transaction, createdby are the buyer, createdAt are the time of each transaction, quantity are the quantity of transaction
I want to find out the maximum, minimum, median and average for each repeat order (the buyer with transaction > 1)
so on this table, expected results are just like this
+-----+-----+---------+--------+
| MAX | MIN | Average | Median |
+-----+-----+---------+--------+
| 3 | 2 | 2.5 | 3 |
+-----+-----+---------+--------+
note: im using mysql 5.7
I am using this syntax
select -- om.createdby, om.quantity, x1.count_
MAX(count(om.createdby)) AS max,
MIN(count(om.createdby)) AS min,
AVG(count(om.createdby)) AS average
from (select count(xx.count_) as count_
from (select count(createdby) as count_ from order_match
group by createdby
having count(createdby) > 1) xx
) x1,
(select createdby
from order_match
group by createdby
having count(createdby) > 1) yy,
order_match om
where yy.createdby = om.createdby
and om.createdAt <= '2017-02-04'
and EXISTS (select 1 from order_match om2
where om.createdby = om2.createdby
and om2.createdAt >= '2017-02-02'
and om2.createdAt <= '2017-02-04')
but it's said
Invalid use of group function
We can try aggregating by createdby, and then taking the aggregates you want:
SELECT
MAX(cnt) AS MAX,
MIN(cnt) AS MIN,
AVG(cnt) AS Average
FROM
(
SELECT createdby, COUNT(*) AS cnt
FROM order_match
GROUP BY createdby
HAVING COUNT(*) > 0
) t
To simulate the median in MySQL 5.7 is a lot of work, and ugly. If you have a long term need for median, consider upgrading to MySQL 8+.

MySQL sum plus count in same query

I'm trying to get the rates from anonymous people plus the ones who are registered. They are in different tables.
SELECT product.id, (SUM( users.rate + anonymous.rate ) / COUNT( users.rate + anonymous.rate ))
FROM products AS product
LEFT JOIN users ON users.id_product = product.id
LEFT JOIN anonymous ON anonymous.id_product = product.id
GROUP BY product.id
ORDER BY product.date DESC
So, the tables are like the following:
users-->
id | rate | id_product | id_user
1 2 2 1
2 4 1 1
3 5 2 2
anonymous-->
id | rate | id_product | ip
1 2 2 192..etc
2 4 1 198..etc
3 5 2 201..etc
What I'm trying with my query is: for each product, I would like to have the average of rates. Currently the output is null, but I have values in both tables.
Thanks.
Try like this..
SELECT product.id, (SUM( ifnull(ur.rate,0) + ifnull(ar.rate,0) ) / (COUNT(ur.rate)+Count(ar.rate)))
FROM products AS product
LEFT JOIN users_rate AS ur ON ur.id_product = product.id
LEFT JOIN anonymous_rate AS ar ON ar.id_product = product.id
GROUP BY product.id
Sql Fiddle Demo
First, you are getting a cross join for each product within the table. This is not what you really want. I think this is close to what you are looking for
SELECT p.id,
(coalesce(u.sumrate, 0) + coalesce(a.sumrate, 0)) / coalesce(u.num, 0) + coalesce(a.num, 0))
FROM products p LEFT JOIN
(select id_product, sum(rate) as sumrate, count(*) as num
from users u
group by id_product
) u
ON u.id_product = p.id left join
(select id_product, sum(rate) as sumrate, count(*) as num
from anonymous a
group by id_product
) a
ON a.id_product = p.id
ORDER BY p.date DESC;
Assuming that id is unique in the product table, you don't need an aggregation at the outer level.
You cannot use count and sum on joins if you group by
CREATE TABLE products (id integer);
CREATE TABLE users_rate (id integer, id_product integer, rate integer, id_user integer);
CREATE TABLE anonymous_rate (id integer, id_product integer, rate integer, ip varchar(25));
INSERT INTO products VALUES (1);
INSERT INTO products VALUES (2);
INSERT INTO products VALUES (3);
INSERT INTO products VALUES (4);
INSERT INTO users_rate VALUES(1, 1, 3, 1);
INSERT INTO users_rate VALUES(1, 2, 3, 1);
INSERT INTO users_rate VALUES(1, 3, 3, 1);
INSERT INTO users_rate VALUES(1, 4, 3, 1);
INSERT INTO anonymous_rate VALUES(1, 1, 3, '192..');
INSERT INTO anonymous_rate VALUES(1, 2, 3, '192..');
select p.id,
ifnull(
( ifnull( ( select sum( rate ) from users_rate where id_product = p.id ), 0 ) +
ifnull( ( select sum( rate ) from anonymous_rate where id_product = p.id ), 0 ) )
/
( ifnull( ( select count( rate ) from users_rate where id_product = p.id ), 0 ) +
ifnull( ( select count( rate ) from anonymous_rate where id_product = p.id ), 0 )), 0 )
from products as p
group by p.id
http://sqlfiddle.com/#!2/a2add/8
I've check on sqlfiddle. When there are no rates 0 is given. You may change that.

MySQL query, MAX() + GROUP BY

Daft SQL question. I have a table like so ('pid' is auto-increment primary col)
CREATE TABLE theTable (
`pid` INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
`timestamp` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`cost` INT UNSIGNED NOT NULL,
`rid` INT NOT NULL,
) Engine=InnoDB;
Actual table data:
INSERT INTO theTable (`pid`, `timestamp`, `cost`, `rid`)
VALUES
(1, '2011-04-14 01:05:07', 1122, 1),
(2, '2011-04-14 00:05:07', 2233, 1),
(3, '2011-04-14 01:05:41', 4455, 2),
(4, '2011-04-14 01:01:11', 5566, 2),
(5, '2011-04-14 01:06:06', 345, 1),
(6, '2011-04-13 22:06:06', 543, 2),
(7, '2011-04-14 01:14:14', 5435, 3),
(8, '2011-04-14 01:10:13', 6767, 3)
;
I want to get the PID of the latest row for each rid (1 result per unique RID). For the sample data, I'd like:
pid | MAX(timestamp) | rid
-----------------------------------
5 | 2011-04-14 01:06:06 | 1
3 | 2011-04-14 01:05:41 | 2
7 | 2011-04-14 01:14:14 | 3
I've tried running the following query:
SELECT MAX(timestamp),rid,pid FROM theTable GROUP BY rid
and I get:
max(timestamp) ; rid; pid
----------------------------
2011-04-14 01:06:06; 1 ; 1
2011-04-14 01:05:41; 2 ; 3
2011-04-14 01:14:14; 3 ; 7
The PID returned is always the first occurence of PID for an RID (row / pid 1 is frst time rid 1 is used, row / pid 3 the first time RID 2 is used, row / pid 7 is first time rid 3 is used). Though returning the max timestamp for each rid, the pids are not the pids for the timestamps from the original table. What query would give me the results I'm looking for?
(Tested in PostgreSQL 9.something)
Identify the rid and timestamp.
select rid, max(timestamp) as ts
from test
group by rid;
1 2011-04-14 18:46:00
2 2011-04-14 14:59:00
Join to it.
select test.pid, test.cost, test.timestamp, test.rid
from test
inner join
(select rid, max(timestamp) as ts
from test
group by rid) maxt
on (test.rid = maxt.rid and test.timestamp = maxt.ts)
select *
from (
select `pid`, `timestamp`, `cost`, `rid`
from theTable
order by `timestamp` desc
) as mynewtable
group by mynewtable.`rid`
order by mynewtable.`timestamp`
Hope I helped !
SELECT t.pid, t.cost, to.timestamp, t.rid
FROM test as t
JOIN (
SELECT rid, max(tempstamp) AS maxtimestamp
FROM test GROUP BY rid
) AS tmax
ON t.pid = tmax.pid and t.timestamp = tmax.maxtimestamp
I created an index on rid and timestamp.
SELECT test.pid, test.cost, test.timestamp, test.rid
FROM theTable AS test
LEFT JOIN theTable maxt
ON maxt.rid = test.rid
AND maxt.timestamp > test.timestamp
WHERE maxt.rid IS NULL
Showing rows 0 - 2 (3 total, Query took 0.0104 sec)
This method will select all the desired values from theTable (test), left joining itself (maxt) on all timestamps higher than the one on test with the same rid. When the timestamp is already the highest one on test there are no matches on maxt - which is what we are looking for - values on maxt become NULL. Now we use the WHERE clause maxt.rid IS NULL or any other column on maxt.
You could also have subqueries like that:
SELECT ( SELECT MIN(t2.pid)
FROM test t2
WHERE t2.rid = t.rid
AND t2.timestamp = maxtimestamp
) AS pid
, MAX(t.timestamp) AS maxtimestamp
, t.rid
FROM test t
GROUP BY t.rid
But this way, you'll need one more subquery if you want cost included in the shown columns, etc.
So, the group by and join is better solution.
If you want to avoid a JOIN, you can use:
SELECT pid, rid FROM theTable t1 WHERE t1.pid IN ( SELECT MAX(t2.pid) FROM theTable t2 GROUP BY t2.rid);
Try:
select pid,cost, timestamp, rid from theTable order by timestamp DESC limit 2;