Change Value based on previous rows in mysql - mysql

I have a table which has below structure. I'm using phpmyadmin 3.4.5, mysql version 5.5.16.
Table
Invoice_ID - PO_ID- Product - Quantity Invoice_Qty - Amount
Inv1 - PO1 - P1 - 100 - 50 - 1000
Inv2 - PO1 - P1 - 100 - 50 - 1000
Inv3 - PO2 - P2 - 50 - 20 - 500
Inv4 - PO2 - P2 - 50 - 20 - 500
Inv5 - PO2 - P3 - 50 - 10 - 250
What I'm really want to do is that
If Previous Row of PO_ID and and Product Name is
Same as current Row PO_ID and Product then Quantity of current row should be zero?
Sum of Invoice_Quantity = Quantity. So Required like below
My Expected Output given below:
Out Put:
Invoice_ID - PO_ID- Product - Quantity Invoice_Qty - Amount
Inv1 - PO1 - P1 - 100 - 50 - 1000
Inv2 - PO1 - P1 - 0 - 50 - 1000
Inv3 - PO2 - P2 - 50 - 20 - 500
Inv4 - PO2 - P2 - 0 - 20 - 500
Inv5 - PO2 - P3 - 0 - 10 - 250
I tried the How to get result set like Oracle lag function. But It not worked for me.
And tried to write a procedure for that. I'm stuck with export resutlset.
That is I don't know how to assign and get the result set.
Please help me out this problem.
Refer:
http://sqlfiddle.com/#!2/5c0b0/4

Your sqlfiddle was confusing. Please don't provide sample data here and then use different sample data in the sqlfiddle. And your desired result here is wrong, since you said in the description
If Previous Row of PO_ID and and Product Name is Same as current Row PO_ID and Product then Quantity of current row should be zero
Anyway, used my own...
select
t.*,
if(#previd = po_id and #prevprod = Product, 0, Quantity) AS new_quantity,
#previd := po_id,
#prevprod := product
from
t
, (select #previd:=null, #prevprod:=null) var_init
order by po_id, product
sqlfiddle
Note, that the order in the select clause is important, as well as the order by clause.

The previous ID is the maximum ID of all lower IDs. So the statement can be written as:
select
invoice_id, po_id, product,
case when mytable.po_id = prev_mytable.po_id and mytable.product = prev_mytable.product
then 0
else mytable.quantity
end as qty,
invoice_qty, amount
from mytable
left join mytable prev_mytable on prev_mytable.id =
(
select max(id)
from mytable all_prev_mytable
where all_prev_mytable.id < mytable.id
)
order by invoice_id;
And here is the SQL fiddle: http://sqlfiddle.com/#!2/5c0b0/11.
This is Standard SQL and should thus work with about any dbms.

this works :):) :
select Invoice_ID,PO_ID,product,
case when
decode(lead(Quantity) over (order by PO_ID),Quantity,'SAME','DIFF') = 'SAME'
then Quantity
else 0
end Quantity, Amount
from <table-name>

Related

Why does my SQL Statement not SUM() up correctly?

I have the following statement:
SELECT
ROUND(SUM(invoicetitle.unitpricegross*invoicetitle.suppliedquantity),2) as Costs,
SUM(invoicetitle.suppliedquantity) AS Unitamounts
FROM invoicetitle
WHERE
((SELECT invoice.state
FROM invoice where invoicetitle.invoiceid = invoice.invoiceid
and (invoice.invoicedate >= 1609459200000 and invoice.invoicedate <= 1640908800000)) = (1 or 4))
GROUP BY invoicetitle.invoicetitle_number
note that = (1 or 4) refers to two statements in the database where 1 is sold and 4 is a refund.
With = (1)) I get the following results:
Costs - Unitamounts
3.281,10 - 582
With = (4)) I get the following results:
Costs - Unitamounts
-115,2 - -32
With = (1 or 4)) I get the following results:
Costs - Unitamounts
3.281,10 - 582
But I expect as a correct SUM() of it:
Costs - Unitamounts
3.165,9 - 550
What am I doing wrong that the results are not subtracted correctly?
You probably meant to do:
SELECT
ROUND(SUM(invoicetitle.unitpricegross*invoicetitle.suppliedquantity),2) as Costs,
SUM(invoicetitle.suppliedquantity) AS Unitamounts
FROM invoicetitle
INNER JOIN invoice ON invoicetitle.invoiceid = invoice.invoiceid
and (invoice.invoicedate BETWEEN 1609459200000
and 1640908800000)
WHERE invoice.state IN (1,4)
GROUP BY invoicetitle.invoicetitle_number

MySQL How to get the row with max date when joining multiple tables?

My goal is the get a list of current prices and prices at the time of whatever date is given. The price as of today is always product.price. Each time a new price is set, an entry is added to product_audit and revinfo.
If we are looking for what the prices were on 2020-11-31, it would return:
num CurrentPrice OldPrice
--------------------------------------
1001 100 175
1030 110 100
2010 150 130
EDIT FOR CLARIFICATION: My intention is to get what the price was on a specific day. So OldPrice is actually the newest entry in Product_aud/revinfo that is before or on the set date (in this case, 2020-11-31). Looking specifically at code 1001, the price was changed on 2020-08-02, 2020-09-26, and 2020-01-08. If we are looking at 2020-11-31, that means it should grab 2020-09-26 because it is the soonest date before then. This means the price of 1001 on 2020-11-31 was 175.
There are three tables: Product, product_audit, revinfo
Everytime the price is changed, an entry is added to product_audit with the new price and a reference to a new entry in revinfo that has the date/time. Revinfo contains entries for other audit tables mixed in.
product.id = product_audit.id
product_audit.rev = revinfo.id
product
id num price
------------------------
1 1001 100
2 1030 110
3 2010 150
product_audit
id rev price
------------------------
1 1 200
1 3 175
1 6 100
2 2 100
2 7 110
3 4 130
3 5 120
3 8 150
revinfo
id timestamp
-------------------
1 2020-08-02
2 2020-09-25
3 2020-09-26
4 2020-11-12
5 2020-12-20
6 2021-01-08
7 2021-01-09
8 2021-01-23
Of course this just returns the oldest price from product_audit:
SELECT product.num, product.price AS CurrentPrice, product_audit.price AS OldPrice
FROM product
LEFT JOIN product_audit ON product_audit.id = product.id
LEFT JOIN revinfo ON revinfo.id = product_audit.rev
WHERE rev.timestamp <= "2020-11-31"
GROUP BY product.id
I tried nesting joins like this based on some stuff I was reading, but quickly realized it still wasn't going to get the right price:
SELECT product.id, product.num, product.price AS CurrentPrice, revisions.price AS OldPrice
FROM product
LEFT JOIN (SELECT product_audit.id AS id, product_audit.price AS price, MAX(revinfo.timestamp) AS timestamp
FROM product_audit
LEFT JOIN revinfo ON product_audit.rev = revinfo.id
WHERE revinfo.timestamp <= $DATE{Date}
GROUP BY product_aud.id) AS revisions ON revisions.id = product.id
I can't seem to think of how to get to that last step. Some sort of WHERE timestamp = (SELECT...) maybe? But I haven't been able to figure that out.
Also, just a heads up, I'm limited to statements that start with SELECT because of permissions. I can't add functions or anything like that.
I had to assume how we were getting the "old" price, and my assumption was that you wanted the "earliest" revision record, so I used Row_number and a derived table to get that record and then use it in the join constraint for the revision table... not exactly sure what your logic is, but here is a fiddle with the resultset that matches your "desired results"
SELECT product.num, product.price AS CurrentPrice, product_audit.price AS OldPrice
FROM product
LEFT JOIN (select p.price, p.id, p.rev,
ROW_NUMBER() over (partition by p.id order by p.rev asc) as rn
From product_audit p
) AS product_audit ON product_audit.id = product.id
and product_audit.rn = 1
LEFT JOIN revinfo ON revinfo.id = product_audit.rev
WHERE revinfo.timestamp <= '2020-11-31';
https://www.db-fiddle.com/f/fbvrgo2gRLoBPhgwQnuvY9/3
WITH cte AS ( SELECT product.num,
product.price CurrentPrice,
product_audit.price OldPrice,
ROW_NUMBER() OVER (PARTITION BY product.num
ORDER BY revinfo.`timestamp` DESC) rn
FROM product
JOIN product_audit ON product_audit.id = product.id
JOIN revinfo ON revinfo.id = product_audit.rev
WHERE revinfo.`timestamp` <= #date
)
SELECT num, CurrentPrice, OldPrice
FROM cte
WHERE rn = 1;
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=a276ec8ad89e3c2f3aaeee411072fa3e

SQL if condition true, count only once

I have a table with three columns: Name, Country, Price and I need a SQL query that creates a fourth Boolean column named Qualify. This column needs to be true if Price<100 and there is no other row with price<100 and the same country.
Example:
Name - Country- Price- Qualify
Daniel - ES - 98 - TRUE
John - PT - 45 - TRUE
Maria - UK - 102 - FALSE
Anna - PT - 31 - FALSE (because there is already a row with PT and Price<100)
Joseph - UK - 25 - TRUE
Miriam -DK - 105 - FALSE
All this is because I do not want to count volumes more than one time if the price is under 100 and the country is the same. Is this even possible? Thanks
Think exists. In MySQL, you don't even need a case expression:
select t.*,
(t.price < 100 and
not exists (select 1
from t t2
where t2.country = t.country and t2.name <> t.name and t2.price < 100
)
) as flag
from t;
This assumes that name is unique, at least with respect to country.
Just providing another option using CASE statement:
Select
#row_number:=CASE
WHEN #country = Country AND Price < 100 AND #price < 100
THEN 1
ELSE 0 END AS Qualify,
#country:= Country As Country,
#price:= Price As Price
FROM
Test
ORDER BY Country, Price
Here is the demo

mysql search for duplicates by column A and delete by criteria on othe columns

I have a mysql database, containing a table with following columns: PartCode, Price and Quantity. The goal is to write a query which would search for rows with duplicate 'PartCode', then compare them by Price and Quantity, and delete the row with higher 'Price' values, but if one of the 'Quantities' is 0, then to delete the one with 0 quantity.
A sample table would look like this:
PartCode - Price - Quantity
ABCD - 5 - 2
CDEF - 6 - 1
CDEF - 4 - 1
VZXY - 8 - 4
VZXY - 7 - 0
.... - ... - ...
And for the results, I'd like to get this:
PartCode - Price - Quantity
ABCD - 5 - 2
CDEF - 4 - 1
VZXY - 8 - 4
.... - ... - ...
I've found the solution to receive all duplicate PartCodes:
SELECT `PartCode` FROM `Table` GROUP BY `PartCode` HAVING COUNT(*) > 1
What I cannot figure out, is how to implement it to delete one of the duplicates, by other columns comparison.
Thanks in advance.
The following query will delete one record from each PartCode group having either the highest quantity, or if one record has a zero quantity then it would be deleted instead.
DELETE
FROM yourTable t1
WHERE (PartCode, Quantity) IN
(
SELECT
t.PartCode,
t.qty
FROM
(
SELECT
PartCode,
CASE WHEN MIN(Quantity) = 0 THEN MIN(Quantity) ELSE MAX(Quantity) END AS qty
FROM yourTable
GROUP BY PartCode
HAVING COUNT(*) > 1
) t
)

How can I have a column with sum of smaller IDs in MySQL?

Assume I have a table like this:
id pay
-- ---
1 10
2 20
3 30
4 40
5 50
6 60
I want to create a view from table above with this result:
id pay paid_before
-- --- -------------
1 10 0
2 20 10
3 30 30
4 40 60
5 50 100
6 60 150
which "paid_before" is sum of pay rows that have smaller id.
How could I do this job?
This accomplishes what you want.
SELECT p1.id,p1.pay, sum(p2.pay) as Paid_Before FROM PAYMENTS P1 LEFT JOIN
PAYMENTS P2 ON p1.id > p2.id
GROUP BY p1.id, p1.pay
See this sql fiddle
In MySQL, this is most efficiently done with variables:
select p.id, p.pay, (#p := #p + p.pay) - p.pay as PaidBefore
from payments p cross join
(select #p := 0) vars
order by id;
Although this is not standard SQL (which I usually prefer), that is okay. The standard SQL solution is to use cumulative sum:
select p.id, p.pay, sum(p.pay) over (order by p.id) - p.pay as PaidBefore
from payments p;
Many databases support this syntax, but not MySQL.
The SQL Fiddle (courtesy of Atilla) is here.