Incremental values when a condition is met in if statement - sql

I've been stuck on this one for quite some time now and I can't figure it out.
Here's my problem:
I have two boolean columns condition_1 and condition_2, and I want to create a third column inc where the value increments each time this condition if condition_2 is false and lead(condition_1) over(partition by column_x order by column_y) is false is met.
The result would look something like that:
column_x column_y condition_1 condition_2 inc
A 12/03/2020 true true 1
A 13/03/2020 true false 1
A 14/03/2020 false false 2
A 15/03/2020 false true 3
A 16/03/2020 true false 3
A 17/03/2020 false true 4
Doing something like
if(condition_2 is false and lead(condition_1) over(partition by column_x order by column_y) is false, lag(inc) over(partition by column_x order by column_y) + 1, lag(inc) over(partition by column_x order by column_y)) inc obv doesn't work since inc doesn't yet exist at the time of the query, and doing
if(condition_2 is false and lead(condition_1) over(partition by column_x order by column_y) is false, + 1, + 0) inc won't be incremental as it will reset to 0 for each row.
Does someone have an idea?
Thanks a lot!

You describe this formula:
select t.*,
countif( (not condition_2) and (not next_1)) over (partition by column_x order by column_y)
from (select t.*,
lead(condition_1) over (partition by column_x order by column_y) as next_1
from t
) t;
If you want the numbers to start at 1, then you need to add "1" to the value.

Related

What happens when I pass an CASE expression to the LAST_VALUE function

My LAST_VALUE function looks somethin like this
LAST_VALUE(
CASE
WHEN statement_1 then 0
WHEN statement_2 then 1
WHEN statement_3 then 0
ELSE NULL
END IGNORE NULLS) OVER (PARTITION BY column1 ORDER BY column2)
Can someone explains what value is the LAST_VALUE supposed to return if there is expression.
I understand what happens when a column is passed, but incase of such expressions no clue whatsoever.
You can find the description and examples at https://docs.oracle.com/cd/E11882_01/server.112/e41084/functions085.htm#SQLRF00655
Regards...
Btw. the CASE ... END structure is just as you are selecting values from the table field but with If condition. It doesn't affect the LAST_VALUE function.
It is something like this:
CASE WHEN ID = 1 THEN 10 WHEN ID = 2 THEN 20 ELSE 99 END
where ID = 1 is Statement1, ID = 2 is Statement2 ... and so on..
The same as if your case expression were itself a column...
SELECT
*,
LAST_VALUE(new_column IGNORE NULLS)
OVER (PARTITION BY column1
ORDER BY column2
)
FROM
(
SELECT
*,
CASE
WHEN statement_1 then 0
WHEN statement_2 then 1
WHEN statement_3 then 0
ELSE NULL
END
AS new_column
FROM
your_table
)
sub_query

Oracle SQL -- Pick Max Value from RN Function but update all fields with that value

My query is as follows
SELECT HEADER_TABLE.SEGMENT1,
LINES_TABLE.LINE_NUM,
CASE
WHEN ( HEADER_TABLE.REVISION_NUM = '0'
AND HEADER_TABLE.PRINT_COUNT = '0')
THEN
'Unavailable'
ELSE
NVL (ACK_TABLE.ACK_TYPE, 'Absent')
END
AS X_ACK_TYPE,
ACK_TABLE.GXS_DATE
FROM HEADER_TABLE,
LINES_TABLE,
(SELECT po_number,
po_line_number,
gxs_date,
po_ack_filename,
ack_type
FROM (SELECT po_number,
po_line_number,
gxs_date,
po_ack_filename,
ack_type,
ROW_NUMBER ()
OVER (PARTITION BY po_number ORDER BY gxs_date DESC)
rn
FROM xxcmst_po_ack_from_gxs_stg)
WHERE rn = 1) ACK_TABLE,
(SELECT PO_NUMBER FROM XXCMST.XXCMST_ACTION_TABLE_ACKNOWLEDGEMENT) ACTION_TABLE
WHERE HEADER_TABLE.PO_HEADER_ID = LINES_TABLE.PO_HEADER_ID
AND HEADER_TABLE.SEGMENT1 = ACK_TABLE.PO_NUMBER(+)
AND HEADER_TABLE.SEGMENT1 = ACTION_TABLE.PO_NUMBER(+)
AND LINES_TABLE.LINE_NUM = ACK_TABLE.PO_LINE_NUMBER(+)
AND HEADER_TABLE.SEGMENT1 = '100';
This is giving me 6 records with 1 GXS_DATE and X_ACK_TYPE = 'Absent'. The RN function is needed here to pull 1 record only from the subquery but the requirement is to have all the 6 records have the same date and ACK_TYPE which is not happening. How can I achieve this? Please refer to the below screenshot and I need X_ACK_TYPE = AK for all the 6 LINE_NUMs and GXS_DATE = 3/6/2020 for all these 6 records.
My current data screenshot here
Instead of
ACK_TABLE.GXS_DATE
in SELECT clause use the LAG function as follows:
CASE WHEN ACK_TABLE.GXS_DATE IS NOT NULL
THEN ACK_TABLE.GXS_DATE
ELSE LAG(ACK_TABLE.GXS_DATE IGNORE NULLS)
OVER (PARTITION BY HEADER_TABLE.SEGMENT1 ORDER BY LINES_TABLE.LINE_NUM )
END AS GXS_DATE
or If there will be always one value of ACK_TABLE.GXS_DATE exists per HEADER_TABLE.SEGMENT1 then you can simply write it as
MIN(ACK_TABLE.GXS_DATE)
OVER (PARTITION BY HEADER_TABLE.SEGMENT1) AS GXS_DATE
-- Update --
for ACK_TYPE, You need to apply the same logic in ELSE portion of your CASE statement from the original query as follows:
Replace this:
ELSE
NVL (ACK_TABLE.ACK_TYPE, 'Absent')
END
With this:
ELSE
NVL (MIN(ACK_TABLE.ACK_TYPE)
OVER (PARTITION BY HEADER_TABLE.SEGMENT1), 'Absent')
END

Count text values across columns in SQL using CASE WHEN expressions

I have a table where I am determining whether a person's ID number exists across multiple databases. If the ID exists in only one database, then I would like to add another column that labels the person as "UNIQUE"; otherwise, it should be labeled as "NOT UNIQUE".
My query thus far is set up like this:
/* CTE that creates a long column of all distinct PersonID's across three databases */
WITH cte as
(SELECT DISTINCT t1.*
FROM
(SELECT PersonID FROM DB_1.dbo.Persons
UNION
SELECT PersonID FROM DB_2.dbo.Persons
UNION
SELECT PersonID FROM DB_3.dbo.Persons)
t1)
/* Use CASE WHEN statements to check if Person exists in three other tables in DB_1, DB_2, and DB_3 */
SELECT PersonID,
CASE WHEN PersonID IN (SELECT PersonID FROM DB_1.dbo.Table_1
UNION
SELECT PersonID FROM DB_1.dbo.Table_2
UNION
SELECT PersonID FROM DB_1.dbo.Table_3)
THEN 'TRUE'
ELSE 'FALSE'
END AS IN_DB_1,
CASE WHEN PersonID IN (SELECT PersonID FROM DB_2.dbo.Table_1
UNION
SELECT PersonID FROM DB_2.dbo.Table_2
UNION
SELECT PersonID FROM DB_2.dbo.Table_3)
THEN 'TRUE'
ELSE 'FALSE'
END AS IN_DB_2,
CASE WHEN PersonID IN (SELECT PersonID FROM DB_3.dbo.Table_1
UNION
SELECT PersonID FROM DB_3.dbo.Table_2
UNION
SELECT PersonID FROM DB_3.dbo.Table_3)
THEN 'TRUE'
ELSE 'FALSE'
END AS IN_DB_3
FROM cte
The results look like this:
PersonID IN_DB_1 IN_DB_2 IN_DB_3
---------|----------|----------|----------|
001 TRUE FALSE FALSE
002 FALSE TRUE TRUE
003 TRUE FALSE FALSE
004 FALSE TRUE FALSE
005 TRUE FALSE TRUE
As can be seen, PersonID numbers 001, 003, and 004 appear only in one database.
I would like to add a fifth column called "PID_UNIQUE" that counts the number of "TRUE" text values across the columns and specifies whether the person is unique.
It should look like this:
PersonID IN_DB_1 IN_DB_2 IN_DB_3 PID_UNIQUE
---------|----------|----------|----------|-----------|
001 TRUE FALSE FALSE UNIQUE
002 FALSE TRUE TRUE NOT UNIQUE
003 TRUE FALSE FALSE UNIQUE
004 FALSE TRUE FALSE UNIQUE
005 TRUE FALSE TRUE NOT UNIQUE
I assume this would be set up using another CASE WHEN expression. I am a little stuck as to how I could write that out to count across the three "IN_DB_no" columns.
I tried this:
CASE WHEN COUNT('TRUE') = 1
THEN 'UNIQUE'
ELSE 'NOT UNIQUE'
END AS PID_UNIQUE
However, it returned a column where all records were unique, which is not what I need.
I have a table where I am determining whether a person's ID number exists across multiple databases.
Your sample query references many more tables than this suggests. Hence, it seems much more complicated than necessary.
Let me assume that there are really three tables, one in each database. I see just an aggregation after UNION ALL:
SELECT PersonID, MAX(in_1), MAX(in_2), MAX(in_3),
(CASE WHEN MAX(in_1) + MAX(in_2) + MAX(in_3) = 1 THEN 'UNIQUE'
ELSE 'NOT UNIQUE'
END) as pid_Unique
FROM ((SELECT DISTINCT PersonID, 1 as in_1, 0 as in_2, 0 as in_3
FROM DB_1.dbo.Persons
) UNION ALL
(SELECT DISTINCT PersonID, 0 as in_1, 1 as in_2, 0 as in_3
FROM DB_2.dbo.Persons
) UNION ALL
(SELECT DISTINCT PersonID, 0 as in_1, 0 as in_2, 1 as in_3
FROM DB_3.dbo.Persons
)
) p
GROUP BY PersonId;
I figured out a solution that works for me using the CROSS APPLY operator, along with a CASE / WHEN expression.
Basically, I added an additional column to the table I already made.
The query looked like this:
SELECT * FROM My_New_DB.dbo.My_New_Tbl
CROSS APPLY (
SELECT CASE WHEN 1 = (SELECT COUNT(*)
FROM (VALUES (IN_DB_1), (IN_DB_2), (IN_DB_3)) C (Val)
WHERE Val = 'TRUE')
THEN 'UNIQUE'
ELSE 'NOT UNIQUE'
END AS UNIQUE_ID
) A
Simply put, when 1 = 1, it is unique.

Return 1 Record if All are False for ID

I am trying to come up with a query that returns a list of parts where all of the flags for that one part are false, and the returned list should only have one record returned for that part.
Part Revision Flag
Part1| A | true
Part1| B | false
Part2| C | false
Part2| D | false
Part2| E | false
Part3| A | true
Part4| F | false
Output:
Part Revision Flag
Part2| C(or any other revision) | false
Part4| F | false
Part1 and Part3 have at least 1 flag set to true so they don't get outputted. Part2 and 4 have no flags at all set to true, and Part2 only returns 1 row for all of them.
SELECT DISTINCT [PartNum],[RevisionNum],[Flag]
FROM [Prod].[Erp].[PartRev]
WHERE RevisionNum is not null AND Approved = 0
ORDER BY PartNum
GROUP BY [PartNum],[RevisionNum],[Flag]
So far I can get one row per part number, but it will return just one row even if one of the others for that part number is true.
What gets returned:
Part Revision Flag
Part1| B | false
Part2| C | false
Part4| F | false
Does anyone know how to fix this?
Another option
Select Part
,Revision = max(Revision)
,Flag = max(flag)
from YourTable
Group By Part
Having max(Flag)='false'
Returns
Part Revision Flag
Part2 E false
Part4 F false
First, select all of the records that have flag = true
SELECT DISTINCT Part FROM PartRev WHERE flag = 1 --or whatever your RDBMS uses for a true value
Then, select all the distinct records that are not included in the first query. So, the query would look like this
SELECT Part, MIN(Revision), flag
FROM PartRev WHERE Part NOT IN (
SELECT DISTINCT Part FROM PartRev WHERE flag = 1
)
GROUP BY Part, flag
You can do this with a WHERE NOT EXISTS to not pull any records that have a true flag.
It also looks like you're wanting to pull the most recent revision for the part as the record returned, which can be accomplished via a ROW_NUMBER() and selecting just the most recent record.
;With Cte As
(
Select *,
Row_Number() Over (Partition By Part Order By Revision Desc) As RN
From [Prod].[Erp].[PartRev] P1
Where RevisionNum is not null
And Approved = 0
And Not Exists
(
Select *
From [Prod].[Erp].[PartRev] P2
Where P1.Part = P2.Part
And P2.Flag = 'true'
)
)
Select Part, Revision, Flag
From Cte
Where RN = 1
How about something like this, as a more general formulation, because the case statement can be made quite complex:
SELECT
SP.Part
, SP.Revision
, SP.Flag
FROM (
SELECT
Part
, Count(Part) as TC
, SUM( CASE Flag WHEN 1 THEN 0 ELSE 1 END ) FC
, MAX(Revision) AS Revision
, MAX(flag) AS Flag
FROM test
GROUP BY Part
) SP
WHERE SP.TC=SP.FC

How to group by one column and return ifnull for the other column

Example Data:
event_id, session_id, did_tap_on_screen
1 1 true
2 1 false
3 2 false
4 2 false
Desired Results:
session_id, did_tap_on_screen
1 true
2 false
Basically, I want to group by the session_id and return true for did_tap_on_screen if any is true and return false if all are false
This is the pseudo query I wrote:
SELECT
session_id,
did_tap_on_screen -- what should I do here
FROM events
GROUP BY session_id
If did_tap_on_screen is a string, you could do:
select session_id, max(did_tap_on_screen)
from events
group by session_id;
If it is a boolean (of some type), then the appropriate formulation depends on the database. For instance, Postgres supports bool_or()
select session_id, bool_or(did_tap_on_screen)
from events
group by session_id;
Here's a way to determine whether at least one did_tap_on_screen per session_id is true:
select session_id,
count(case when did_tap_on_screen then 1 end) > 0
from events
group by session_id