Oracle SQL connect by level - sql

Can anyone explain the behavior of the below query:-
select level,t.*
from
( select 'one','two'from dual
union all
select 'one','two'from dual
) t
connect by level<=2
There are 2 rows in the inner query. I was expecting 4 rows of output, but i get 6 rows of output. Why is it so and how does this work?

The query starts with one of your two rows and adds both rows, then it continues with the second row and adds both rows again.
Change your query like this:
select level,t.*
from
( select 'one' from dual
union all
select 'two' from dual
) t
connect by level<=2;
This makes it easier to see what happens:
1 one
2 one
2 two
1 two
2 one
2 two

Read this http://docs.oracle.com/cd/B19306_01/server.102/b14200/queries003.htm
When level <= 1, you will get each of the records 1 time.
When level <= 2, then you will get each level 1 time (for level 1) + the number of records in the table
(That means for this condition 2 records having level 1 + 2*2 records having level 2. This is the reason you are getting 6 records.)

CONNECT BY LEVEL gives following number of rows
x+x2+x3+x4+...x^n = Sx^n
where n is number of LEVEL and x is number of rows in a table

Related

Why aren't there duplicates in a recursive SQL "range" query?

For reference, here it is:
WITH range_10 AS (
SELECT 1 AS n
UNION ALL
SELECT n+1 AS n
FROM range_10
WHERE n < 10
)
SELECT * FROM range_10;
As we all know and love, this generates a single column n with values from one through ten. However, I am confused about why there aren't duplicate values of n in the output. Here's my understanding of how this query is executed:
Iteration 1
range_10 begins empty
range_10 ends as:
1
Iteration 2
range_10 ends as:
1
2
Iteration 3
range_10 ends as:
1
2
2
3
Why? Because we SELECT'ed n+1 for all rows of the table (there were two of them), then did a UNION ALL, which doesn't remove duplicates. To make this more clear:
Iteration 4
1
2
2
3
2
3
3
4
Does UNION ALL really mean UNION in the context of recursive queries? Or am I missing something more fundamental about recursive queries?
As discussed here the input to each recursive execution is just the result of the previous level, not the cumulative result so far.
The anchor level emits 1. Running the recursive part on that produces 2. Running the recursive part on 2 produces 3, and so on.

Dual command in Big Query

"I am trying to achieve dual command in big query"
"I tried using the temp tables but not able to achieve it"
Oracle query: SELECT LEVEL - 1 F FROM
DUAL CONNECT BY LEVEL <= 2
"I expect the output in below format "
F
1
2
I have salary table with salaries : 50$ and 200$
I want to have duplicate of each row : 50$ ,-50$,200$ and -200$ is the output which i am expecting like 4 rows in total
You can use
SELECT
1
from (
select SESSION_USER())
to return a resultset with just one row.
Since BigQuery doesn't support CONNECT BY clause and since you want to get a positive and negative values from your data, you could try using a simple query like this one:
SELECT my_value FROM `project.dataset.table`
UNION ALL
SELECT -my_value FROM `project.dataset.table`
Notice the - in the second query as it'll give you negative values.
Hope it helps.

Splitting every row of varchar column and create new row for every split part

I have a table called "Table" and attribute named "History". This history attribute has values like:
1 Finished (30-05-2018);
2 InProgress (25-05-2018); Rejected(26-05-2018); InProgress (28-05-2018); Finished (30-05-2018);
3 InProgress (25-05-2018); Finished (30-05-2018);
I want to split this attribute by the semicolumn (;) and create a new row for every history part. So for the rows in the code/example above it should create 7 rows. I have managed to do this for one row by the code below. The problem is that I want to do this for every row in this table. Here occurs a problem: when I remove the WHERE condition in the WITH I get way to many resultrows and also a lot of NULL values. What am I doing wrong?
WITH DATA AS
( SELECT "WorkID" w,"History" his FROM Table
where "WorkID" = 75671
)
SELECT w, trim(regexp_substr(his, '[^;]+', 1, LEVEL)) his
FROM DATA
CONNECT BY regexp_substr(his , '[^;]+', 1, LEVEL) IS NOT NULL
Here's a full working example where the regex looks for a delimiter of a semi-colon followed by a space OR the end of the line:
SQL> WITH Tbl(WorkID, History) AS(
select 1, 'Finished (30-05-2018);' from dual union all
select 2, 'InProgress (25-05-2018); Rejected(26-05-2018); InProgress (28-05-2018); Finished (30-05-2018);' from dual union all
select 3, 'InProgress (25-05-2018); Finished (30-05-2018);' from dual
)
select WorkID, regexp_substr(History, '(.*?)(; |;$)', 1, level, NULL, 1) history
from Tbl
connect by regexp_substr(History, '(.*?)(; |;$)', 1, level) is not null
and prior WorkID = WorkID
and prior sys_guid() is not null;
WORKID HISTORY
---------- -------------------------
1 Finished (30-05-2018)
2 InProgress (25-05-2018)
2 Rejected(26-05-2018)
2 InProgress (28-05-2018)
2 Finished (30-05-2018)
3 InProgress (25-05-2018)
3 Finished (30-05-2018)
7 rows selected.
If you want to use a connect by against many rows to split a column into multiple rows, you need to have a couple of extra things - a means of telling Oracle to loop over the same row, and a check to make sure it knows which prior row to use.
That means your query would become something like:
select "WorkID" w,
"History" his
from table
connect by prior "WorkID" = "WorkID" -- I'm assuming that "WorkID" uniquely identifies a row; if not, add in the additional columns that identify a row
and regexp_substr("History", '[^;]+', 1, level) is not null
and prior sys_guid() is not null;

Help me to understand a CTE query using the dual table and connect by level

Trying to interpret this query...
SELECT blah1, blah2, ... FROM (SELECT level-1 HOUR_ID
FROM dual
CONNECT BY level <= 24
) LU_HOUR
what does blah values contain? what does level mean to a dual table?
dual is a dummy table with one column (named dummy) and one row (containing X for the dummy column).
CONNECT BY level <= 24 is a hierarchical query term which means that for each match at some level you connect each possible match at the next level provided that your condition is true. Here, LEVEL is automatically incremented, and you can refer to the parent match with PRIOR.
This is a trick to forge a query result with 24 rows, from 0 to 23.

sql query on dual

This:
select *
from dual
connect by level <= i
...will return result as
1
2
3
...
i
Can the query be modified to get result row wise? i.e
1 2 3 .....i
The functionality you're after is called "pivot"--it's converting row data into columnar data. The opposite is called "unpivot". There is PIVOT/UNPIVOT syntax, but it isn't available until Oracle 11g.
On Oracle 9i+, CASE statements are used while prior to that you need to use DECODE to construct the logic so the values come out in columns. Here's an example for addressing if the limit to LEVEL is five on Oracle 9i+:
SELECT MAX(CASE WHEN LEVEL = 1 THEN LEVEL END) AS one,
MAX(CASE WHEN LEVEL = 2 THEN LEVEL END) AS two,
MAX(CASE WHEN LEVEL = 3 THEN LEVEL END) AS three,
MAX(CASE WHEN LEVEL = 4 THEN LEVEL END) AS four,
MAX(CASE WHEN LEVEL = 5 THEN LEVEL END) AS five
FROM DUAL
CONNECT BY LEVEL <= 5
The aggregate function MAX is necessary to "flatten" the resultset into a single row/record.
If you got this far, you're thinking "but I don't want to have to specify for every column--I want it to be dynamic based on i...". There's two issues:
There is a limit to the number of columns - see ORA-01792 maximum number of columns in a table or view is 1000. That goes for 8i & 9i, anyways. And frankly, needing to generate that many columns raises serious questions about what you're trying to do with the resultset, if there might be a better approach...
Dynamic requirements means using dynamic SQL
Try this:
select trim(both ',' from sys_connect_by_path(level, ',')) as string
from dual
where level = 100
connect by level <= 100
update: Testing the output:
SQL>
SQL> select trim(both ',' from sys_connect_by_path(level, ',')) as string
2 from dual
3 where level = 20
4 connect by level <= 20
5 /
STRING
--------------------------------------------------------------------------------
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
another update: Guess I haven't read all comments well enough before posting, sorry. :) If you need to select each number as a separate column, then yes, see, OMG Ponies' answer - it's either pivot or dynamic SQL. :)