Oracle passing outer query value to inner query - sql

This might be a simple but I need to apply the logic in other:
WITH t(col) AS (
SELECT 1 FROM dual
UNION SELECT 2 FROM dual
UNION SELECT 3 FROM dual
UNION SELECT 4 FROM dual
UNION SELECT 5 FROM dual
)
SELECT col , --- will works as usual
(SELECT col FROM t WHERE col = outer_q.col) new_col, --working as well
(
SELECT sum (latest_col)
from
(
SELECT col latest_col FROM t WHERE col = outer_q.col
UNION ALL
SELECT col FROM t WHERE col = outer_q.col
)
)newest_col -- need to get an output "4"
from t outer_q where col = 2;
An simple output like:
COL NEW_COL NEWEST_COL
---------- ---------- ----------
2 2 4
I just need to use the outer most value to the inner I used for the third column
EDITING-- sample with more data:
WITH
t(col) AS
( SELECT 1 FROM dual
UNION
SELECT 2 FROM dual
UNION
SELECT 3 FROM dual
UNION
SELECT 4 FROM dual
UNION
SELECT 5 FROM dual
),
t1(amount, col) AS
(SELECT 100 , 2 FROM dual
UNION
SELECT 200, 3 FROM dual
)
SELECT col,
(SELECT col FROM t WHERE col = outer_q.col
) new_col,
(SELECT SUM(x)
FROM
(SELECT col x FROM t
UNION ALL
SELECT amount x FROM t1
)
WHERE col = outer_q.col
) newest_col -- gives 315 as it takes whole `SUM`
FROM t outer_q
WHERE col = 2;
An output is expected like:
COL NEW_COL NEWEST_COL
---------- ---------- ----------
2 2 102
Thanks in advance for any help.

Well, you can if you refactor a but your query:
WITH t(col) AS (
SELECT 1 FROM dual
UNION SELECT 2 FROM dual
UNION SELECT 3 FROM dual
UNION SELECT 4 FROM dual
UNION SELECT 5 FROM dual
)
SELECT col,
(SELECT col FROM t WHERE col = outer_q.col) new_col,
(SELECT sum (latest_col)
from
(
SELECT col latest_col FROM t
UNION ALL
SELECT col FROM t
) x
where x.latest_col = outer_q.col
) newest_col -- need to get an output "4"
from t outer_q where col = 2;
This is possible here because outer_q is now in the where clause of the sub-query. It was used before in the sub-sub-query (the one with the UNION ALL), and this one was hiding it.
To try to make things clearer, now we have something like:
with t as (...)
select col,
(SELECT col FROM t WHERE col = outer_q.col) new_col,
(SELECT col FROM (Something more complex) WHERE ... = outer_q.col) new_col,
from t outer_q where col = 2;
So we now have the same level of "interiority".
EDIT: to answer the updated question, there is a little adaptation needed:
WITH t(col) AS
(
SELECT 1 FROM dual
UNION
SELECT 2 FROM dual
UNION
SELECT 3 FROM dual
UNION
SELECT 4 FROM dual
UNION
SELECT 5 FROM dual
),
t1(amount, col) AS
(
SELECT 100, 2 FROM dual
UNION
SELECT 200, 3 FROM dual
)
SELECT col,
(SELECT col FROM t WHERE col = outer_q.col) new_col,
(SELECT SUM(amount)
FROM
(SELECT col, col amount FROM t -- row is (1, 1), then (2, 2) etc
UNION ALL
SELECT col, amount FROM t1 -- row is (2, 100), then (3, 200) etc
)
WHERE col = outer_q.col
) newest_col -- gives 102 as it takes whole `SUM`
FROM t outer_q
WHERE col = 2;
The part to understand is in the innermost query: you want to sum both the column and the amount value, so you repeat the col value as if it was an amount.
Another way to obtain the same result (with more performance, I guess) would be to sum col and amount on the same row:
WITH t(col) AS
(
SELECT 1 FROM dual
UNION
SELECT 2 FROM dual
UNION
SELECT 3 FROM dual
UNION
SELECT 4 FROM dual
UNION
SELECT 5 FROM dual
),
t1(amount, col) AS
(
SELECT 100, 2 FROM dual
UNION
SELECT 200, 3 FROM dual
)
SELECT col,
(SELECT col FROM t WHERE col = outer_q.col) new_col,
(SELECT SUM(all_amount)
FROM
(SELECT col, col + amount all_amount FROM t1)
WHERE col = outer_q.col
) newest_col -- gives 315 as it takes whole `SUM`
FROM t outer_q
WHERE col = 2;

The inner query fails because you tried to push the outer_q.col reference two levels down. Correlated query goes only 1 level down
Reference: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:1853075500346799932

Related

Compare before column in before row with next column in next row

My code is :
with x as
(
select 1 col from dual union all
select 2 col from dual union all
select 8 col from dual union all
select 4 col from dual union all
select 3 col from dual union all
select 2 col from dual
)
select col col1, col col2, col col3, rownum
from x
where col2.ROWNUM > col1.ROWNUM -1
and col2.ROWNUM > col3ROWNUM +1 ;
I want to compare col2.ROWNUM > col1.ROWNUM -1 and col2.ROWNUM > col3ROWNUM + 1 but that doesn't work and I got an error
ORA-01747: invalid user.table.column, table.column, or column specification
01747. 00000 - "invalid user.table.column, table.column, or column specification"
*Cause:
*Action:
Error at Line: 10 Column: 13
Please help me
It looks you got something wrong.
Result of that CTE is a single-column table whose only column is named col. There are no other columns.
SQL> with x as (
2 select 1 col from dual union all --> in UNION, all columns are
3 select 2 col from dual union all named by column name(s) from the
4 select 8 col from dual union all first SELECT statement
5 select 4 col from dual union all
6 select 3 col from dual union all
7 select 2 col from dual)
8 select x.*, rownum
9 from x;
COL ROWNUM
---------- ----------
1 1
2 2
8 3
4 4
3 5
2 6
6 rows selected.
SQL>
Therefore, where clause you wrote doesn't make any sense. Perhaps you should explain what you really have, rules that should be applied to source data and result you'd like to get.
Based on text you put into the title:
compare before column in before row with next column in next row
maybe you'd be interested in lag and lead analytic functions which then let you compare values in adjacent rows (pay attention to NULL values; I didn't). For example:
SQL> with x as (
2 select 1 col from dual union all
3 select 2 col from dual union all
4 select 8 col from dual union all
5 select 4 col from dual union all
6 select 3 col from dual union all
7 select 2 col from dual
8 ),
9 temp as
10 (select col,
11 rownum as rn
12 from x
13 ),
14 temp2 as
15 (select
16 rn,
17 col as this_row,
18 lag(col) over (order by rn) as previous_row,
19 lead(col) over (order by rn) as next_row
20 from temp
21 )
22 select this_row,
23 previous_row,
24 next_row,
25 --
26 case when this_row < previous_row then 'This < previous'
27 when this_row < next_row then 'This < next'
28 else 'something else'
29 end as result
30 from temp2
31 order by rn;
Result:
THIS_ROW PREVIOUS_ROW NEXT_ROW RESULT
---------- ------------ ---------- ---------------
1 2 This < next
2 1 8 This < next
8 2 4 something else
4 8 3 This < previous
3 4 2 This < previous
2 3 This < previous
6 rows selected.
SQL>
Use lead or lag functions. But, please, do not use rownum for such purposes.
Rownum indicates simply the order in which a row was found in the database and cannot be used for other purposes except limiting the number of rows fetched, like where rownum<=1 to be certain you won't get a too_many_rows exception, for instance. Still, if in a query you do fetch the pseud-column rownum, give it an alias so that you may use that value later on.
Moreover, what is supposed to mean col2.ROWNUM or col1.ROWNUM? That is not clear. col1 and col2 are two columns, which do not have the attribute rownum.
Something that may help in the future for analytic queries:
https://oracle-base.com/articles/misc/lag-lead-analytic-functions
And, if you wish to get a working SQL, please explain clearly what you wish to achieve, for I haven't really understood what that code is intended to do.
A way you may use rownum without getting errors:
with x as (
select 1 col from dual union all
select 2 col from dual union all
select 8 col from dual union all
select 4 col from dual union all
select 3 col from dual union all
select 2 col from dual)
,x2 as (
select col col1 ,col col2, col col3 ,rownum rn
from x
)
select *
from x2
where rn between 2 and 3 --- rownum cannot be used in such a
condition!!!
;
Or, to be certain you get only the first row from a table satisfying a given condition:
select x_col1, x_col2 into v_col1, v_col2
from x_table
where ... --- logical conditions
and rownum<=1; --- rownum <= 1 avoids too_many_rows_exception if several rows satisfy the logical conditions given before
In Oracle, results sets have a non-deterministic order (i.e. they are unordered) unless you use an ORDER BY clause. Therefore, if you have a physical table, you need another column to provide the order (rather than relying on the ROWNUM pseudo-column, which may result in unexpected behaviour):
CREATE TABLE x (order_id, col) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 2 FROM DUAL UNION ALL
SELECT 3, 8 FROM DUAL UNION ALL
SELECT 4, 4 FROM DUAL UNION ALL
SELECT 5, 3 FROM DUAL UNION ALL
SELECT 6, 2 FROM DUAL;
If you want to find the rows that go up in succession, then you can use MATCH_RECOGNIZE for row-by-row pattern matching:
SELECT *
FROM x
MATCH_RECOGNIZE(
ORDER BY order_id
MEASURES
any_row.col AS col1,
FIRST(up.col) AS col2,
LAST(up.col) AS col3,
FIRST(order_id) AS start_order_id
PATTERN ( any_row up{2} )
DEFINE up AS ( col > PREV(col) )
)
or the LEAD analytic function:
SELECT *
FROM (
SELECT col AS col1,
LEAD(col, 1) OVER (ORDER BY order_id) AS col2,
LEAD(col, 2) OVER (ORDER BY order_id) AS col3,
order_id
FROM x
)
WHERE col2 > col1
AND col3 > col2;
Which both output:
COL1
COL2
COL3
START_ORDER_ID
1
2
8
1
fiddle
It looks like you want to find the rows where the value of the column is bigger than it is in both - the previous and next row. If so, you could try this:
WITH
tbl (ID, COL) AS -- Sample data (ID column is just to preserve order of the rows)
(
Select 1, 1 From Dual Union All
Select 2, 2 From Dual Union All
Select 3, 8 From Dual Union All
Select 4, 4 From Dual Union All
Select 5, 3 From Dual Union All
Select 6, 2 From DUAL
)
Select ID, COL, CASE WHEN COL > LAG(COL, 1) OVER(Order By ID) And COL > LEAD(COL, 1) OVER(Order By ID) THEN 'YES' END "BIGGER_THAN_PREV_AND_NEXT"
From tbl
Order By ID
ID COL BIGGER_THAN_PREV_AND_NEXT
---------- ---------- -------------------------
1 1
2 2
3 8 YES
4 4
5 3
6 2
... with a bit different sample data this will find the other row(s) that satisfy the condition ...
WITH
tbl (ID, COL) AS -- Sample data (ID column is just to preserve order of the rows)
(
Select 1, 1 From Dual Union All
Select 2, 2 From Dual Union All
Select 3, 8 From Dual Union All
Select 4, 4 From Dual Union All
Select 5, 5 From Dual Union All -- value of COL changed from 3 to 5
Select 6, 2 From DUAL
)
Select ID, COL, CASE WHEN COL > LAG(COL, 1) OVER(Order By ID) And COL > LEAD(COL, 1) OVER(Order By ID) THEN 'YES' END "BIGGER_THAN_PREV_AND_NEXT"
From tbl
Order By ID
ID COL BIGGER_THAN_PREV_AND_NEXT
---------- ---------- -------------------------
1 1
2 2
3 8 YES
4 4
5 5 YES
6 2
OR without ID - using ROWNUM (as in your question), - not adviseable, though...
WITH
tbl (COL) AS -- Sample data (without ID column)
(
Select 1 From Dual Union All
Select 2 From Dual Union All
Select 8 From Dual Union All
Select 4 From Dual Union All
Select 5 From Dual Union All
Select 2 From DUAL
)
Select COL, CASE WHEN COL > LAG(COL, 1) OVER(Order By ROWNUM) And COL > LEAD(COL, 1) OVER(Order By ROWNUM) THEN 'YES' END "BIGGER_THAN_PREV_AND_NEXT"
From tbl
COL BIGGER_THAN_PREV_AND_NEXT
---------- -------------------------
1
2
8 YES
4
5 YES
2
Any Order By clause added to the query could change the ROWNUM values and the result...

Connect by lead incremental values Oracle

I have this table
COL1 COL2
---------
A 1
B 5
C 12
D 14
And I would like to obtain this other one. This is, until the next col2 for each col1 is reached, a row with the COL1 and incremental values.
COL1 COL2
---------
A 1
A 2
A 3
A 4
B 5
B 6
B 7
B 8
B 9
B 10
B 11
C 12
C 13
D 14
EDIT: this is what I've tried so far. It seems I'm not far away from the solution but struggling to progress further than this.
WITH aux (
col1,
col2
) AS (
SELECT
'A',
1
FROM
dual
UNION ALL
SELECT
'B',
5
FROM
dual
UNION ALL
SELECT
'C',
12
FROM
dual
UNION ALL
SELECT
'D',
14
FROM
dual
), aux1 AS (
SELECT
a.*,
nvl(LEAD(a.col2) OVER(
ORDER BY
a.col2
), a.col2) h
FROM
aux a
)
SELECT
*
FROM
aux1
CONNECT BY level >= col2
AND level <= h;
testseq is the table containing your initial 4 rows. Use lead to find the stop value for col2 for each col1, and recursion to iterate and create the additional rows.
WITH xrows (col1, col2, lastcol2) AS (
SELECT t.*, LEAD(col2) OVER (ORDER BY col1) - 1
FROM testseq t
UNION ALL
SELECT col1, col2+1, lastcol2
FROM xrows t
WHERE col2 < lastcol2
)
SELECT col1, col2
FROM xrows
ORDER BY col1, col2
;
First you need to find the "next" number (whatever ordering you prefer) and then generate such number of rows with recursive subquery:
with a(code, num) as(
select 'A', 1 from dual union all
select 'B', 5 from dual union all
select 'C', 12 from dual union all
select 'D', 14 from dual
)
, b as (
select
a.*
, lead(num - 1, 1, num) over(order by code asc) as next_num
from a
)
select
b.code
, gen.val
from b
cross join lateral(
select num + level - 1 as val
from dual
connect by num + level - 1 <= next_num
) gen
order by 2 asc
Or if you prefer recursive CTE:
with a(code, num) as(
select 'A', 1 from dual union all
select 'B', 5 from dual union all
select 'C', 12 from dual union all
select 'D', 14 from dual
)
, b(code, next_num, val) as (
select
a.code
, lead(num - 1, 1, num) over(order by code asc) as next_num
, num
from a
union all
select
code
, next_num
, val + 1
from b
where val < next_num
)
select
b.code
, val
from b
order by 2 asc
CODE
VAL
A
1
A
2
A
3
A
4
B
5
B
6
B
7
B
8
B
9
B
10
B
11
C
12
C
13
D
14
livesql demo

Find rows with consecutive ones

I've two integer columns and need to display the rows with consecutive one's in the NUM column.
Sample data:
CREATE TABLE table_name ( ID, NUM ) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 1 FROM DUAL UNION ALL
SELECT 3, 1 FROM DUAL UNION ALL
SELECT 4, 2 FROM DUAL UNION ALL
SELECT 5, 1 FROM DUAL UNION ALL
SELECT 6, 2 FROM DUAL UNION ALL
SELECT 7, 2 FROM DUAL;
Expected Output:
ID NUM
-- ---
1 1
2 1
3 1
I have tried using self-joins and achieved the result:
WITH TAB (ID, NUM) AS
(
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 1 FROM DUAL UNION ALL
SELECT 3, 1 FROM DUAL UNION ALL
SELECT 4, 2 FROM DUAL UNION ALL
SELECT 5, 1 FROM DUAL UNION ALL
SELECT 6, 2 FROM DUAL UNION ALL
SELECT 7, 2 FROM DUAL
)
SELECT DISTINCT
T.ID,
T.NUM
FROM
TAB T
JOIN (
SELECT
T1.ID ID1,
T2.ID ID2,
T1.NUM,
COUNT(1) OVER(
PARTITION BY T1.NUM
) RN
FROM
TAB T1
JOIN TAB T2 ON ( T1.NUM = T2.NUM
AND T1.ID = T2.ID + 1 )
) T_IN ON ( ( T.ID = T_IN.ID1
OR T.ID = T_IN.ID2 )
AND T.NUM = T_IN.NUM
AND RN >= 2 ) -- THIS CONDITION IS TO RESTRICT CONSECUTIVES LESS THAN 3
ORDER BY
1
output:
db<>fiddle demo
Use analytic functions LAG or LEAD:
Oracle Setup:
CREATE TABLE table_name ( ID, NUM ) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 1 FROM DUAL UNION ALL
SELECT 3, 1 FROM DUAL UNION ALL
SELECT 4, 2 FROM DUAL UNION ALL
SELECT 5, 1 FROM DUAL UNION ALL
SELECT 6, 2 FROM DUAL UNION ALL
SELECT 7, 2 FROM DUAL;
Query:
SELECT id,num
FROM (
SELECT id,
num,
LAG( num ) OVER ( ORDER BY id ) AS prev_num,
LEAD( num ) OVER ( ORDER BY id ) AS next_num
FROM table_name
)
WHERE num = 1
AND ( num = prev_num
OR num = next_num )
Output:
ID | NUM
-: | --:
1 | 1
2 | 1
3 | 1
db<>fiddle here

Pivot from row to column - Oracle SQL

I have a table:
Table1
row_id var var_val
1 Test 1 123
1 Test 2 456
1 Test 3 789
1 Test 4 1234
2 Test 1 665t
2 Test 2 dsfs
2 Test 3 df
2 Test 4 sfd
3 Test 1 sfs
3 Test 2 sf
3 Test 3 sdfs
3 Test 4 sfsd
Here is the output:
Table2
row_id var1 var2
1 123 456
2 665t dsfs
3 sfs sf
For var1 - get value where var = "Test 1"
For var2 - get value where var = "Test 2"
Is there a way to use pivot or some way of extracting the variable for each row_id from the table1 as per above?
You can use conditional aggregation or a join:
select t11.row_id, t11.var, t12.var
from table1 t11 join
table1 t12
on t11.row_id = t12.row_id and
t11.var = 'Test 1' and
t12.var = 'Test 2'
Is there a way to use pivot...?
Sure:
select *
from table1
pivot (max(var_val) for var in ('Test 1' var1, 'Test 2' var2))
demo
You can use correlated subqueries and row_number() window analytic function together
with table1(row_id, var, var_val) as
(
select 1,'Test 1','123' from dual union all
select 1,'Test 2','456' from dual union all
select 1,'Test 3','789' from dual union all
select 1,'Test 4','1234' from dual union all
select 2,'Test 1','665t' from dual union all
select 2,'Test 2','dsfs' from dual union all
select 2,'Test 3','df' from dual union all
select 2,'Test 4','sfd' from dual union all
select 3,'Test 1','sfs' from dual union all
select 3,'Test 2','sf' from dual union all
select 3,'Test 3','sdfs' from dual union all
select 3,'Test 4','sfsd' from dual
), t2 as
(
select t.*, row_number() over (partition by var order by row_id) as rn
from table1 t
)
select distinct row_id,
(select var_val
from table1 t2
where t2.var = 'Test 1'
and t2.row_id = rn) as var1,
(select var_val
from table1 t2
where t2.var = 'Test 2'
and t2.row_id = rn) as var2
from t2
order by row_id
ROW_ID VAR1 VAR2
------ ---- ----
1 123 456
2 665t dsfs
3 sfs sf
Demo

How to find a specific pattern in some Access data?

I have ex 17 rows and 2 columns in my tabel. Like this:
ColA ColB
---- ----
X 1
X 2
X 3
X a
Y 1
Y 2
Y a
Z 4
Z 4
Z b
Q 1
Q 2
Q 3
Q a
W 4
W b
W 5
Is there a way to look for a pattern in colB of 1,2,3,a for the same value of ColA?
That would give me at output of X and Q.
Your sample data shows distinct rows. In that case, you can use this GROUP BY query.
SELECT y.ColA
FROM YourTable AS y
WHERE y.ColB In ('1','2','3','a')
GROUP BY y.ColA
HAVING Count(*) = 4;
If your actual data might include duplicate rows, you can start with SELECT DISTINCT in a subquery before applying the GROUP BY.
SELECT sub.ColA
FROM
(
SELECT DISTINCT y.ColA, y.ColB
FROM YourTable AS y
WHERE y.ColB In ('1','2','3','a')
) AS sub
GROUP BY sub.ColA
HAVING Count(*) = 4;
(I've assumed that your table is named [PatternData].)
If you use Allen Browne's ConcatRelated function you can create a query to "string together" all of the [ColB] values for each distinct value of [ColA] like this...
SELECT
ColA,
ConcatRelated("ColB", "PatternData", "ColA=""" & ColA & """" , "ColB", "") AS ColB_values
FROM (SELECT DISTINCT ColA FROM PatternData)
...returning...
ColA ColB_values
---- -----------
Q 123a
W 45b
X 123a
Y 12a
Z 44b
Then you can use the above query as the basis for a query to find the [ColA] values with the desired pattern
SELECT ColA
FROM
(
SELECT
ColA,
ConcatRelated("ColB", "PatternData", "ColA=""" & ColA & """" , "ColB", "") AS ColB_values
FROM (SELECT DISTINCT ColA FROM PatternData)
)
WHERE ColB_values = "123a"
...returning...
ColA
----
Q
X
Below is one possible solution:
WITH
data AS (
SELECT 'X' cola, '1' colb FROM dual
UNION ALL SELECT 'X' cola, '2' FROM dual
UNION ALL SELECT 'X', '3' FROM dual
UNION ALL SELECT 'X', 'a' FROM dual
UNION ALL SELECT 'Y', '1' FROM dual
UNION ALL SELECT 'Y', '2' FROM dual
UNION ALL SELECT 'Y', 'a' FROM dual
UNION ALL SELECT 'Z', '4' FROM dual
UNION ALL SELECT 'Z', '4' FROM dual
UNION ALL SELECT 'Z', 'b' FROM dual
UNION ALL SELECT 'Q', '1' FROM dual
UNION ALL SELECT 'Q', '2' FROM dual
UNION ALL SELECT 'Q', '3' FROM dual
UNION ALL SELECT 'Q', 'a' FROM dual
UNION ALL SELECT 'W', '4' FROM dual
UNION ALL SELECT 'W', '5' FROM dual
UNION ALL SELECT 'W', 'b' FROM dual
),
data_agg AS (
SELECT cola, listagg(colb) WITHIN GROUP (ORDER BY colb) AS agg_colb
FROM data
GROUP BY cola
)
SELECT cola
FROM data_agg da
WHERE EXISTS (SELECT 1
FROM data_agg
WHERE cola != da.cola
AND agg_colb = da.agg_colb
)
;
Edit: ops, for some reason I thought you were using Oracle... Hopefully, you'll be able to modify above query to be able to use it.