MySQL: calculate percentiles among each group - mysql

I have a Transaction table with transactions data :
"created" - DateTime of transaction
"price" - transaction price
"id": product identifier
Sample data
id
created
price
5
2022-05-08 20:20:00
1
5
2022-05-08 19:00:00
2
5
2022-05-08 7:40:00
3
5
2022-05-05 8:20:00
4
2
2022-05-09 10:40:00
5
2
2022-05-09 10:40:00
6
2
2022-05-07 15:40:00
7
2
2022-05-03 16:30:00
8
Goal: to calculate the 25% percentile for prices and the number of transactions with prices lower than the 25 price percentile(25% percent of all) per id.
Expected result:
id
price 1st q
n_transactions
5
2
1
2
6
1
I have tried:
SELECT
id,
MAX(CASE WHEN Quartile = 1 THEN price END) 1Quartile,
FROM (
SELECT
id,
price,
NTILE(4) OVER (PARTITION BY id ORDER BY price) AS Quartile
FROM
Transactions) Vals
GROUP BY
id
ORDER BY
id
which should return the 1 quartile for the price but it returns only an execution error with the message "check SQL syntax".
MySQL version: 5.7.36

Related

Running total in two tables

Consider two tables: invoices and payments. The invoices table contains records of invoices raised, and the payments table contains records of payments received.
invoices
id
date
cname
amount
1
2021-12-12
cname1
10000
2
2021-12-13
cname2
5000
3
2022-01-15
cname1
7000
4
2022-01-16
cname2
1000
payments
id
date
cname
amount
1
2022-01-05
cname1
5000
2
2022-01-07
cname2
5000
3
2022-02-05
cname1
10000
4
2022-02-06
cname2
1000
CALCULATE RUNNING BALANCE
Q) Extend the SQL query to do invoice / payment matching as follows (as of 28/2/2022)
matching
date
document_id
cname
amount
due
2021-12-12 00:00:00
1
cname1
10000
10000
2022-01-05 00:00:00
1
cname1
-5000
5000
2022-01-15 00:00:00
3
cname1
7000
12000
2022-02-05 00:00:00
3
cname1
-10000
2000
2021-12-13 00:00:00
2
cname2
5000
5000
2022-01-07 00:00:00
2
cname2
-5000
0
2022-01-16 00:00:00
4
cname2
1000
1000
2022-02-06 00:00:00
4
cname2
-1000
0
You can union both tables considering the second one with negative amount, and then a simple running total will produce the result you want. For example:
select
date,
id as document_id,
cname,
amount,
sum(amount) over(partition by id order by date) as due
from (
select * from invoices
union all select id, date cname, -amount from payments
) x
order by cname, date
SELECT `date`,
documentId,
cname,amount,
due FROM (SELECT `date`,
documentId,
cname,
amount,
(CASE WHEN #running_customer='' THEN #running_balance:=amount
WHEN #running_customer=cname THEN #running_balance:=#running_balance+amount ELSE #running_balance:=amount END) due,
#running_customer:=cname
FROM (SELECT `date`, id AS documentId,cname, amount FROM `invoices`i
UNION ALL
SELECT `date`, id AS documentId,cname, amount*-1 AS actionType FROM `payments` p) final
JOIN (SELECT #running_customer:='') rc
JOIN (SELECT #running_balance:=0) rb
ORDER BY cname, `date`) finalResult
You need to be using assignment operator for these kind of problems.

Mysql: How to Query 1 table with 2 different conditions and display result in separate columns

I have 1 table named ItemDelivery. I wanted to get the count of items that has DeliveryDate and the items that has been receivedDate per month. Some items deliveryDate month have different receiveDate month such as items scheduled for delivery on the later part of the month would be received on early days of succeeding month. Some may take months to be delivered for overseas.
This is the data:
id iditem deliveryDate receiveDate
1 2 2021-01-03 2021-01-05
2 2 2021-01-03
3 3 2021-02-05 2021-02-06
4 5 2021-02-05
5 4 2021-02-20 2021-03-01
6 3 2021-03-15 2021-04-08
I would like to have
Mo Delivery Recieve
Jan 2 1
Feb 3 1
Mar 1 1
Apr 0 1
This query gives 1 columns only
select date_format(deliveryDate,'%b') as mo ,
count(id) as delivery
from ItemDelivery
where year(deliveryDate)=2021
group by month(deliveryDate)
union all
select date_format(receiveDate,'%b') as mo ,
count(id) as received
from ItemDelivery
where year(receiveDate)=2021
group by month(receiveDate)
Output:
Mo Delivery
Jan 2
Feb 3
Mar 1
Jan 1
Feb 1
Mar 1
Apr 1
This query also have different output
SELECT d1.mo, d1.delivery, d2.received
FROM
(SELECT month(deliveryDate) as mo, count(id) AS delivery
FROM ItemDelivery
WHERE year(deliveryDate)=2021 group by month(deliveryDate)) as d1,
(SELECT month(receiveDate) as mo, count(id) AS received
FROM ItemDelivery
WHERE year(receiveDate)=2021 group by month(receiveDate)) as d2
Output:
mo delivery received
1 2 1
2 3 1
3 1 1
1 2 1
2 3 1
3 1 1
1 2 1
2 3 1
3 1 1
1 2 1
2 3 1
3 1 1
This has also the same output except if I use condition d1.mo=d2.mo:
select d1.mo, d1.delivery, d2.received
from
(SELECT month(deliveryDate) as mo, count(id) as delivery
FROM ItemDelivery
WHERE year(deliveryDate)=2021 group by month(deliveryDate)) d1
inner join
(SELECT month(receiveDate) as mo, count(id) as received
FROM ItemDelivery
WHERE year(receiveDate)=2021 group by month(receiveDate)) d2
Any suggestions ?
SELECT
date_format(eventDate,'%b') AS mo,
SUM(delivery) AS delivery,
SUM(receive) AS receive
FROM
(
SELECT deliveryDate AS eventDate, 1 AS delivery, 0 AS receive FROM ItemDelivery
UNION ALL
SELECT receiveDate AS eventDate, 0 AS delivery, 1 AS receive FROM ItemDelivery
)
AS rotated
WHERE
eventDate >= '2021-01-01'
AND eventDate < '2022-01-01'
GROUP BY
month(eventDate)

SQL query for below data

For input, When quantity value greater then 1, convert in a new row with value 1 for quantity column.
INPUT
ID ProductFK Quantity Price
------------------------------------------------
10 1 2 100
11 2 3 150
12 1 1 120
OUTPUT
ID ProductFK Quantity Price
------------------------------------------------
10 1 1 100
10 1 1 100
11 2 1 150
11 2 1 150
11 2 1 150
12 1 1 120
We can do this using a sequence table trick. Inner join your current table to a sequence on the condition that the quantity be greater than or equal to the sequence value. For example:
SELECT t1.ID, t1.ProductFK, 1 AS Quantity, t1.Price
FROM yourTable t1
INNER JOIN (SELECT 1 AS Quantity UNION ALL SELECT 2 UNION ALL SELECT 3) t2
ON t1.Quantity >= t2.Quantity
ORDER BY t1.ID;
Demo

Mysql finding count of previous date of occurences for each record

I have a table to store id, sid with a date time.
id is used as primary key and no meaning in data.
sid is used to identify entity.
eg.
id sid date
--------------------
1 1 2020-01-12
2 2 2020-01-01
3 1 2019-12-31
4 2 2019-12-31
5 1 2019-12-31
6 1 2019-11-01
7 3 2019-11-01
8 3 2018-12-21
9 2 2018-12-21
Then I would like to query for each record, count occurrences in the same table with the previous date of current date, and with the same sid, like:
id sid date previous_count
----------------------------------
1 1 2020-01-12 2
2 2 2020-01-01 1
3 1 2019-12-31 1
4 2 2019-12-31 1
5 1 2019-12-31 1
6 1 2019-11-01 0
7 3 2019-11-01 1
8 3 2018-12-21 0
9 2 2018-12-21 0
Explanation:
for row 1, since sid 1 has two records in 2019-12-31, which is the previous date of 2020-01-12 for sid 1 in the table, it has 2 in previous_count;
while in row 2, since sid 2 has only 1 record in 2019-12-31, which is the previous date of 2020-01-01 for sid 2, it has 1 in previous_count.
Thanks
Your are looking for dense_rank() - 1:
select t.*,
(dense_rank() over (partition by sid order by date) - 1) as previous_count
from t
order by id;
In older versions of MySQL, you could use variables or a correlated subquery:
select t.*,
(select count(distinct t2.date)
from t t2
where t2.sid = t.sid and t2.date < t.date
) as previous_count
from t
order by id;
EDIT:
Ahh, I think I may have misunderstood the problem. I think this does what you want:
select t.*, lag(cnt, 1, 0) over (partition by sid order by date)
from (select t.*,
count(*) over (partition by sid, date) as cnt
from t
) t
order by id;
Here is a db<>fiddle.

Merging Multiple Queries - Same Table

I have 1 table called itemmovement : It has Item Id , Quantity In , Quantity Out , Invoice Id, Date. I need to make in one query to show how many pieces are sold and beside the sold column there will be the current on hand quantity .
itemmovement
Id itemid qtyin qtyout invid purchasereturnid date
1 1 10 2019-01-04
2 2 8 2019-01-06
3 2 2 1 2019-01-08
4 1 3 2 2019-01-12
5 2 1 2019-02-04
6 3 4 2019-03-04
7 1 1 3 2019-04-04
8 1 1 1 2019-04-14
9 3 1 2 2019-04-24
I need the query to show this result
Id itemid Sold Quantity OnHandQty
1 1 4 5
2 2 2 7
3 3 0 3
I'm Trying to use this query but not working
SELECT *
FROM
(SELECT itmv.itemid,
sum(itmv.qtyout)-sum(itmv.qtyin)
FROM itemmovement itmv
WHERE (itmv.systemdate BETWEEN '2019-01-01' AND '2019-06-01')
AND invid>0
GROUP BY itmv.itemid) AS result1,
(SELECT sum(itmv2.qtyin)-sum(itmv2.qtyout)
FROM itemmovement itmv2
WHERE itmv.itemid=itmv2.itemid
GROUP BY itmv2.itemid) AS result2
ORDER BY sum(itmv.qtyin)-sum(itmv.qtyout)
I'm getting :
Unknown column 'itmv.itemid' in 'where clause it for this syntax :
where itmv.itemid = itmv2.itemid
Here's your query.
select itemid
, sum(case when COALESCE(invid,0) > 0 then qtyout else 0 end) as Sold_Qantity
, sum(qtyin)-sum(qtyout) as OnHandQty
from itemmovement
group by itemid