How to make pl/sql code to Ansi-Sql? - sql

My question may be not challenging for sql expert. i want to rewrite my sql as a ansi-sql. How can i change below sql to ansi-sql in Oracle?
select *
from TEST r
start with r.childid=#CHILDID
connect by prior r.PARENTID=r.childid and r.recordstatus=1

The ANSI SQL equivalent would be a recursive common table expression:
with recursive tree as (
select *
from test
where childid = .... --<< this is the START WITH part
union all
select child.*
from test child
join tree parent ON child.parentid = parent.childid and child.recordstatus = 1 --<< this is the CONNECT BY part
)
select *
from tree
I'm not 100% if you also want to apply the recordstatus = 1 condition to the recursion start.
Oracle doesn't comply with the standard here, and you are not allowed to use the recursive keyword.
So you need to remove recursive from the query above (the same is true for SQL Server)
More details about recursive common table expressions (called "subquery factoring" in Oracle) can be found in the manual:
https://docs.oracle.com/database/121/SQLRF/statements_10002.htm#SQLRF55268

Related

Why isn't a CTE evaluated one time?

If we take the following:
WITH tbl AS (
SELECT RAND() AS a
) SELECT * FROM tbl AS tbl1, tbl AS tbl2
Why does this return two values instead of one? I thought a CTE is basically evaluated one time at the start and then it is used wherever it is needed? Or is my understanding of persistency of a CTE incorrect? Or does the implementation from db to db differ?
Compared with MySQL where it returns a single persisted-value:
According to the docs
BigQuery only materializes the results of recursive CTEs, but does not materialize the results of non-recursive CTEs inside the WITH clause. If a non-recursive CTE is referenced in multiple places in a query, then the CTE is executed once for each reference.
So what you see is expected at least for BigQuery.

Using calculation with an an aliased column in ORDER BY

As we all know, the ORDER BY clause is processed after the SELECT clause, so a column alias in the SELECT clause can be used.
However, I find that I can’t use the aliased column in a calculation in the ORDER BY clause.
WITH data AS(
SELECT *
FROM (VALUES
('apple'),
('banana'),
('cherry'),
('date')
) AS x(item)
)
SELECT item AS s
FROM data
-- ORDER BY s; -- OK
-- ORDER BY item + ''; -- OK
ORDER BY s + ''; -- Fails
I know there are alternative ways of doing this particular query, and I know that this is a trivial calculation, but I’m interested in why the column alias doesn’t work when in a calculation.
I have tested in PostgreSQL, MariaDB, SQLite and Oracle, and it works as expected. SQL Server appears to be the odd one out.
The documentation clearly states that:
The column names referenced in the ORDER BY clause must correspond to
either a column or column alias in the select list or to a column
defined in a table specified in the FROM clause without any
ambiguities. If the ORDER BY clause references a column alias from
the select list, the column alias must be used standalone, and not as
a part of some expression in ORDER BY clause:
Technically speaking, your query should work since order by clause is logically evaluated after select clause and it should have access to all expressions declared in select clause. But without looking at having access to the SQL specs I cannot comment whether it is a limitation of SQL Server or the other RDBMS implementing it as a bonus feature.
Anyway, you can use CROSS APPLY as a trick.... it is part of FROM clause so the expressions should be available in all subsequent clauses:
SELECT item
FROM t
CROSS APPLY (SELECT item + '') AS CA(item_for_sort)
ORDER BY item_for_sort
It is simply due to the way expressions are evaluated. A more illustrative example:
;WITH data AS
(
SELECT * FROM (VALUES('apple'),('banana')) AS sq(item)
)
SELECT item AS s
FROM data
ORDER BY CASE WHEN 1 = 1 THEN s END;
This returns the same Invalid column name error. The CASE expression (and the concatenation of s + '' in the simpler case) is evaluated before the alias in the select list is resolved.
One workaround for your simpler case is to append the empty string in the select list:
SELECT
item + '' AS s
...
ORDER BY s;
There are more complex ways, like using a derived table or CTE:
;WITH data AS
(
SELECT * FROM (VALUES('apple'),('banana') AS sq(item)
),
step2 AS
(
SELECT item AS s FROM data
)
SELECT s FROM step2 ORDER BY s+'';
This is just the way that SQL Server works, and I think you could say "well SQL Server is bad because of this" but SQL Server could also say "what the heck is this use case?" :-)

Does Oracle always resolve CTE with clauses even if they are not used in the result set

If I have an Oracle SQL query like this:
with
query1 as (
select * from animals where type = 'dog'
),
query2 as (
select * from animals where type = 'cat'
)
select * from query1;
Will the DBMS actually do the work of resolving/running query2, or does Oracle know that query2 is not required by the final output, so the work of that CTE/with should be skipped?
Oracle version is 12c Enterprise.
I was going going to say "it's up to the optimizer" or "this is hard to answer" or "you need to look at the execution plan". But coming up with a single example where the code is not run is sufficient.
So here is an example demonstrating that at least one version of Oracle for at least one example does not evaluate the CTE:
with query1 as (
select * from animals where type = 'dog'
),
query2 as (
select a.*, type + 1 from animals a
)
select * from query1;
The second CTE would generate an error if it were evaluated.
This is not a guarantee, of course, that Oracle always ignores unused CTEs. And there could possibly be more arcane explanations for the behavior, but non-evaluation seems like the simplest.

SQL - WITH RECURSIVE doesn't work, when there is another query before

I have a (postgresql) query, that goes like
with
recursive account_tree as (<recursive function depending on path>), -- 1
path as (<some other query>) -- 2
select * from account_tree;
This works perfectly fine.
But when I reorder the with queries to that
with
path as (<some other query>), -- 2
recursive account_tree as (<recursive function depending on path>) -- 1
select * from account_tree;
it suddenly shows a syntax error. This behaviour doesn't occur, when I have standard non-recursive queries. With non-recursive queries, I can order them, as they please me.
Why does it do that?
The recursive keyword always goes right behind WITH regardless which of the CTE is actually the recursive one:
with recursive path as (
<some other query>
), account_tree as (
<recursive function depending on path>
)
select *
from account_tree;
recursive refers to the entire with clause. So just use:
with recursive path as (<some other query>), -- 2
What recursive really means is that Postgres will search for "table" names first as CTEs and then as tables/views in the recursive part of the CTE. The issue is really resolving identifiers. It has no effect on other CTEs.

recursive query with select * raises ORA-01789

This is a minimized version of complex recursive query. The query works when columns in recursive member (second part of union all) of recursive CTE are listed explicitly:
with t (c,p) as (
select 2,1 from dual
), rec (c,p) as (
select c,p from t
union all
select t.c,t.p from rec join t on rec.c = t.p
)
select * from rec
I don't get why error ORA-01789: query block has incorrect number of result columns is raised when specified t.* instead.
with t (c,p) as (
select 2,1 from dual
), rec (c,p) as (
select c,p from t
union all
select t.* from rec join t on rec.c = t.p
)
select * from rec
Why t.* is not equivalent to t.c,t.p here? Could you please point me to documentation for any reasoning?
UPDATE: reproducible on 11g and 18 (dbfiddle).
I finally asked on AskTom forum and according to response from Oracle expert Connor McDonald, this behavior is in compliance with documentation, namely the sentence The number of column aliases following WITH query_name and the number of columns in the SELECT lists of the anchor and recursive query blocks must be the same which can be found in this paragraph.
The point is, the expansion of star expression is done after checking whether the numbers of columns are same. Hence one must list columns explicitly, shortening to star is not possible.
Seems like there could be some kind of bug to me. I modified the query slightly just to test various cases and am now able to reproduce an ORA-00600 error in my Oracle 19.6.0.0.0 database! Running the problematic query on apex.oracle.com or on livesql.oracle.com (which is running 19.8.0.0.0) also results in errors. Reporting it to Oracle now!