SQL - Multiple values to one - sql

Please help me, the question is probably stupid, but I'm at a dead end. There is a table with the following data:
Num
Start number
Date start
111
225
11.11.22
111
223
9.11.22
111
220
9.11.22
222
347
11.11.22
222
345
11.11.22
222
343
10.11.22
I would like to come to something like this, so that Num is displayed in one cell, the first and last values are displayed in the Start number and Date start fields, respectively, and their number is also counted in one cell:
Num
Start number
Date start
Count
111
225
11.11.22
3
220
9.11.22
222
347
11.11.22
3
343
10.11.22

What you want is literally done in this query:
with t(Num, Start_number, Date_start) as (
select 111 , 225 , '11.11.22' from dual union all
select 111 , 223 , '9.11.22' from dual union all
select 111 , 220 , '9.11.22' from dual union all
select 222 , 347 , '11.11.22' from dual union all
select 222 , 345 , '11.11.22' from dual union all
select 222 , 343 , '10.11.22' from dual
)
select case when p.is_first = 1 then p.num end as num
, p.start_number
, p.date_start
, case when p.is_first = 1 then p.cnt end as num
from (
select t.*
, case when start_number = max(start_number) over (partition by num) then 1 else 0 end as is_first
, case when start_number = min(start_number) over (partition by num) then 1 else 0 end as is_last
, count(*) over (partition by num) as cnt
from t
) p
where p.is_first = 1 or p.is_last = 1
fiddle
However I agree with commenters this should be done at GUI level rather than SQL level. It does not make sense to alternate null and nonnull values (is it for some report?). You can utilize the p ("precomputation") subquery anyway, just change outer query then.

You can try the following SQL code:
SELECT
Num,
StartNumber,
DateStart,
[Count]
FROM
(
SELECT
Num,
[Count],
(SELECT MAX(StartNumber) FROM table AS t2 WHERE (t2.Num = t1.Num) AND (t2.DateStart = t1.MaxDateStart)) AS StartNumber,
MaxDateStart AS DateStart
FROM
(
SELECT
Num,
COUNT(*) AS [Count],
MAX(DateStart) AS MaxDateStart
FROM
table
GROUP BY
Num
) AS t1
UNION
SELECT
Num,
[Count],
(SELECT MIN(StartNumber) FROM table AS t2 WHERE (t2.Num = t1.Num) AND (t2.DateStart = t1.MinDateStart)) AS StartNumber,
MinDateStart AS DateStart
FROM
(
SELECT
Num,
COUNT(*) AS [Count],
MIN(DateStart) AS MinDateStart
FROM
table
GROUP BY
Num
) AS t1
) AS t
ORDER BY
Num, DateStart DESC
This code works without OVER for different RDBMS.

Related

How to find multiple pairs in one oracle table

I have a problem with finding matches in one Oracle-Table and I hope you can help me.
I have one bill-table containing booking data looking like this:
ID GROUP Bill-Number Value Partner-Number
1 1 111 10,90
2 1 751 40,28
3 1 438 125,60
4 1 659 -10,90 987
5 1 387 -165,88 755
6 1 774 -100,10
7 1 664 -80,12
8 1 259 180,22 999
9 2 774 -200,10
10 2 664 -80,12
11 2 259 280,22 777
As you can see, we have some bills that are containing costs.
Some time later, there is coming the counter-bill that is summarizing the previous costs. The sum of the bills and the associated counter-bill is creating a Value of 0.
Example: value of (id 2 + id 3 = id 5*-1)
or in numbers: 40,28 + 125,60 + (-165,88) = 0
The counter-bills are containing a "Partner-Number". I need to add this information to the associated bills.
The solution should look like this:
ID GROUP Bill-Number Value Partner-Number
1 1 111 10,90 987
2 1 751 40,28 755
3 1 438 125,60 755
4 1 659 -10,90 987
5 1 387 -165,88 755
6 1 774 -100,10 999
7 1 664 -80,12 999
8 1 259 180,22 999
9 2 774 -200,10 777
10 2 664 -80,12 777
11 2 259 280,22 777
I have to match the bills only inside a group. (ID is my primary key)
As long as the group is containing one counter-bill with a 1:1 relation to a bill it is doable for me.
But how can I find the matches like in group 1 where the relation is 1:N? (the group contains multiple counter-bills)
I hope you can help me - thank you in advance :)
The following SQL code, has been tested with Oracle 12c and 18c, respectively. Ideas/steps:
{1} Split the original table into a MINUSES and PLUSES table, containing just positive numbers, saving us a few function calls later.
{2} Create 2 views that will find combinations of pluses that fit a particular minus (and vice versa).
{3} List all components, in "comma-separated" form in a table called ALLCOMPONENTS.
{4} Table GAPFILLERS: Expand the (comma separated) IDs of all components, thereby obtaining all necessary values to fill the gaps in the original table.
{5} LEFT JOIN the original table to the GAPFILLERS.
Original table/data
create table bills ( id primary key, bgroup, bnumber, bvalue, partner )
as
select 1, 1, 111, 10.90, null from dual union all
select 2, 1, 751, 40.28, null from dual union all
select 3, 1, 438, 125.60, null from dual union all
select 4, 1, 659, -10.90, 987 from dual union all
select 5, 1, 387, -165.88, 755 from dual union all
select 6, 1, 774, -100.10, null from dual union all
select 7, 1, 664, -80.12, null from dual union all
select 8, 1, 259, 180.22, 999 from dual union all
select 9, 2, 774, -200.10, null from dual union all
select 10, 2, 664, -80.12, null from dual union all
select 11, 2, 259, 280.22, 777 from dual ;
{1} split the table into a PLUS and a MINUS table
-- MINUSes
create table minuses as
select id
, bgroup as mgroup
, bnumber as mnumber
, bvalue * -1 as mvalue
, partner as mpartner
from bills where bvalue < 0 ;
-- PLUSes
create table pluses as
select id
, bgroup as pgroup
, bnumber as pnumber
, bvalue as pvalue
, partner as ppartner
from bills where bvalue >= 0 ;
{2} View: find components of PLUSvalues
-- used here: "recursive subquery factoring"
-- and LATERAL join (needs Oracle 12c or later)
create or replace view splitpluses
as
with recursiveclause ( nextid, mgroup, tvalue, componentid )
as (
select -- anchor member
id as nextid
, mgroup as mgroup
, mvalue as tvalue -- total value
, to_char( id ) as componentid
from minuses
union all
select -- recursive member
M.id
, R.mgroup
, R.tvalue + M.mvalue
, R.componentid || ',' || to_char( M.id )
from recursiveclause R
join minuses M
on M.id > R.nextid and M.mgroup = R.mgroup -- only look at values in the same group
)
--
select
mgroup
, tvalue as plusvalue
, componentid as minusids
, ppartner
from
recursiveclause R
, lateral ( select ppartner from pluses P where R.tvalue = P.pvalue ) -- fetch the partner id
where
tvalue in ( select pvalue from pluses where ppartner is not null ) -- get all relevant pvalues that must be broken down into components
and ppartner is not null -- do this for all pluses that have a partner id
;
{2b} View: find components of MINUSvalues
create or replace view splitminuses
as
with recursiveclause ( nextid, pgroup, tvalue, componentid )
as (
select -- anchor member
id as nextid
, pgroup as pgroup
, pvalue as tvalue -- total value
, to_char( id ) as componentid
from pluses
union all
select -- recursive member
P.id
, R.pgroup
, R.tvalue + P.pvalue
, R.componentid || ',' || to_char( P.id )
from recursiveclause R
join pluses P
on P.id > R.nextid and P.pgroup = R.pgroup
)
--
select
pgroup
, tvalue as minusvalue
, componentid as plusids
, mpartner
from
recursiveclause R
, lateral ( select mpartner from minuses M where R.tvalue = M.mvalue )
where
tvalue in ( select mvalue from minuses where mpartner is not null )
and mpartner is not null
;
The views give us the following result sets:
SQL> select * from splitpluses;
MGROUP PLUSVALUE MINUSIDS PPARTNER
1 180.22 6,7 999
2 280.22 9,10 777
SQL> select * from splitminuses ;
PGROUP MINUSVALUE PLUSIDS MPARTNER
1 10.9 1 987
1 165.88 2,3 755
{3} Table ALLCOMPONENTS: list of all "components"
create table allcomponents ( type_, group_, value_, cids_, partner_ )
as
select 'components of PLUS' as type_, M.* from splitminuses M
union all
select 'components of MINUS', P.* from splitpluses P
;
SQL> select * from allcomponents ;
TYPE_ GROUP_ VALUE_ CIDS_ PARTNER_
components of PLUS 1 10.9 1 987
components of PLUS 1 165.88 2,3 755
components of MINUS 1 180.22 6,7 999
components of MINUS 2 280.22 9,10 777
{4} Table GAPFILLERS: derived from ALLCOMPONENTS, contains all values we need to fill the "gaps" in the original table.
-- One row for each CSV (comma-separated value) of ALLCOMPONENTS
create table gapfillers
as
select unique type_, group_, value_
, trim( regexp_substr( cids_, '[^,]+', 1, level ) ) cids_
, partner_
from (
select type_, group_, value_, cids_, partner_
from allcomponents
) AC
connect by instr( cids_, ',', 1, level - 1 ) > 0
order by group_, partner_ ;
SQL> select * from gapfillers ;
TYPE_ GROUP_ VALUE_ CIDS_ PARTNER_
components of PLUS 1 165.88 2 755
components of PLUS 1 165.88 3 755
components of PLUS 1 10.9 1 987
components of MINUS 1 180.22 6 999
components of MINUS 1 180.22 7 999
components of MINUS 2 280.22 10 777
components of MINUS 2 280.22 9 777
7 rows selected.
{5} The final LEFT JOIN
select
B.id, bgroup, bnumber, bvalue
, case
when B.partner is null then G.partner_
else B.partner
end as partner
from bills B
left join gapfillers G on B.id = G.cids_
order by 1 ;
-- result
ID BGROUP BNUMBER BVALUE PARTNER
1 1 111 10.9 987
2 1 751 40.28 755
3 1 438 125.6 755
4 1 659 -10.9 987
5 1 387 -165.88 755
6 1 774 -100.1 999
7 1 664 -80.12 999
8 1 259 180.22 999
9 2 774 -200.1 777
10 2 664 -80.12 777
11 2 259 280.22 777
11 rows selected.
DBFIDDLE here.
SQLis idealy designe for a brute force approach to solve the problems (the only problm is, that for large data the query would hang forever).
Here a possible step by step approach, considering only one counter-bill in teh first step, than two in the second step etc.
I'm showing queries for the first two steps, you should get the idea how to proceed - most probably with dynamic SQL in a loop.
The first step is trivial self-joining joining the table and constraining the GROUP and value. A result table is created, whichis used further to limit already matched rows.
create table tab_match as
-- 1 row match
select b.ID, b.GROUP_ID, b.BILL_NUMBER, b.VALUE, a.partner_number from tab a
join tab b
on a.group_id = b.group_id and /* same group */
-1 * a.value = b.value /* oposite value */
where a.partner_number is not NULL /* consider group row only */
In the second step you repeats the same, only adding one join (we investigate two sub.bills) with an additional constraint on the total value -1 * a.value = (b.value + c.value)
Also we suppress all partner_numbers and bills already assigned. The result is inserted in the temporary table.
insert into tab_match (ID, GROUP_ID, BILL_NUMBER, VALUE, PARTNER_NUMBER)
select b.ID, b.GROUP_ID, b.BILL_NUMBER, b.VALUE, a.partner_number partner_number_match from tab a
join tab b
on a.group_id = b.group_id and /* same group */
sign(a.value) * sign(b.value) < 0 and /* values must have oposite signs */
abs(a.value) > abs(b.value) /* the partial value is lower than the sum */
join tab c /* join to 2nd table */
on a.group_id = c.group_id and
sign(a.value) * sign(c.value) < 0 and
abs(a.value) > abs(c.value) and
-1 * a.value = (b.value + c.value)
where a.partner_number is not NULL and /* consider open group row only */
a.partner_number not in (select partner_number from tab_match) and
a.id not in (select id from tab_match) /* ignore matched rows */
;
You must proceed with processing of 3,4 etc. rows until all partner_numbers and bills are assigned.
Add a next join
join tab d
on a.group_id = d.group_id and
sign(a.value) * sign(d.value) < 0 and
abs(a.value) > abs(d.value)
and adjust the total sum predicate in each step
-1 * a.value = (b.value + c.value + d.value)
Good Luck;)

I want first and second inserted value with condition

Table abc:
Consgno Name Entrydatetime
111 A 01/03/2017 10:10:15
111 A 01/03/2017 10:20:15
111 A 01/03/2017 11:10:20
222 B 02/03/2017 10:10:25
333 C 06/03/2017 10:10:25
333 C 07/03/2017 10:10:12
444 D 04/03/2017 10:10:41
444 D 04/03/2017 01:10:20
444 D 06/03/2017 10:10:32
555 E 05/04/2017 10:10:15
One Consgno has entered ONE more than one time.
When one Consgno is only once, then the first value should come, otherwise the second entered value should come.
I want to output like this:
Consgno Name Entrydatetime
111 A 01/03/2017 10:20:15
222 B 02/03/2017 11:10:36
333 C 07/03/2017 10:10:12
444 D 04/03/2017 01:10:20
555 E 05/04/2017 10:10:15
You can use a query like the following:
;WITH MyWindowedTable AS (
SELECT Consgno, Name, Entrydatetime,
ROW_NUMBER() OVER (PARTITION BY Consgno
ORDER BY Entrydatetime) AS rn,
COUNT(*) OVER (PARTITION BY Consgno) AS cnt
FROM mytable
)
SELECT Consgno, Name, Entrydatetime
FROM MyWindowedTable
WHERE (cnt = 1 AND rn = 1) OR (cnt > 1 AND rn = 2)
Using windowed version of COUNT:
COUNT(*) OVER (PARTITION BY Consgno)
returns the population, cnt, of each Consgno partition. We can use cnt to properly filter the records returned: in partitions with a population of 1 we get the single record of the partition, whereas in the rest of the cases we get the one having rn = 2.
Use ROW_NUMBER and COUNT built in functions :
CREATE TABLE #table1 ( Consgno INT, Name VARCHAR(1), Entrydatetime
DATETIME)
INSERT INTO #table1 ( Consgno , Name , Entrydatetime )
SELECT 111,'A','01/03/2017 10:10:15' UNION ALL
SELECT 111,'A','01/03/2017 10:20:15' UNION ALL
SELECT 111,'A','01/03/2017 11:10:20' UNION ALL
SELECT 222,'B','02/03/2017 10:10:25' UNION ALL
SELECT 333,'C','06/03/2017 10:10:25' UNION ALL
SELECT 333,'C','07/03/2017 10:10:12' UNION ALL
SELECT 444,'D','04/03/2017 10:10:41' UNION ALL
SELECT 444,'D','04/03/2017 01:10:20' UNION ALL
SELECT 444,'D','06/03/2017 10:10:32' UNION ALL
SELECT 555,'E','05/04/2017 10:10:15'
SELECT Consgno , Name , Entrydatetime
FROM
(
SELECT Consgno , Name , Entrydatetime , ROW_NUMBER() OVER (PARTITION BY
Consgno ORDER BY Entrydatetime) RNo , COUNT(*) OVER (PARTITION BY
Consgno) AS _Count
FROM #table1
) A WHERE ( RNo = 1 AND _Count = 1) OR (_Count > 1 AND RNo = 2 )

Max and Min value's corresponding records

I have a scenario to get the respective field value of "Max" and "Min" records
Please find the sample data below
-----------------------------------------------------------------------
ID Label ProcessedDate
-----------------------------------------------------------------------
1 Label1 11/01/2016
2 Label2 11/02/2016
3 Label3 11/03/2016
4 Label4 11/04/2016
5 Label5 11/05/2016
I have the "ID" field populated in another table as a foreign key. While querying those records in that table based on the "ID" field I need to get the "Label" field of "Max" Processed date and "Min" processed date.
-----------------------------------------------------------------------
ID LabelID GroupingField
-----------------------------------------------------------------------
1 1 101
2 2 101
3 3 101
4 4 101
5 5 101
6 1 102
7 2 102
8 3 102
9 4 102
And the final result set I expect it to look something like this.
-----------------------------------------------------------------------
GroupingField FirstProcessed LastProcessed
-----------------------------------------------------------------------
101 Label1 Label5
102 Label1 Label4
I have 'almost' managed to get this above result using rank function but still not satisfied with it. So I am looking if someone can provide me with a better option.
Thanks,
Prakazz
CREATE TABLE #Details (ID INT,LabelID INT,GroupingField INT)
CREATE TABLE #Details1 (ID INT,Label VARCHAR(100),ProcessedDate VARCHAR(100))
INSERT INTO #Details1 (ID ,Label ,ProcessedDate )
SELECT 1,'Label1','11/01/2016' UNION ALL
SELECT 2,'Label2','11/02/2016' UNION ALL
SELECT 3,'Label3','11/03/2016' UNION ALL
SELECT 4,'Label4','11/04/2016' UNION ALL
SELECT 5,'Label5','11/05/2016'
INSERT INTO #Details (ID ,LabelID ,GroupingField )
SELECT 1,1,101 UNION ALL
SELECT 2,2,101 UNION ALL
SELECT 3,3,101 UNION ALL
SELECT 4,4,101 UNION ALL
SELECT 5,5,101 UNION ALL
SELECT 6,1,102 UNION ALL
SELECT 7,2,102 UNION ALL
SELECT 8,3,102 UNION ALL
SELECT 9,4,102
;WITH CTE (GroupingField , MAXId ,MinId) AS
(
SELECT GroupingField,MAX(LabelID) MAXId,MIN(LabelID) MinId
FROM #Details
GROUP BY GroupingField
)
SELECT GroupingField ,B.Label FirstProcessed, A.Label LastProcessed
FROM CTE
JOIN #Details1 A ON MAXId = A.ID
JOIN #Details1 B ON MinId = B.ID
You can use SQL Row_Number() function using Partition By as follows with a combination of Group By
;with cte as (
select
t.Label, t.ProcessedDate,
g.GroupingField,
ROW_NUMBER() over (partition by GroupingField Order By ProcessedDate ASC) minD,
ROW_NUMBER() over (partition by GroupingField Order By ProcessedDate DESC) maxD
from tbl t
inner join GroupingFieldTbl g
on t.ID = g.LabelID
)
select GroupingField, max(FirstProcessed) FirstProcessed, max(LastProcessed) LastProcessed
from (
select
GroupingField,
FirstProcessed = CASE when minD = 1 then Label else null end,
LastProcessed = CASE when maxD = 1 then Label else null end
from cte
where
minD = 1 or maxD = 1
) t
group by GroupingField
order by GroupingField
I also used CTE expression to make coding easier and understandable
Output is as

Retrieve the maximum number for each group of records oracle DB

I failed to write SQL code to retrieve maximum number in order_number column.
notice that visit_number is group contain more than one record and i want the maximum value of order_number column.
visit_number , Order_number , NAME
111 , 1 , 001
111 , 2 , 001
111 , 3 , 001
222 , 1 , 252
222 , 2 , 252
003 , 1 , 121
003 , 2 , 121
I want the result to be like below matrix:
111 , 3 , 001
222 , 2 , 252
003 , 2 , 121
this is my query
SELECT VISIT_NUMBER , MAX(ORDER_NUMBER) , NAME
from table
group by ( visit_number , name )
Use GROUP BY and the MAX function.
SELECT visit_number, MAX(Order_number), NAME
FROM yourtable
GROUP BY visit_number, NAME
Join the table with a sub-query that returns each visit_number together with its max order_number:
SELECT distinct t1.*
FROM tablename t1
JOIN (select visit_number, MAX(Order_number) as max_Order_number
from tablename group by visit_number) as t2
ON t1.visit_number = t2.visit_number and t1.Order_number = t2.max_Order_number
(Will return all rows if several different Names for a maximum order number.)
select VISIT_NUMBER, ORDER_NUMBER, NAME
from (
select VISIT_NUMBER, ORDER_NUMBER, NAME, row_number() over(partition by VISIT_NUMBER order by ORDER_NUMBER desc) as ORDER_DESC
from TABLE
)
where ORDER_DESC = 1
This will return the max for each VISIT_NUMBER, even if the NAME changes

How can I get the first result for each account in this SQL query?

I'm trying to write a query that follows this logic:
Find the first following status code of an account that had a previous status code of X.
So if I have a table of:
id account_num status_code
64 1 X
82 1 Y
72 2 Y
87 1 Z
91 2 X
103 2 Z
The results would be:
id account_num status_code
82 1 Y
103 2 Z
I've come up with a couple of solutions but I'm not all that great with SQL and so they've been pretty inelegeant thus far. I was hoping that someone here might be able to point me in the right direction.
View:
SELECT account_number, id
FROM table
WHERE status_code = 'X'
Query:
SELECT account_number, min(id)
FROM table
INNER JOIN view
ON table.account_number = view.account_number
WHERE table.id > view.id
At this point I have the id that I need but I'd have to write ANOTHER query that uses the id tolook up the status_code.
Edit: To add some context, I'm trying to find calls that have a status_code of X. If a call has a status_code of X we want to dial it a different way the next time we make an attempt. The aim of this query is to provide a report that will show the results of the second dial if the first dial resulted an X status code.
Here's a SQL Server solution.
UPDATE
The idea is to avoid a number of NESTED LOOP joins as proposed by Olaf because they roughly have O(N * M) complexity and thus extremely bad for your performance. MERGED JOINS complexity is O(NLog(N) + MLog(M)) which is much better for real world scenarios.
The query below works as follows:
RankedCTE is a subquery that assigns a row number to each id partioned by account and sorted by id which represents the time. So for the data below the output of this
SELECT
id,
account_num,
status_code,
ROW_NUMBER() OVER (PARTITION BY account_num ORDER BY id DESC) AS item_rank
FROM dbo.Test
would be:
id account_num status_code item_rank
----------- ----------- ----------- ----------
87 1 Z 1
82 1 Y 2
64 1 X 3
103 2 Z 1
91 2 X 2
72 2 Y 3
Once we have them numbered we join the result on itself like this:
WITH RankedCTE AS
(
SELECT
id,
account_num,
status_code,
ROW_NUMBER() OVER (PARTITION BY account_num ORDER BY id DESC) AS item_rank
FROM dbo.Test
)
SELECT
*
FROM
RankedCTE A
INNER JOIN RankedCTE B ON
A.account_num = B.account_num
AND A.item_rank = B.item_rank - 1
which will give us an event and a preceding event in the same table
id account_num status_code item_rank id account_num status_code item_rank
----------- ----------- ----------- ----------- ----------- ----------- ----------- -----------
87 1 Z 1 82 1 Y 2
82 1 Y 2 64 1 X 3
103 2 Z 1 91 2 X 2
91 2 X 2 72 2 Y 3
Finally, we just have to take the preceding event with code "X" and the event with code not "X":
WITH RankedCTE AS
(
SELECT
id,
account_num,
status_code,
ROW_NUMBER() OVER (PARTITION BY account_num ORDER BY id DESC) AS item_rank
FROM dbo.Test
)
SELECT
A.id,
A.account_num,
A.status_code
FROM
RankedCTE A
INNER JOIN RankedCTE B ON
A.account_num = B.account_num
AND A.item_rank = B.item_rank - 1
AND A.status_code <> 'X'
AND B.status_code = 'X'
Query plans for this query and #Olaf Dietsche solution (one of the versions) are below.
Data setup script
CREATE TABLE dbo.Test
(
id int not null PRIMARY KEY,
account_num int not null,
status_code nchar(1)
)
GO
INSERT dbo.Test (id, account_num, status_code)
SELECT 64 , 1, 'X' UNION ALL
SELECT 82 , 1, 'Y' UNION ALL
SELECT 72 , 2, 'Y' UNION ALL
SELECT 87 , 1, 'Z' UNION ALL
SELECT 91 , 2, 'X' UNION ALL
SELECT 103, 2, 'Z'
SQL Fiddle with subselect
select id, account_num, status_code
from mytable
where id in (select min(t1.id)
from mytable t1
join mytable t2 on t1.account_num = t2.account_num
and t1.id > t2.id
and t2.status_code = 'X'
group by t1.account_num)
and SQL Fiddle with join, both for MS SQL Server 2012, both returning the same result.
select id, account_num, status_code
from mytable
join (select min(t1.id) as min_id
from mytable t1
join mytable t2 on t1.account_num = t2.account_num
and t1.id > t2.id
and t2.status_code = 'X'
group by t1.account_num) t on id = min_id
SELECT MIN(ID), ACCOUNT_NUM, STATUS_CODE FROM (
SELECT ID, ACCOUNT_NUM, STATUS_CODE
FROM ACCOUNT A1
WHERE EXISTS
(SELECT 1
FROM ACCOUNT A2
WHERE A1.ACCOUNT_NUM = A2.ACCOUNT_NUM
AND A2.STATUS_CODE = 'X'
AND A2.ID < A1.ID)
) SUB
GROUP BY ACCOUNT_NUM
Here's an SQLFIDDLE
Here's query, with your data, checked under PostgreSQL:
SELECT t0.*
FROM so13594339 t0 JOIN
(SELECT min(t1.id), t1.account_num
FROM so13594339 t1, so13594339 t2
WHERE t1.account_num = t2.account_num AND t1.id > t2.id AND t2.status_code = 'X'
GROUP BY t1.account_num
) z
ON t0.id = z.min AND t0.account_num = z.account_num;