ListAgg() ORA-01002: fetch out of sequence - oracle

I'm running a query where I don't want to include the GL column in the GROUP BY clause because I don't want to show a different row for each variation of the GL field. But I need to evaluate the field in subsequent processing, comparing it to a list of dynamically generated GL numbers. The best way I can think to do this is with a function and I'm trying ListAgg(). First off, is there a better function to use to compare the GL from each row to a list? Also, I am able to get the results below using SQL Developer, but not when I try to output it on a web page where Oracle returns a ORA-01002 error. We are using 12c in both cases. Below is the query and the output. Without ListAgg(), I have to include the GL column in the SELECT and GROUP BY and it undesirably outputs a separate row for each GL.
SELECT h.h_spa_id as spa_id,
h.submit_dt,
h.oa_ap_date,
ListAgg(gl,',') within group (order by gl) "mygl"
WHERE h.next_apprv= 'approverID'
and h.table1_id = d.table2_id
and h.table1_id = table3_id
group by h.h_spa_id,h.submit_dt,h.oa_ap_date
order by h.h_spa_id
H_SPA_ID SUBMIT_DT OA_AP_DATE MYGL
1627005 1/25/2008 10:11:53 AM 1/25/2008 11:15:56 AM 52287,52287,52287,52287,52287,52287,52287,52287,52287,52287,52385,52385,52385,52385,52385,52385,52385,52385,52385,52385,52385,52385,52385,52385,52385

You can use CAST and COLLECT to group it into a collection:
SELECT h.h_spa_id as spa_id,
h.submit_dt,
h.oa_ap_date,
CAST( COLLECT( DISTINCT gl ) AS SYS.ODCINUMBERLIST ) AS mygl
FROM h
INNER JOIN
d
ON ( h.table1_id = d.table2_id )
WHERE h.next_apprv = 'approverID'
AND h.table1_id = table3_id
GROUP BY h.h_spa_id,
h.submit_dt,
h.oa_ap_date
ORDER BY h.h_spa_id
Then when you use it for subsequent processing the values will be in an array.

Related

How to apply a filter based on the current row

I'm building a calculated column that needs to sum all the rows in the same table that share a few common properties and have a greater date value.
I know I need to use calculate to break the filter context, but I'm not sure how to reference the row being calculated vs the table of the same name inside the calculate function. In Sql, this would be done as a self join with two different aliases for the same table, what is the DAX equivalent?
SQL pseudo code:
select
t1.Name
,sum(t2.a)
from table t1
inner join table t2 on t1.b = t2.b
and t1.c < t2.c
group by t1.name
DAX (how do I correctly reference outer row vs inner table?):
calculate(sum(table[a]),
filter(all(table), table[b] = table[b])
)
As suggested by #RADO, the earlier function does this (but you might not guess that from the name!).
Using my previous example, it looks like this:
calculate(sum(table[a]),
filter(all(table), table[b] = earlier(table[b]))
)

How to add decode but also have count, group by and order by - not a group by expression

I'm rather new to Oracle. I'm trying to do a count of ports for a vendor/model. However, I only want ones with port_addr_status of 3 or 4. For some reason, I'm getting error
ORA-00979 not a group by expression
This is what I have so far. It works without the decode part, but I don't think the part with pi.port_addr.status in ('3','4') works without it. I'm open to working around that issue as well.
select
count(pi.port) as cnt, d.VENDOR, trim(d.model) as model,
decode(pi.PORT_ADDR_STATUS, '1', 'Unassigned', '2','Pending','3','In Service',
'4','Pending Disco', '5','Trouble', '6','Reserved',
'7','Reserved Capacity', pi.PORT_ADDR_STATUS)
from
table1 pi,
table2 d,
table3 c
where
pi.id = d.id and
pi.circuit_id = c.circuit_id
and pi.port_addr_status in ('3','4')
and (d.dslam_type_desc not in ('AGGREGATOR') or d.dslam_type_desc is null)
and d.DSLAM not like '%#%'
group by
d.VENDOR, d.model --, trim(d.model), pi.RACK, pi.SHELF, pi.SLOT, pi.PORT, pi.BROADBAND_CIRCUIT_ID, d.DSLAM,
order by
d.VENDOR asc, cnt desc
This is sample output:
1031 Adtran TA5000
10 Adtran TA1248V
3 Adtran TA1248
Since the decoded port type is not required in your output, just remove it from the select list. Group By should include everything in the select list except the aggregate functions. The below query should do the job:
select
count(pi.port) as cnt, d.VENDOR, trim(d.model) as model
from
table1 pi,
table2 d,
table3 c
where
pi.id = d.id and
pi.circuit_id = c.circuit_id
and pi.port_addr_status in ('3','4')
and (d.dslam_type_desc not in ('AGGREGATOR') or d.dslam_type_desc is null)
and d.DSLAM not like '%#%'
group by
d.VENDOR, trim(d.model)
order by
d.VENDOR asc, cnt desc
Note that the filter down to specific port types is achieved here:
and pi.port_addr_status in ('3','4')
in the where clause - the decode statement in your original query did nothing to solve your requirements and was the direct cause of the error. In a basic aggregate query like this, group by should have every item that is in the select list that is not an aggregate function.
You might use decode statement in the select list with an aggregation function preferably max as
max( decode(pi.PORT_ADDR_STATUS, '1', 'Unassigned', '2','Pending', .... )
The reason you're getting that error is:
when using a GROUP BY clause you are asking the DBMS to give you a single row corresponding to your GROUP BY list
in your case, you are asking for one row per (Vendor, Model) combination
since each of these rows represents possibly many "underlying" rows (say, e.g. 5 rows for Vendor=A and Model=B), the only other fields you can have in the select list must be aggregates (that's why the "count" field works, it counts the rows, i.e. it can take many row values and return a single value)
the DECODE function is not an aggregate function - it can't "collapse" multiple values into one value (like, say, MAX() can)
It looks to me as if your DECODE function returns strings, so I recommend you aggregate the strings with the LISTAGG function (so if your 5 rows have values of 'asdf', 'fdsa', 'qwer', 'rewq', and 'zxcv' the LISTAGG function would return a single string: 'asdf,fdsa,qwer,rewq,zxcv')
So you would replace your DECODE(...) call with, e.g. LISTAGG(DECODE(...), ',')
Reference for LISTAGG function: https://docs.oracle.com/cd/E11882_01/server.112/e41084/functions089.htm#SQLRF30030

how to compare one value against 2 values in Oracle

I want to compare a a value against 2 values without using OR or DECODE. The value I want to compare with two values is the one which I am getting as a return code of a function. If I use OR or DECODE then I have to call function twice and it gives performance hit. Currently I am coding as below
select *
from table1 t1, table2 t1
where t1.empid = t2.empid
and myfunction(t2.balance) = t1.total OR myfunction(t2.balance) = -1
Please suggest if there is a way to call function once and compare with 2 values.
To shorten your code you could use IN operator which acts like OR.
select *
from table1 t1
join table2 t1 on
t1.empid = t2.empid
and myfunction(t2.balance) in (t1.total, -1)
I've also replaced old-fashioned join syntax in where clause for JOIN keyword and you're advised to be using that in your future SQL journeys.
Good thing to know would be that even though you call the function twice, most modern databases would actually call it only once, so I wouldn't be that much concerned about it.

Compare first row to other rows

The issue I face that I need after selecting multiple rows,to loop over each row and fetch those who have some related information to the first row.
Example:
select NAME,ENGLISH_GRADE,FRANCE_GRAE
from (some complex query that have order by and returns 100 rows) WHOLE_ROWS
where
//Here I need to loop over WHOLE_ROWS and make something like that:
//if(currentRow.ENGLISH_GRADE==WHOLE_ROWS(0).ENGLISH_GRADE)
//fetch this row
Basically, you need to join your query to itself. You can do with with a subquery factoring clause:
WITH complex_query AS
( ... complex query here ... )
SELECT
FROM complex_query cq1
WHERE cq1.english_grade = ( SELECT english_grade FROM cq1
WHERE rownum = 1 )
Here is a SQL Fiddle. You could also do this with analytics, but those seem to me more difficult to understand.

Oracle: MIN() Statement causes empty row returns

I'm having a small issue with sorting the data returned from a query, with the aim of getting the oldest updated value in dataset so that I can update only that record. Here's what I'm doing:
WHERE ROWNUM = 1 AND TABLE1.ID != V_IGNOREID
AND TABLE1.LASTREADTIME = (SELECT MIN(TABLE1.LASTREADTIME) FROM TABLE1)
ORDER BY TABLE1.LASTREADTIME DESC;
It makes no difference as to whether the ORDER BY statement is included or not. If I only use the ROWNUM and equality checks, I get data, but it alternates between only two rows, which is why I'm trying to use the LASTREADTIME data (so that I can modify more than these two rows). Anybody have any thoughts on this, or any suggestions as to how I can use the MIN function effectively?
Cheers
select * from (
-- your original select without rownum and with order by
)
WHERE ROWNUM = 1
EDIT some explanation
I think the order by clause is applied on the resultset after the where clause. So if the rownum = 1 is in the same select statement with the order by, then it will be applied first and the order by will order only 1 row, which will be the first row of the unordered resultset.

Resources