How to get the previous non 0 value from a table? - sql

I still have not read a simple solution to this problem.
This is the table that I have:
This is the result that I want:
Basically, if the value for column SeriesStartRowNum is 0, it should retrieve the lastest, previous non 0 value from the table.
Anyone that knows how to easily do this?
Thanks in advance!

This is untested, due to images of data, but this looks like a gaps and island problem. One method, therefore, would be to use a windowed COUNT to count the number of non-zero values so far in the column SeriesStartRowNum to create "groups", and then in get the MAX value for SeriesStartRowNum in that group:
WITH Groups AS(
SELECT RowNumber,
CustomerID,
Value,
StartDate,
EndDate,
SeriesStartRowNum,
COUNT(NULLIF(SeriesStartRowNum,0)) OVER (/*PARTTION BY ???*/ ORDER BY RowNumber) AS Grp
FROM dbo.YourTable)
SELECT RowNumber,
CustomerID,
Value,
StartDate,
EndDate,
MAX(SeriesStartRowNum) OVER (PARTITION BY Grp)
FROM Groups;

If your actual data is like the sample data that you posted, with the 1st row of each combination of CustometID and Value having a non-0 value and all the rest are 0s, then all you need is MAX() window function:
SELECT RowNumber, CustometID, Value, StartDate, EndDate,
MAX(SeriesStartRowNum) OVER (PARTITION BY CustometID, Value) SeriesStartRowNum
FROM tablename

Related

Function to REPLACE* last previous known value for NULL

I want to fill the NULL values with the last given value for that column. A small sample of the data:
2021-08-15 Bulgaria 1081636
2021-08-16 Bulgaria 1084693
2021-08-17 Bulgaria 1089066
2021-08-18 Bulgaria NULL
2021-08-19 Bulgaria NULL
In this example, the NULL values should be 1089066 until I reach the next non-NULL value.
I tried the answer given in this response, but to no avail. Any help would be appreciated, thank you!
EDIT: Sorry, I got sidetracked with trying to return the last value that I forgot my ultimate goal, which is to replace the NULL values with the previous known value.
Therefore the query should be
UPDATE covid_data
SET people_vaccinated = ISNULL(?)
Assuming the number you have is always increasing, you can use MAX aggregate over a window:
SELECT dt
, country
, cnt
, MAX(cnt) OVER (PARTITION BY country ORDER BY dt)
FROM #data
If the number may decrease, the query becomes a little bit more complex as we need to mark the rows that have nulls as belonging to the same group as the last one without a null first:
SELECT dt
, country
, cnt
, SUM(cnt) OVER (PARTITION BY country, partition)
FROM (
SELECT country
, dt
, cnt
, SUM(CASE WHEN cnt IS NULL THEN 0 ELSE 1 END) OVER (PARTITION BY country ORDER BY dt) AS partition
FROM #data
) AS d
ORDER BY dt
Here's a working demo on dbfiddle, it returns the same data with ever increasing amount, but if you change the number for 08-17 to be lower than that of 08-16, you'll see MAX(...) method producing wrong results.
In many datasets it is incorrect to make assumptions about the behaviour of the data in the underlying dataset, if your goal is simply to fill the blanks that might appear mid-way in a dataset then the answer to the post you referenced A:sql server nulls duplicate last known value in table is still one of the best solutions, here is an adaptation:
SELECT dt
, country
, cnt
, ISNULL(source.cnt, excludeNulls.LastCnt)
FROM #data source
OUTER APPLY ( SELECT TOP 1 cnt as LastCnt
FROM #data
WHERE dt < source.dt
AND cnt IS NOT NULL
ORDER BY dt desc) ExcludeNulls
ORDER BY dt
MAX and LAST_VALUE will give you the a value with respect to the entire record set, which would not work with the existing solutions if you had a value for 2021-08-19. In that case the last value would be used to fill the gaps, not the previous non-null value.
When we need to fill in gaps that occur part-way through the results we need to apply a filter to the window query, TOP 1 ... ORDER BY gives us the ability to filter and sort on entirely different fields to the one that we want to capture, but also means that we can display the last value for fields that are not numeric, see this fiddle a few other examples: https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=372285d29f97dbb9663e8552af6fb7a2

postgresql how to get min and max value from specific range

I have this table of log finger print attendannce.
and i want to select the table with the result like this.
as you can see, in the mindailylog field, it contains the minumum date of that day, and the maxdailylog field value contains the max value of that day.
You can use window functions:
select t.*,
min(waktuabsen) over (partition by userid, waktuabsen::date),
max(waktuabsen) over (partition by userid, waktuabsen::date),
from t;
This does the minimum per user_id. If you want the overall minimum, just remove userid from the partition by.

Replace first and last row having null values or missing values with previous/next available value in Postgresql12

I am a newbiew to postgresql.
I want to replace my first and last row of table,T which has null or missing values, with next/previous available values. Also, if there are missing values in the middle, it should be replaced with previous available value. For example:
id value EXPECTED
1 1
2 1 1
3 2 2
4 2
5 3 3
6 3
I am aware that there are many similar threads, but none seems to address this problem where the start and end also have missing values (including some missing in the middle rows). Also some of the concepts such as first_row ,partition by, top 1(which does not work for postgres) are very hard to grasp as a newbie.
So far i have referred to the following threads: value from previous row and Previous available value
Could someone kindly direct me in the right direction to address this problem?
Thank you
Unfortunately, Postgres doesn't have the ignore nulls option on lead() and lag(). In your example, you only need to borrow from the next row. So:
select t.*,
coalesce(value, lag(value) over (order by id), lead(value) over (order by id)) as expected
from t;
If you had multiple NULLs in a row, then this is trickier. One solution is to define "groups" based on when a value starts or stops. You can do this with a cumulative count of the values -- ascending and descending:
select t.*,
coalesce(value,
max(value) over (partition by grp_before),
max(value) over (partition by grp_after)
) as expected
from (select t.*,
count(value) over (order by id asc) as grp_before,
count(value) over (order by id desc) as grp_after
from t
) t;
Here is a db<>fiddle.

Calculated column reference in DB2

I have a table with the columns:date1,name and price. What i want to do is to add 2 columns one right having the minimum and maximum dates of the consecutive dates of the same name.
I have written the following query that explains the rule:
select date1,name,price
case
when lag(name,1) over(order by date1 ASC,name ASC)=name then lag(minDate,1) over(order by date1 ASC,name ASC)
else date1
end as minDate,
case
when lag(name,1) over(order by date1 DESC,name DESC)=name then lag(maxDate,1) over(order by date1 DESC,name DESC)
else date1
end as maxDate
from MyTable order by date1 ASC,name ASC
My problem is that i get an "invalid context for minDate/maxDate" (SQLCODE=-206, SQLSTATE=42703)
Why can't i refer to a calculated column? Is there any other way?
It's complaining about the lag(maxDate,1), because maxDate isn't defined in that scope; it's not a column in MyTable, and aliases aren't available until after the SELECT list finishes in DB2 (they become available by pushing this into a subquery, or in clauses like HAVING).
Incidentally, your query can be better written as the following:
SELECT date1, name, price,
LAG(date1, 1, date1) OVER(PARTITION BY name ORDER BY date1) AS minDate,
LEAD(date1, 1, date1) OVER(PARTITION BY name ORDER BY date1) AS maxDate
FROM MyTable
ORDER BY date1, name
(I've left out ASC, as it's the default for all ordering)
LEAD(...) is the opposite function to LAG(...), and looks one row ahead. Using both functions this way allows the optimizer to compute just one window (what's specified in the OVER(...)).
The third parameter to the windowing functions here is a default value - in the case there wasn't a next/previous row, it returns date1 from the current row.
PARTITION BY is essentially a grouping for windowing function, and takes the place of the CASE ... WHEN ... in your original query. (In general, I find that such constructs reflect mentality common to imperative programming, which tends to run counter to the set-based nature of SQL. There are usually better ways to do things)

get previous from max value

I have folowing sql query an di want to get previous of max value from table.
select max(card_no),vehicle_number
FROM WBG.WBG_01_01
group by vehicle_number
Through this query i got each maximum card number of each vehicle.But i want to get previouse of that max.For example
if vehicle number has card number 21,19,17,10,5,6,1 and i want to get 19 from max function
Please anyone tell me how can i do this in sql.
Another idea would be to use analytics, something like this:
select
vehicle_number,
prev_card_no
from (
select
card_no,
vehicle_number,
lag(card_no) over
(partition by vehicle_number order by card_no) as prev_card_no,
max(card_no) over
(partition by vehicle_number) as max_card_no
FROM WBG.WBG_01_01
)
where max_card_no = card_no;
Of course, this doesn't take into account your seemingly arbitrary ordering from your question, nor would it work with duplicate maximum numbers.
try this one:
select max(card_no),vehicle_number
FROM WBG.WBG_01_01
where card_no < (Select max(card_no) from WBG.WBG_01_01 group by vehicle_number)
group by vehicle_number