SQL row_number() with conditions - sql

I have the following information in SQL Server table:
How can I add C1-C2-C3-C4 columns? To do this each colum has different conditions. I'm using row_number() order by id_pv desc, but it doesn't work.

I think you can do this with nested case statements -- both in the partition by clause and outside the row_number(). For the first column:
select t.*,
(case when expiry_date > #somdate and
row_number() over (partition by cod_suc, cod_ramo,
(case when expiry_date > #somdate then 1 else 0 end)
order by id_pv desc) as col1
then 1 else 0
end)
from table t;

Assuming from your example you want it to place 0 when your conditions are not met, and the row number otherwise, try:
Select [your columns]
, case when ExpiryDate >= #someDate then row_number()
over (order by [list of columns])
else 0 end as c1
, case when ExpiryDate >= #someDate and Cod_grupo = 4 then row_number()
over (order by [other list of columns])
else 0 end as c2

Related

Displaying a multiple columns in single row in SQL

SELECT * FROM
(select ID,POLICY_ID,CONTACT_ID,POLICY_CONTACT_ROLE,ROW_NUMBER () OVER (PARTITION BY POLICY_ID,POLICY_CONTACT_ROLE ORDER BY ID ) AS ORDERNO
FROM P_POL_HEADER_CONTACT ) SUB
WHERE SUB.ORDERNO=1 AND POLICY_ID =20001
I want to arrange the above query to take result like this
Thanks
You need to pivot on a row-number.
You can use PIVOT, but I often find a simple GROUP BY/MAX is simpler, especially when you need to rename the columns.
SELECT
SUB.POLICY_ID
,MAX(CASE WHEN POLICY_CONTACT_ROLE_NO = 1 THEN POLICY_CONTACT_ROLE END) AS POLICY_CONTACT_ROLE
,MAX(CASE WHEN POLICY_CONTACT_ROLE_NO = 2 THEN POLICY_CONTACT_ROLE END) AS POLICY_CONTACT_ROLE2
,MAX(CASE WHEN POLICY_CONTACT_ROLE_NO = 3 THEN POLICY_CONTACT_ROLE END) AS POLICY_CONTACT_ROLE3
-- etc etc
FROM (
SELECT *,
ROW_NUMBER () OVER (PARTITION BY POLICY_ID ORDER BY POLICY_CONTACT_ROLE) AS POLICY_CONTACT_ROLE_NO
FROM (
SELECT *
ROW_NUMBER () OVER (PARTITION BY POLICY_ID, POLICY_CONTACT_ROLE ORDER BY ID ) AS ORDERNO
FROM P_POL_HEADER_CONTACT
WHERE POLICY_ID = 20001
) SUB
WHERE SUB.ORDERNO = 1
) SUB
GROUP BY SUB.POLICY_ID
You want conditional aggregation. I think the logic you want is:
SELECT PCH.POLICY_ID,
MAX(CASE WHEN SEQNUM = 1 THEN POLICY_CONTACT_ROLE END) as POLICY_CONTACT_ROLE_1,
MAX(CASE WHEN SEQNUM = 2 THEN POLICY_CONTACT_ROLE END) as POLICY_CONTACT_ROLE_2,
MAX(CASE WHEN SEQNUM = 3 THEN POLICY_CONTACT_ROLE END) as POLICY_CONTACT_ROLE_3
FROM (SELECT PHC.*,
ROW_NUMBER() OVER (PARTITION BY POLICY_ID ORDER BY ID ) AS SEQNUM
FROM P_POL_HEADER_CONTACT PHC
) PHC
WHERE POLICY_ID = 20001
GROUP BY POLICY_ID;
I'm not sure what your filtering on ORDERNO is really doing. If you have duplicates, use DENSE_RANK() instead of ROW_NUMBER(). However, without knowing what your data looks like, I am guessing that is unnecessary.
Exclude CONTACT_ID from query. And use STUFF query to convert your rows into columns

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.

How to do Partition validation inside of partition of a table

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

SQL Server Select most recent record (with a twist)

Suppose I have the following table:
ActionDate ActionType
------------ ------------
2018-08-02 12:59:56.000 Drill
2018-08-02 13:20:45.000 Hammer
2018-08-02 14:36:02.000 Drill
I want to select the most recent ActionType based on the ActionDate. This is not a problem using ROW_NUMBER() OVER syntax and either grabbing the first or last record depending on how I sorted. However consider this table setup:
ActionDate ActionType
------------ ------------
2018-08-02 12:59:56.000 Drill
2018-08-02 13:20:45.000
2018-08-02 14:36:02.000 Drill
In this case, since the only action listed is Drill, I want the oldest occurrence, since the Action didn't actually change. Is there a way to satisfy both requirements at the same time?
You can use TOP 1 WITH TIES with a CASE statement.
select top 1 with ties
*
from YourTable
order by
case
when (select count(distinct ActionType) from #table) = 1
then row_number() over (order by ActionDate asc)
else row_number() over (order by ActionDate desc)
end
Or in a subquery if you like that better...
select ActionDate, ActionType
from
(select
*,
RN = case
when (select count(distinct ActionType) from #table) = 1
then row_number() over (order by ActionDate asc)
else row_number() over (order by ActionDate desc)
end
from YourTable) x
where RN = 1
This assume the blank is actually a NULL which is ignored in COUNT DISTINCT. If that is a blank space instead of NULL then you need to handle that with an additional CASE or IIF or whatever like this:
select top 1 with ties
*
from YourTable
order by
case
when (select count(distinct case when ActionType = '' then null else ActionType end) from #table) = 1
then row_number() over (order by ActionDate asc)
else row_number() over (order by ActionDate desc)
end

OVER clause. How to order by multiple columns within a CASE clause?

The following SQL code is a sample of larger SQL statement. My question is how can I do CASE multiple column ORDER BY in the SELECT ROW_NUMBER(), similar to the one below comment line. The presented code works, but I need to order by two columns.
I am using MSSQL 2008
SELECT TOP(50)
ROW_NUMBER() OVER(ORDER BY CASE WHEN #OrderBy = 'Total' THEN SUM(TotalViews) ELSE SUM(LastMonthViews) END DESC) AS Position
,SUM(Albums.TotalViews) AS TotalViews
,SUM(Albums.LastMonthViews) AS LastMonthViews
FROM Albums
--The code to be implemented in the SELECT ROW_NUMBER()
ORDER BY SUM(LastMonthViews) DESC, SUM(TotalViews) DESC
You can repeat the case statement in the row_number() partitioning clause:
SELECT TOP(50)
ROW_NUMBER() OVER (ORDER BY (CASE WHEN #OrderBy = 'Total' THEN SUM(LastMonthViews) ELSE SUM(LastMonthViews) END) DESC,
(CASE WHEN #OrderBy = 'Total' THEN SUM(TotalViews) ELSE SUM(LastMonthViews) END) DESC
) AS Position
,SUM(Albums.TotalViews) AS TotalViews
,SUM(Albums.LastMonthViews) AS LastMonthViews
FROM Albums;