SQL query to allow for latest datasets per items - sql

I have this table in an SQL server database:
and I would like a query that gives me the values of cw1, cw2,cw3 for a restricted date condition.
I would like a query giving me the "latest" values of cw1, cw2, cw3 giving me previous values of cw1, cw2, cw3, if they are null for the last plan_date. This would be with a date condition.
So if the condition is plan_date between "02.01.2020" and "04.01.2020" then the result should be
1 04.01.2020 null, 9, 4
2 03.01.2020 30 , 15, 2
where, for example, the "30" is from the last previous date for item_nr 2.

You can get the last value using first_value(). Unfortunately, that is a window function, but select distinct solves that:
select distinct item_nr,
first_value(cw1) over (partition by item_nr
order by (case when cw1 is not null then 1 else 2 end), plan_date desc
) as imputed_cw1,
first_value(cw2) over (partition by item_nr
order by (case when cw2 is not null then 1 else 2 end), plan_date desc
) as imputed_cw2,
first_value(cw3) over (partition by item_nr
order by (case when cw3 is not null then 1 else 2 end), plan_date desc
) as imputed_cw3
from t;
You can add a where clause after the from.
The first_value() window function returns the first value from each partition. The partition is ordered to put the non-NULL values first, and then order by time descending. So, the most recent non-NULL value is first.
The only downside is that it is a window function, so the select distinct is needed to get the most recent value for each item_nr.

Related

Moving average within groups that returns NULL if any row is NULL (Snowflake - SQL)

I need to caluclate the moving average of a column per group (partitioned by id). The only twist is that I need the result to be NULL if any value in the corresponding window is NULL.
Example of expected behaviour (for a given id and window size=3):
A
mov_ave_A
NULL
NULL
1
NULL
1
NULL
1
1
4
2
The first 3 rows of the moving average are NULL, because the first value (which is included in the first 3 windows) is NULL. Row 4 of mov_ave_A is equal to 1 because it's the average of rows 2 to 4 of A, and so on.
I tried:
CASE WHEN SUM(CASE WHEN a IS NULL THEN 1 ELSE 0 END) = 0 THEN AVG(a) ELSE NULL END
OVER (
PARTITION BY id
ORDER BY date_month
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS mov_ave_A
but I get
"Sliding window frame unsupported for function CASE".
Also, I'd really like the solution to be short and simple as I need to create 6 such columns. So, I'll have to repeat the logic 6 times.
The issue with your query is the OVER clause is after the END. I believe this should work. You need to have the OVER clause for each window function so once for COUNT and once for AVG. COUNT is a easier to way to check for NULL's then using SUM
SELECT
*
,CASE
/*Check for 3 values in a, if so return the rolling AVG value. Implicit ELSE NULL*/
WHEN COUNT(a) OVER (PARTITION BY ID ORDER BY date_month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) = 3
THEN AVG(a) OVER (PARTITION BY ID ORDER BY date_month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)
END AS mov_ave_A
FROM YourTable
Use the following case expression:
CASE WHEN COUNT(a) OVER (
PARTITION BY id
ORDER BY date_month
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) = 3 THEN AVG(a) OVER (
PARTITION BY id
ORDER BY date_month
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) END AS mov_avg

SQL Server find results within partition

I have the following table:
ID Date
-------------------
1 Null
1 1/2/2020
2 Null
2 12/2/2020
3 Null
For every ID which has at least one non-null date, I need to classify as 'accounted'.
Result set should look like below:
id Date AccountFlag
----------------------------
1 Null Accounted
1 1/2/2020 Accounted
2 Null Accounted
2 12/2/2020 Accounted
3 Null Unaccounted
You can use window functions to check if the same id has at least one non-null date, and a case expression to set the flag accordingly. Window aggregate functions come handy for this:
select id, date,
case when max(date) over(partition by id) is not null
then 'Accounted'
ese 'Unaccounted'
end as accountflag
from mytable
max() ignores null values, so it returns null if and only if all values in the partition are null. This would work just the same with min().

Oracle SQL - select last 3 rows after a specific row

Below is my data:
My requirement is to get the first 3 consecutive approvals. So from above data, ID 4, 5 and 6 are the rows that I need to select. ID 1 and 2 are not eligible, because ID 3 is a rejection and hence breaks the consecutive condition of actions. Basically, I am looking for the last rejection in the list and then finding the 3 consecutive approvals after that.
Also, if there are no rejections in the chain of actions then the first 3 actions should be the result. For below data:
So my output should be ID 11, 12 and 13.
And if there are less than 3 approvals, then the output should be the list of approvals. For below data:
output should be ID 21 and 22.
Is there any way to achieve this with SQL query only - i.e. no PL-SQL code?
Here is one method that uses window functions:
Find the first row where there are three approvals.
Find the minimum action_at among the rows with three approvals
Filter
Keep the three rows you want
This version uses fetch which is in Oracle 12+:
select t.*
from (select t.*,
min(case when has_approval_3 = 3 then action_at end) over () as first_action_at
from (select t.*,
sum(case when action = 'APPROVAL' then 1 else 0 end) over (order by action_at rows between current row and 2 following) as has_approval_3
from t
) t
) t
where action = 'APPROVAL' and
(action_at >= first_action_at or first_action_at is null)
order by action_at
fetch first 3 rows only;
You can use IN and ROW_NUMBER analytical function as following:
SELECT * FROM
( SELECT
T.*,
ROW_NUMBER() OVER(ORDER BY Y.ACTION_AT) AS RN
FROM YOUR_TABLE Y
WHERE Y.ACTION = 'APPROVE'
AND Y.ACTION_AT >= COALESCE(
(SELECT MAX(YIN.ACTION_AT)
FROM YOUR_TABLE YIN
WHERE YIN.ACTION = 'REJECT'
), Y.ACTION_AT) )
WHERE RN <= 3;
Cheers!!

If one value is null get previous value in that quarter, in sql select query

I have data as shown below,
Now i want to get result as ,
DateDisplayName Active
Q2(Jun)-2015 736
Q3(Sep)-2015 734
Q4(Dec)-2015 NULL
Q1(Mar)-2016 NULL
So if last month data is null in that quarter then get last but one data.
Ex: in Q3 Active is null for Sep so i shoul show Aug data.
You'd rank your records. Use ROW_NUMBER to give the best record per quarter row number 1 and then only keep those.
select
date_display_name,
active
from
(
select
date_display_name,
active,
row_number() over
(
partition by date_display_name
order by
case when active is null then 2 else 1 end,
defaultdate desc
) as rn
from mytable
) ranked
where rn = 1;

Count of group for null is always 0 (zero)

In TSql what is the recommended approach for grouping data containing nulls?
Example of the type of query:
Select Group, Count([Group])
From [Data]
Group by [Group]
It appears that the count(*) and count(Group) both result in the null group displaying 0.
Example of the expected table data:
Id, Group
---------
1 , Alpha
2 , Null
3 , Beta
4 , Null
Example of the expected result:
Group, Count
---------
Alpha, 1
Beta, 1
Null, 0
This is the desired result which can be obtained by count(Id). Is this the best way to get this result and why does count(*) and count(Group) return an "incorrect" result?
Group, Count
---------
Alpha, 1
Beta, 1
Null, 2
edit: I don't remember why I thought count(*) did this, it may be the answer I'm looking for..
The best approach is to use count(*) which behaves exactly like count(1) or any other constant.
The * will ensure every row is counted.
Select Group, Count(*)
From [Data]
Group by [Group]
The reason null shows 0 instead of 2 in this case is because each cell is counted as either 1 or null and null + null = null so the total of that group would also be null. However the column type is an integer so it shows up as 0.
Just do
SELECT [group], count([group])
GROUP BY [group]
SQL Fiddle Demo
Count(id) doesn't gives the expected result as mentioned in question. Gives value of 2 for group NULL
try this..
Select Group, Count(isNull(Group,0))
From [Data]
Group by [Group]
COUNT(*) should work:
SELECT Grp,COUNT(*)
FROM tab
GROUP BY Grp
One more solution could be following:
SELECT Grp, COUNT(COALESCE(Grp, ' '))
FROM tab
GROUP BY Grp
Here is code at SQL Fiddle