How to do Partition validation inside of partition of a table - sql

I have a table like as below
I need to add a new column called as "FLAG" which is look like as below
The logic behind the FLAG column is
Join_date<= sys_assignment then i need to give FLAG "Y" for the minimum sys_assignment date and remaining as "N" (ex: 101 and 103 records)
If join_date> sys_assignment (if any partition satisfy this condition, in this example 102 and 104 ) consider only those records (sub partition) and give FLAG as "Y" for the maximum value of sys_assignment and remaining all are "N" (The sub partitions are highlighted in THICK colors)
Please help me on this..!!!!!

Below is for BigQuery Standard SQL
#standardSQL
SELECT empid, join_date, sys_assignment,
IF((option AND min_flag) OR (NOT option AND NOT grp AND max_flag), 'Y', 'N') flag
FROM (
SELECT *,
join_date <= sys_assignment grp,
COUNT(1) OVER(PARTITION BY empid) = COUNTIF(join_date <= sys_assignment) OVER(PARTITION BY empid) option,
sys_assignment = MIN(sys_assignment) OVER(PARTITION BY empid, join_date <= sys_assignment) min_flag,
sys_assignment = MAX(sys_assignment) OVER(PARTITION BY empid, join_date > sys_assignment) max_flag
FROM `project.dataset.table`
)
when applied to your sample data - above query produces below result (which looks to me exactly what is expected)

You can use row_number():
select t.*,
(row_number() over (partition by empid
order by (case when join_date < sys_assignment then 1 else 2 end),
(case when join_date < sys_assignment then sys_assignment end) asc,
(case when join_date < sys_assignment then NULL else sys_assignment end) desc
) = 1
) as flag
from t;
The flag here is represented as a boolean rather than as character, which is more appropriate for BigQuery.

Probably could do it with partitioning, but I find it easier to read this way.
Look if there is no record with a higher sys_assignment to get the lowest. Then look to see there is another record to No the single case.
Try this:
update mytable
set flag=case when not exists (select 'x' from mytable t where t.join_date=mytable.join_date and t.sys_assignment<mytable.sys_assignment)
and exists (select 'x' from mytable t where t.join_date=mytable.join_date and t.sys_assignment>mytable.sys_assignment)
then 'Y' else 'N' end

Related

SQL create flag based on earliest/latest date

I have a data set with the following attributes:
- IDs are not unique and has multiple rows
- Each ID has a different date called 'Start Date'
I am trying to add a flag (Y/N) to determine which ID row to use, based on the earliest date.
This is what I have so far:
SELECT *,
min(Start_Date) OVER (PARTITION BY ID) AS FirstEntryFlag,
From `table`
Could someone please give me guidance on how I would achieve this? Thankyou
Is this what you want?
select (case when start_date = min(Start_Date) OVER (PARTITION BY ID)
then 1 else 0
end) as FirstEntryFlag
from t;
If the start date has duplicates for an id and you want only one row flagged, use row_number():
select (case when 1 = row_number() over (partition by id order by Start_Date)
then 1 else 0
end) as FirstEntryFlag
from t;
Finally, some databases support boolean types, so the case is not necessary. Just the conditional expression can return a valid value.

Oracle SQL Look through column, if condition fulfill, compare dates and insert value to column

need help with oracle sql code. I want my code able to do something like this:
Look for Event:TS-0068 then take the Task Number value '55', then look for next '55' with 'EVENT:BC-0050' and compare both date.If Event:TS-0068,date A smaller than Event:BC-0050,Date B then return string "Overhead" at column Condition, else Null or blank will do. I include test data here in link
Dummy Data
Your conditions translate directly into analytic functions and case expressions:
select t.*,
(case when text2 = 'EVENT:TS-0068' and TASK_NUMBER = 55 and
row_number() over (partition by text2, task_number order by starttime) = 1 and
min(case when text2 = 'EVENT:BC-0050' then starttime end) over (order by starttime desc) > starttime
then 'Overhead'
end) as condition
from t
order by starttime;
Here is a db<>fiddle.
Determine the first record with text2='EVENT:TS-0068' when ordered by the start time through use of dense_rank() analytic function in the first query.
Then take only the records with returned dense_rank values equal to 1 in order to create condition column by use of a correlated subquery to scan whether there exists at least one record for text2='EVENT:BC-0050' exceeding starttime value those are for text2='EVENT:TS-0068' :
WITH t AS
(
SELECT nvl(case
when text2 = 'EVENT:TS-0068' then
dense_rank() over (partition by text2 order by starttime)
end, 0) as dr,
t.*
FROM tab t
ORDER BY starttime
)
SELECT starttime, text2, task_number,
case
when dr = 1 then
( select nvl2( max(starttime), 'Overhead', null )
from t t1
where t.starttime < t1.starttime
and text2 = 'EVENT:BC-0050')
end as condition
FROM t
ORDER BY starttime;
Demo

Update Flag Based On Change of Previous Value

I have below table .Need sql ,If there is change in INPUT value then update FLAG to 1 else 0.
INPUT START_DATE PERSON_ID FLAG
42707 2017-01-01 227317 0
40000 2018-01-01 227317 1
42400 2019-01-01 227317 1
42400 2019-01-02 227317 0
You can use lag() :
select t.*,
(case when lag(input, 1, input) over (partition by person_id order by start_date) = input
then 0 else 1
end) as FLAG
from table t;
If you want this in a query, then use row_number():
select t.*,
(case when row_number() over (partition by person_id order by start_date) = 1
then 0 else 1
end) as flag
from t;
If the input_value could be the same on different rows, then use first_value():
select t.*,
(case when value <> first_value(input) over (partition by person_id order by start_date) = 1
then 0 else 1
end) as flag
from t;
Either form could be incorporated into an update using an updatable CTE if you want to update the table.
EDIT:
If you want to know if the value changes from one row to the "next", then use lag(). In an update, this looks like:
with toupdate as (
select t.*,
lag(input) over (partition by customerid order by date) as prev_input
from t
)
update toupdate
set flag = (case when prev_input <> input then 1 else 0 end);
That said, I would not advise you to store the data in the table. Instead, just put the logic in a select when you need it. Otherwise, the data could get out of date if a historical value is updated.

Organizing SQL data based on date

I am trying to organize my SQL data based off of the dates from which the orders were made.
My data:
SELECT DISTINCT ORDER_NO, ITEM, VERSION_NO,
(CASE WHEN ROW_NUMBER() OVER (PARTITION BY ORDER_NO ORDER BY NOT_BEFORE_DATE
ASC) = 1
THEN 'what-if'
ELSE 'wh'
END) AS VERSION_NEW
,
(CASE WHEN ROW_NUMBER() OVER (PARTITION BY ORDER_NO ORDER BY
NOT_BEFORE_DATE ASC) = 2
THEN 'initial'
ELSE 'other'
END) AS VERSION
FROM FDT_MAPTOOL
WHERE ITEM IN (1032711)
;
My results:
I want my data to be ordered by PO# and the date it was created.
As you can see in my picture the First two line have the same ITEM and same PO (Order_No). I need the first two to say Initial on the side because they are the first two based on the dates. They were created first. Everything after should say other.
I am not sure if PL/SQL is needed for this?
Thank you!
Use a different analytic function so that more than one row can have the value of 1 e.g.
SELECT DISTINCT ORDER_NO, ITEM, VERSION_NO,
(CASE WHEN DENSE_RANK() OVER (PARTITION BY ORDER_NO ORDER BY NOT_BEFORE_DATE
ASC) = 1
THEN 'what-if'
ELSE 'wh'
END) AS VERSION_NEW
,
(CASE WHEN DENSE_RANK() OVER (PARTITION BY ORDER_NO ORDER BY
NOT_BEFORE_DATE ASC) = 1
THEN 'initial'
ELSE 'other'
END) AS VERSION
FROM FDT_MAPTOOL
WHERE ITEM IN (1032711)
;
Either rank() OR dense_rank() should work here instead of row_number()
nb: note sure if you really need "select distinct"

How To Set Row As Null After Meeting A Certain Criteria

I am trying to have "Cumulative Customers" be NULL after the first 5 "Cumulative Customers":
SUM(Customer) OVER (PARTITION BY Product ORDER BY date DESC) cumulative_customers
The final output will look like this:
Use a CASE expression:
SELECT CASE WHEN cumulative_customers < 5
OR cumulative_customers = 5 AND customer >= 1
THEN cumulative_customers END AS cumulative_customers
, ... -- more columns
FROM (
SELECT ... -- your current query here
) sub;
If the ELSE part is missing it defaults to NULL. You can spell that out, too, if you prefer.
I use customer >= 1 just in case there can be values greater than 1 (unlike your demo suggests).
If you don't want a subquery, you can do this using case:
select (case when SUM(Customer) OVER (PARTITION BY Product ORDER BY date DESC) <= 5
then SUM(Customer) OVER (PARTITION BY Product ORDER BY date DESC)
end) as cumulative_customers
Erwin's solution also works if you want a subquery or CTE.