UPDATE using previously updated value - sql-server-2008

I'm trying to update records using previously updated value in a single query run. It's a running total case, but it's an UPDATE query not a SELECT query.
Table Inventory (Simplified)
Id Qty RunningQty ItShouldBeUpdatedTo InsteadItsUpdatedTo
1 200 0 200 200
2 300 0 500 300
3 400 0 900 400
4 100 0 1000 100
My current query is something like this
UPDATE Inventory
SET RunningQty = ISNULL(A.RunningQuantity, 0) + Quantity
FROM Inventory I
OUTER APPLY
(
-- Take previous row RunningQty
SELECT TOP 1 RunningQty
FROM Inventory
WHERE Id < I.Id
ORDER BY Id DESC
) A
It seems to update next row, sql server not using previously updated value.
Note: It will be quite large table so i calculate it using previous row running quantity and add the value with current row quantity, instead of calculating it from the first row.
What's the correct way to do this?
Thanks in advance.

Let me preface this by saying, this will be significantly easier to do in SQL Server 2012. In that version you will be able to use something like this:
update i1
set i1.runningqty = i2.RunningQty
from inventory i1
inner join
(
select id, SUM(qty) OVER(ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) RunningQty
from inventory
) i2
on i1.id = i2.id;
See SQL Fiddle with Demo
But it is not as easy in SQL Server 2008, however you should be able to use something like this:
declare #total int
set #total = 0
update inventory
set runningqty = #total,
#total = #total + qty;
See SQL Fiddle with Demo.
There are other methods including using a cursor to perform this in sql server 2008.

Related

How to sum daily resetting data using MySQL

I am attempting to plot data cumulatively from a MySQL table which logs a value, resetting to 0 every day. After selecting the values using select * from table where DateTime BETWEEN DateA AND DateB, the data looks like this: current data. I would like the output to look like this: preferred data, ignoring the daily resets.
As I am a novice in SQL I was unable to find a solution to this. I did, however, obtain the correct output in Matlab using a for loop:
output = data;
for k=1:(size(data, 1)-1)
% check if next value is smaller than current
if data(k+1)<data(k)
% add current value to all subsequent values
output = output + (1:size(data, 1)>k)'.*input(k);
end
end
I would like the final product to connect to a web page, so I am curious if it would be possible obtain a similar result using only SQL. While I have tried using SUM(), I have only been able to sum all values, but I need to add the last value each day to all subsequent values.
Using CTE and comparing dates, you can sum all values each date.
Let's say that table1 below is defined.
create table table1 (col_date date, col_value int);
insert into table1 values
('2020-07-15',1000),
('2020-07-15',2000),
('2020-07-16',1000),
('2020-07-16',3000),
('2020-07-16',4000),
('2020-07-17',1000),
('2020-07-18',2000),
('2020-07-19',1000),
('2020-07-19',1000),
('2020-07-19',2000),
('2020-07-19',3000),
('2020-07-20',4000),
('2020-07-20',5000),
('2020-07-21',6000)
;
In this case, the query looks like this:
with cte1 as (
select col_date, sum(col_value) as col_sum from table1
where col_date between '2020-07-16' and '2020-07-20'
group by col_date
)
select a.col_date, max(a.col_sum), sum(b.col_sum)
from cte1 a inner join cte1 b on a.col_date >= b.col_date
group by a.col_date;
The output is below:
col_date |max(a.col_sum) |sum(b.col_sum)
2020-07-16 |8000 | 8000
2020-07-17 |1000 | 9000
2020-07-18 |2000 |11000
2020-07-19 |7000 |18000
2020-07-20 |9000 |27000
The column of max() is just for reference.

Keep the newest one field value until it changes then keeping its newest field value

I have a few tables that have millions of records where a sensor was sending multiple 0 and 1 values and this data was logged to the table even though we only needed it to keep the very first 1 or 0 per each 1 to 0 or 0 to 1 change.
Adjustments have been made so we only now get the 1 and 0 values on each change and not every one second or whatever but I need to cleanup the unnecessary records from the tables.
I've done some research and testing and I'm having trouble figuring out what method to use here to delete the records not needed. I was trying to figure out how to retain the previous value record using variables and also created row numbers but it's not working as I need it to.
I created an SQLFiddle here and tried some logic per the example post MySQL - How To Select Rows Depending on Value in Previous Row (Remove Duplicates in Each Sequence). I keep getting back no results from this and when I tried running it on a large local MySQL table, and I got an error wto I have to increase the MySQL Workbench read query timeout to 600 or it lost connection.
I also found the "MySql - How get value in previous row and value in next row?" post and tried some variations of it and also "How to get next/previous record in MySQL?" and I've come up with total failure getting the expected results.
The Data
The data in the tables has a TimeStr column and a Value column just as in the screen shot and on the SQLFiddle link I posted with a small sample of the data.
Each record will never have the same TimeStr value but I really only need to keep the very first record time wise when the sensor either turned ON or OFF if that clarifies.
I'm not sure if the records will need an incremental row number added to get the expected results since it only has the TimeStr and the Value records otherwise.
My Question
Can anyone help me determine a method that I can use on a couple large tables to delete the records from a table where there are subsequent and duplicate Value values so the tables only has the very first 1 or 0 records where those actually change from a 1 to 0 or 0 to 1?
I will accept an answer that also results in just the records needed—but any that perform fast would be even more greatly appreciated.
I can easily put those into a temp table, drop the original table, and then create and insert the needed records only into the original table.
Expected Results
| TimeStr | Value |
|----------------------|-------|
| 2018-02-13T00:00:00Z | 0 |
| 2018-02-13T00:00:17Z | 1 |
| 2018-02-13T00:00:24Z | 0 |
| 2018-02-13T00:00:28Z | 1 |
Select t.timestr, t.value from (
SELECT s.*, #pv x1, (#pv := s.value) x2
FROM sensor S, (select #pv := -1) x
ORDER BY TimeStr ) t
where t.x1 != t.x2
See http://sqlfiddle.com/#!9/8d0774/122
Try this :
SET #rownum = 0;
SET #rownum_x = 0;
SELECT b.rownum, b.TimeStr, b.Value
FROM
(
SELECT #rownum := #rownum+1 as rownum, TimeStr, Value
FROM sensor
ORDER BY TimeStr
) b
LEFT JOIN (
SELECT #rownum_x := #rownum_x+1 as rownum_x, TimeStr as TimeStr_x, Value as Value_x
FROM sensor
ORDER BY TimeStr
) x ON b.rownum = x.rownum_x + 1
where b.Value <> x.Value_x or x.Value_x is null
order by b.TimeStr
The result I got is
You want the first record for each value when it appears. This suggests variables. Here is one way that only involves sorting and no joining:
select t.*
from (select t.*,
(case when value = #prev_value then value
when (#save_prev := #prev_value) = NULL then NULL
when (#prev_value := value) = NULL then NULL
else #save_prev
end) as prev_value
from (select t.*
from sensor t
order by timestr
) t cross join
(select #prev_value := -1) params
) t
where prev_value <> value;
Notes:
The subquery for ordering only seems to be needed since MySQL 5.7.
The case is just a way to introduce serialized code. When using a variable it should only be used on one expression.
This only requires one sort -- and if you have an index, that doesn't even need to be a sort.
Here is a SQL Fiddle.

Mysql condition on WHERE clause

I m using mysql database using php to fetch data. I have distributor column, i m using WHERE clause like WHERE d_id = 1 . i have 1 to 4 distributors. On front end i have HTML select option in which if i select any distributor it shows it's data but i added an option value="0" as Total . Now if i select Total it should show all data, actually where clause should not work then. (show all distributors data)
Dist: Product Sales
1. dis_a abc 100
2. dis_b abc 50
3. dis_c cde 10
4. dis_c cde 10
Here is an example of a select that should do roughly what you want.
select d.[disc:], d.[Product], d.[Sales]
from your_table d
where d.your_table_seq = case when #dist_id = 0 then d.your_table_seq else #dist_id end
The case block will allow you pull every row when the #dist_id parameter is equal to 0, but will only pull id's that are equal to #dist_id when #dist_id is not equal to 0.

MYSQL Select single row by multiple parameters returning unexpected result

I have a table offers with five relevant columns, offer_id, offer_type, offer_amount, end_date and offer_status
I am trying to select the row with the highest offer_amount that matches the correct offer_type and an offer_status of 1 (active).
The query I am using is
SELECT * FROM offers_tbl WHERE offer_status = 1 AND offer_type = 'site-wide' AND offer_amount = (SELECT MAX(offer_amount) FROM offers_tbl )
Whats happening is that this is not returning any results if the MAX(offer_amount) happens to have a offer_status of 0 (inactive).
If the particular row with the highest MAX(offer_amount) happens to have an offer_status of 1, this works fine. Its only when the offer status is 0 that this breaks.
Try making it find MAX out of active ones:
SELECT * FROM offers_tbl WHERE offer_status = 1 AND offer_type = 'site-wide' AND offer_amount = (SELECT MAX(offer_amount) FROM offers_tbl WHERE offer_status = 1)
Otherwise, it's finding MAX offer amounts that could have a status of 0, which makes the outside query not find a match of status of 1 AND that amount.

Replace mysql user defined variable

I have a query which works great given that the result is only one number, but now I need to allow for multiple rows to be returned and the query cannot handle that because it uses a user define variable... here is original procedure
CREATE DEFINER=`root`#`%` PROCEDURE `MapRank`(pTankID bigint,pMapID int, pColor int(2))
BEGIN
SET #RankNumber:=0;
select RankNumber
from
(select
TankID,
#RankNumber:=#RankNumber+1 as RankNumber,
MapID,
Color
from MAPDATA WHERE MapID = pMapID order by Rank DESC, TotalPP DESC) Query1 where TankID = pTankID AND COLOR = pColor ;
END
this returns a single number, essentially counting the number of records down it is, giving me the "row" location.
now I need to change it to give me all rows with out the where for mapid and color, so that I can see all ranks for all mapid/color combo
this is what I have that currently does not work
SET #RankNumber:=0;
select
RankNumber,MapID,COlor
from
(select
TankID,
#RankNumber:=#RankNumber + 1 as RankNumber,
MapID,
Color
from
MAPDATA
order by TotalPP DESC) Query1
where
TankID = 18209 ORDER BY RankNumber
the yielding query result looks as such:
1062 3 1
3544 3 0
6717 17 1
6752 17 3
7453 3 2
7860 17 0
7984 17 2
9220 3 3
if I run manually lets say, map id 3 and color 3 which says rank number is 9220 with the FIRST query I get this
6022
I need this to be able to be done possibly from multiple MySQL connections so ideally done without use of a temporary variable since its possible another person may come in and use that... any help would be great.
After digging and messing more I have found the solution to be to set the variable back to zero from within the outer select.. and since user defined variable are connection level and I utilize pooling we should never have an issue.
SET #RankNumber:=0;
select
RankNumber,MapID,COlor, #RankNumber:=0
from
(select
TankID,
#RankNumber:=#RankNumber + 1 as RankNumber,
MapID,
Color
from
MAPDATA
order by MapID, Rank DESC, TotalPP DESC ) Query1
where
TankID = pTankID ORDER BY RankNumber;