Picking first instance in grouped rows- PostgreSQL - sql

Here's the background, I have a set of rows:
I only want to get the first ADVANCE after every RECOVERY in the type column per accountid
so I the result should be the advances with procdates
2015-09-03 09:55:12.228343
2015-09-04 23:10:42.016903
Is this possible in one query?

Try this:
WITH CTE_PrevType
AS (
SELECT accountid
,procdate
,LAG(type, 1, NULL) OVER (
ORDER BY procdate
) AS PreviousType
FROM < Table_Name >
)
SELECT
accountid
,procdate
,type
,value
FROM < Table_Name > AS TN
INNER JOIN CTE_PrevType CPT
ON TN.accountid = CPT.accountid
AND TN.procdate = CPT.procdate
WHERE TN.type = 'ADVANCE'
AND CPT.type = 'RECOVERY'

I Have found on how to get it!
SELECT * FROM
(SELECT accountid,
procdate,
type,
value,
LAG(type) OVER (ORDER BY procdate ASC) AS previousType
FROM transaction) w WHERE previousType = 'RECOVERY';

Related

Conditional return values in SQL

I have started a simple auction system where each row contains the following data:
Type:= BID|WIT
ProductName := Str
User := Str
Value := Decimal
CreatedAt := datetime
*Here WIT means withdraw, aka quit from item auction *
A user can naturally do multiple bid requests, always raising the bid value (this is handled at language level)
I would like to list all top bids from all users but under a condition, only if they are not before a WITHDRAW request for the given item.
For example, given the entries
BID,Doll,John,10,2021-11-26 10:10
BID,Ball,John,12,2021-11-26 10:11
BID,Doll,Mary,12,2021-11-26 10:12
BID,Doll,Alice,13,2021-11-26 10:13
BID,Doll,Mary,14,2021-11-26 10:14
BID,Doll,Alice,17,2021-11-26 10:14
BID,Ball,Mary,14,2021-11-26 10:14
WIT,Doll,John,00,2021-11-26 10:16
BID,Doll,Mary,20,2021-11-26 10:18
BID,Ball,John,15,2021-11-26 10:20
If I magic_sql_query(Doll) I would like to get
BID,Doll,Alice,17,2021-11-26 10:14
BID,Doll,Mary,20,2021-11-26 10:18
Also If I magic_sql_query(Ball) I would like to get:
BID,Ball,Mary,14,2021-11-26 10:14
BID,Ball,John,15,2021-11-26 10:20
How can I do it in a SQL Statement?
You can
use the row_number() windowing function to rank within a group (a
group is defined by the username and the product); the latest entry
gets rank 1
get rid of all entries whose rank is > 1 (i.e. the user has a later entry for this product)
get rid of all entries of type 'WIT'
with base as (select
b.type,
b.productname,
b.username,
b.value,
b.createdat,
row_number() over (partition by productname, username
order by createdat desc) as rn
from bids b
order by productname, username, createdat
)
select * from base
where rn = 1
and type = 'BID';
SQL Fiddle
To find all bids that have no withdrawal later (by the same user for the same product) you can use a NOT EXITS condition:
select a1.*
where a1.product_name = 'Ball'
and type = 'BID'
and not exists (select *
from auction a2
where a2.product_name = a1.product_name
and a2.user_name = a1.user_name
and a2.type = 'WIT'
and a2.created_at > a1.created_at)
Now we need to filter out the highest bids per product and user. This can be done using the dense_rank() function.
select type, product_name, user_name, value, created_at
from (
select a1.*, dense_rank() over (partition by product_name, user_name order by value desc) as rnk
from auction a1
where a1.product_name = 'Ball'
and type = 'BID'
and not exists (select *
from auction a2
where a2.product_name = a1.product_name
and a2.user_name = a1.user_name
and a2.type = 'WIT'
and a2.created_at > a1.created_at)
) t
where rnk = 1
order by created_at;
Online example
A left join version
select t.Type, product_name, t.User, Value, CreatedAt
from (
select b.*
, row_number() over(partition by b.product_name, b.user order by b.createdAt desc) rn
from auction b
left join auction w
on w.type = 'WIT'
and b.product_name = w.product_name
and b.user = w.user
and w.createdAt > b.createdAt
where b.type = 'BID'
and w.product_name is null
-- and b.product_name = 'Ball'
) t
where rn=1
-- and product_name = 'Ball'
order by product_name, CreatedAt;
uncomment one of .. product_name = 'Ball' to get only this product.
db<>fidlle
You can use MAX() window function to get for each user the last entry with type = 'BID' and the last entry with type = 'WIT'.
Then filter the results:
SELECT type, productname, "user", value, createdat
FROM (
SELECT *,
MAX(CASE WHEN type = 'BID' THEN createdat END) OVER (PARTITION BY "user") last_bid,
MAX(CASE WHEN type = 'WIT' THEN createdat END) OVER (PARTITION BY "user") last_wit
FROM tablename
WHERE productname = ?
) t
WHERE (last_wit IS NULL OR last_wit < last_bid) AND createdat = last_bid;
Replace ? with the name of the product that you want.
See the demo.

Distinct keyword not fetching results in Oracle

I have the following query where I unique records for patient_id, meaning patient_id should not be duplicate. Each time I try executing the query, seems like the DB hangs or it takes hours to execute, I'm not sure. I need my records to load quickly. Any quick resolution will be highly appreciated.
SELECT DISTINCT a.patient_id,
a.study_id,
a.procstep_id,
a.formdata_seq,
0,
(SELECT MAX(audit_id)
FROM audit_info
WHERE patient_id =a.patient_id
AND study_id = a.study_id
AND procstep_id = a.procstep_id
AND formdata_seq = a.formdata_seq
) AS data_session_id
FROM frm_rg_ps_rg a,
PATIENT_STUDY_STEP pss
WHERE ((SELECT COUNT(*)
FROM frm_rg_ps_rg b
WHERE a.patient_id = b.patient_id
AND a.formdata_seq = b.formdata_seq
AND a.psdate IS NOT NULL
AND b.psdate IS NOT NULL
AND a.psresult IS NOT NULL
AND b.psresult IS NOT NULL) = 1)
OR NOT EXISTS
(SELECT *
FROM frm_rg_ps_rg c
WHERE a.psdate IS NOT NULL
AND c.psdate IS NOT NULL
AND a.psresult IS NOT NULL
AND c.psresult IS NOT NULL
AND a.patient_id = c.patient_id
AND a.formdata_seq = c.formdata_seq
AND a.elemdata_seq! =c.elemdata_seq
AND a.psresult != c.psresult
AND ((SELECT (a.psdate - c.psdate) FROM dual)>=7
OR (SELECT (a.psdate - c.psdate) FROM dual) <=-7)
)
AND a.psresult IS NOT NULL
AND a.psdate IS NOT NULL;
For start, you have a cartesian product with PATIENT_STUDY_STEP (pss).
It is not connected to anything.
select *
from (select t.*
,count (*) over (partition by patient_id) as cnt
from frm_rg_ps_rg t
) t
where cnt = 1
;

Update table with another column in the same table

I have a table like this
Test_order
Order Num Order ID Prev Order ID
987Y7OP89 919325 0
987Y7OP90 1006626 919325
987Y7OP91 1029350 1006626
987Y7OP92 1756689 0
987Y7OP93 1756690 0
987Y7OP94 1950100 1756690
987Y7OP95 1977570 1950100
987Y7OP96 2160462 1977570
987Y7OP97 2288982 2160462
Target table should be like below,
Order Num Order ID Prev Order ID
987Y7OP89 919325 0
987Y7OP90 1006626 919325
987Y7OP91 1029350 1006626
987Y7OP92 1756689 1029350
987Y7OP93 1756690 1756689
987Y7OP94 1950100 1756690
987Y7OP95 1977570 1950100
987Y7OP96 2160462 1977570
987Y7OP97 2288982 2160462
987Y7OP97 2288900 2288982
Prev Order ID should be updated with the Order ID from the previous record from the same table.
I'm trying to create a dummy data set and update..but it's not working..
WITH A AS
(SELECT ORDER_NUM, ORDER_ID, PRIOR_ORDER_ID,ROWNUM RID1 FROM TEST_ORDER),B AS (SELECT ORDER_NUM, ORDER_ID, PRIOR_ORDER_ID,ROWNUM+1 RID2 FROM TEST_ORDER)
SELECT A.ORDER_NUM,B.ORDER_ID,A.PRIOR_ORDER_ID,B.PRIOR_ORDER_ID FROM A,B WHERE RID1 = RID2
You could use Oracles Analytical Functions (also called Window functions) to pick up the value from the previous order:
UPDATE Test_Order
SET ORDERID = LAG(ORDERID, 1, 0) OVER (ORDER BY ORDERNUM ASC)
WHERE PrevOrderId = 0
See here for the documentation on LAG()
In sql-server you cannot use window function in update statement, not positive but don't think so in Oracle either. Anyway to get around that you can just update a cte as follows.
WITH cte AS (
SELECT
*
,NewPreviousOrderId = LAG(OrderId,1,0) OVER (ORDER BY OrderNum)
FROM
TableName
)
UPDATE cte
SET PrevOrderId = NewPreviousOrderId
And if you want to stick with the ROW_NUMBER route you were going this would be the way of doing it.
;WITH cte AS (
SELECT
*
,ROW_NUMBER() OVER (ORDER BY OrderNum) AS RowNum
FROM
TableName
)
UPDATE c1
SET PrevOrderId = c2.OrderId
FROM
cte c1
INNER JOIN cte c2
ON (c1.RowNum - 1) = c2.RowNum

Filter rows by count of two column values

I have following table:
Card(
MembershipNumber,
EmbossLine,
status,
EmbossName
)
with sample data
(0009,0321,'E0','Finn')
(0009,0322,'E1','Finn')
(0004,0356,'E0','Mary')
(0004,0398,'E0','Mary')
(0004,0382,'E1','Mary')
I want to retrieve rows such that only those rows should appear that have count of MembershipNumber > 1 AND count of status='E0' > 1.
For Example The query should return following result
(0004,0356,'E0','Mary')
(0004,0398,'E0','Mary')
I have the query for filtering it with MembershipNumber count but cant figure out how to filter by status='E0'. Here's the query so far
SELECT *
FROM (SELECT *,
Count(MembershipNumber)OVER(partition BY EmbossName) AS cnt
FROM card) A
WHERE cnt > 1
You can just add WHERE status = 'E0' inside your subquery:
SQL Fiddle (credit to Raging Bull for the fiddle)
SELECT *
FROM (
SELECT *,
COUNT(MembershipNumber) OVER(PARTITION BY EmbossName) AS cnt
FROM card
WHERE status = 'E0'
)A
WHERE cnt > 1
You can do it this way:
select t1.*
from card t1 left join
(select EmbossName
from card
where [status]='E0'
group by EmbossName,[status]
having count(MembershipNumber)>1 ) t2 on t1.EmbossName=t2.EmbossName
where t2.EmbossName is not null and [status]='E0'
Result:
MembershipNumber EmbossLine status EmbossName
---------------------------------------------------
4 356 E0 Mary
4 398 E0 Mary
Sample result in SQL Fiddle
try :
WITH cnt AS (
SELECT MembershipNumber, status
FROM Card
WHERE status = 'E0'
GROUP BY MembershipNumber, status
HAVING COUNT(MembershipNumber) > 1 AND COUNT(status) > 1
)
SELECT c.*
FROM Card c
INNER JOIN cnt
ON c.MembershipNumber = cnt.MembershipNumber
AND c.status = cnt.status;
You can try this:
DECLARE #DataSource TABLE
(
[MembershipNumber] SMALLINT
,[EmbossLine] SMALLINT
,[status] CHAR(2)
,[EmbossName] VARCHAR(8)
);
INSERT INTO #DataSource ([MembershipNumber], [EmbossLine], [status], [EmbossName])
VALUES (0009,0321,'E0','Finn')
,(0009,0322,'E1','Finn')
,(0004,0356,'E0','Mary')
,(0004,0398,'E0','Mary')
,(0004,0382,'E1','Mary');
SELECT [MembershipNumber]
,[EmbossLine]
,[status]
,[EmbossName]
FROM
(
SELECT *
,COUNT([MembershipNumber]) OVER (PARTITION BY [EmbossName]) AS cnt1
,SUM(IIF([status] = 'E0' , 1, 0)) OVER (PARTITION BY [EmbossName]) AS cnt2
FROM #DataSource
) DS
WHERE cnt1 > 1
AND cnt2 > 1
AND [status] = 'E0';
The idea is to add a second counter, but instead of COUNT function to use SUM function for counting only the rows that have [status] = 'E0'. Then, in the where clause we are filtering by the two counters and [status] = 'E0'.

Need help creating SQL query from example of data

I have a database table below.
And I want to get list of all DBKey that have: at least one entry with Staled=1, and the last entry is Staled=0
The list should not contain DBKey that has only Staled=0 OR Staled=1.
In this example, the list would be: DBKey=2 and DBKey=3
I think this should do the trick:
SELECT DISTINCT T.DBKey
FROM TABLE T
WHERE
-- checks that the DBKey has at least one entry with Staled = 1
EXISTS (
SELECT DISTINCT Staled
FROM TABLE
WHERE DBKey = T.DBKey
AND Staled = 1
)
-- checks that the last Staled entry for this DBKey is 0
AND EXISTS (
SELECT DISTINCT Staled
FROM TABLE
WHERE DBKey = T.DBKey
AND Staled = 0
AND EntryDateTime = (
SELECT MAX(EntryDateTime)
FROM TABLE
WHERE DBKey = T.DBKey
)
)
Here is a working SQLFiddle of the query, using your sample data.
The idea is to use EXISTS to look for those individual conditions that you've described. I've added comments to my code to explain what each does.
Should be done with a simple JOIN... Starting FIRST with any 1 qualifiers, joined to itself by same key AND 0 staled qualifier AND the 0 record has a higher date. Ensure you have an index on ( DBKey, Staled, EntryDateTime )
SELECT
YT.DBKey,
MAX( YT.EntryDateTime ) as MaxStaled1,
MAX( YT2.EntryDateTime ) as MaxStaled0
from
YourTable YT
JOIN YourTable YT2
ON YT.DBKey = YT2.DBKey
AND YT2.Staled = 0
AND YT.EntryDateTime < YT2.EntryDateTime
where
YT.Staled = 1
group by
YT.DBKey
having
MAX( YT.EntryDateTime ) < MAX( YT2.EntryDateTime )
Maybe this:
With X as
(
Select Row_Number() Over (Partition By DBKey Order By EntryDateTime Desc) RN, DBKey, Staled
From table
)
Select *
From X
Where rn = 1 and staled = 0 and
Exists (select 1 from x x2 where x2.dbkey = x.dbkey and Staled = 1)