Can one not reference a CTE twice in a query in Oracle? - oracle

I have a chain of CTEs, and at the end I want to select from the last one twice. Oracle lets me do either of the selects but not a union of both of them (even if I do "select * from (select union select)". The only thing I could narrow down the problem to was referring to "runinfo" in two selects, but that in and of itself is not the problem as Shannon shows.
WITH lastTen AS (
SELECT id
FROM (SELECT autobuild_id, id, rank() OVER (PARTITION BY autobuild_id ORDER BY id DESC) as rank
FROM runs
WHERE status='FINISHED' AND type='FULL' AND build_failed in ('n', 'N'))
WHERE rank <= 10
),
recentAvg AS (
SELECT autobuild_id, avg(elapsed) avgtime
FROM runs
JOIN lastTen ON (runs.id = lastTen.id)
GROUP BY autobuild_id
),
runinfo AS (
SELECT autobuildid, runid, changelist, status, age
FROM (
SELECT runs.autobuild_id autobuildid, runs.id runid, changelist, runs.status status, runs.create_date, a.avgtime,
CASE WHEN status = 'RUNNING' THEN TO_NUMBER(sysdate - start_date)*86400 -- in seconds to compare to elapsed
WHEN status = 'TO BE' THEN TO_NUMBER(sysdate - create_date) -- in days
END AS age
FROM runs
LEFT JOIN recentAvg a ON (runs.autobuild_id = a.autobuild_id)
)
WHERE (status = 'RUNNING' AND age > avgtime * 1.5)
OR (status = 'TO BE' AND age > 1)
ORDER BY autobuildid, runid DESC
),
running AS (
SELECT autobuilds.id, name, runid, changelist, runinfo.status, age
FROM runinfo
JOIN autobuilds ON (runinfo.autobuildId=autobuilds.id)
WHERE runinfo.status='RUNNING'
),
tobe AS (
SELECT autobuildid, name, runid, changelist, status, age
FROM (SELECT autobuildid, name, runid, changelist, runinfo.status, age, RANK() OVER (PARTITION BY autobuildid ORDER BY runid DESC) AS rank
FROM runinfo
JOIN autobuilds ON (runinfo.autobuildid=autobuilds.id)
WHERE runinfo.status='TO BE')
WHERE rank=1
)
SELECT * FROM running
UNION ALL
SELECT * FROM tobe

What is the simpliest query that doesn't work for you?
What is the complete error message?
Oracle does allow a CTE to be referenced twice, but I am not understanding what you are attempting:
SQL> with a as (select * from dual)
2 , b as (select * from a)
3 , c as (select * from b)
4 select *
5 from b, c
6 where b.dummy = c.dummy;
D D
- -
X X

Related

Getting unsupported subquery type when trying to insert into a table

I have a query as follows:
INSERT ALL
WHEN NEWEST_ID IS NOT NULL AND
(SELECT COUNT(1) FROM (
SELECT *
FROM MY_TABLE
WHERE ID = NEWEST_ID
QUALIFY ROW_NUMBER() OVER (PARTITION BY ID ORDER BY OFFSET DESC) = 1
)
WHERE ACTIVE) = 0 THEN
INTO MY_TABLE VALUES(
NEWEST_ID,
CURRENT_DATE,
NAME,
FALSE
)
SELECT * FROM TEST_TABLE;
However I am getting an unsupported subquery type error when I try to write the select count(1) or count(*) from the subquery. Why is this so?/ How can I change this? In my subquery I am just trying to get the first row in a group of IDs after ordering by the descending offset. And then I am trying to determine whether the ACTIVE column from that result row is TRUE.
the QUALIFY can have the WHERE ACTIVE added to it:
SELECT COUNT(1)
FROM (
SELECT 1
FROM MY_TABLE as x
WHERE x.ID = NEWEST_ID
QUALIFY ROW_NUMBER() OVER (PARTITION BY x.ID ORDER BY x.OFFSET DESC) = 1 AND x.ACTIVE
)
this the inner only keeps the "last" offset per id AND if it is also active
the count = 0 can be turned into a NOT EXIST like:
INSERT ALL
WHEN newest_id IS NOT NULL
AND NOT EXISTS (
SELECT 1
FROM my_table AS x
WHERE x.id = newest_id
QUALIFY ROW_NUMBER() OVER (PARTITION BY x.id ORDER BY x.offset desc) = 1 AND x.active
) THEN
INTO my_table VALUES( newest_id, current_date, name, false)
SELECT * FROM TEST_TABLE;
"in theory"
the other option is to push that into a CTE:
WITH last_id_active AS (
SELECT x.id
FROM my_table AS x
QUALIFY
ROW_NUMBER() OVER (PARTITION BY x.id ORDER BY x.offset desc) = 1
AND x.active
)
which would need to be on the SELECT like:
INSERT ALL
WHEN tt.newest_id IS NOT NULL
AND lia.id IS NOT NULL THEN
INTO my_table VALUES( tt.newest_id, tt.current_date, tt.name, false)
WITH last_id_active AS (
SELECT x.id
FROM my_table AS x
QUALIFY
ROW_NUMBER() OVER (PARTITION BY x.id ORDER BY x.offset desc) = 1
AND x.active
)
SELECT * FROM TEST_TABLE as tt
LEFT JOIN last_id_active as lia
ON tt.newest_id = lia.id;
*based on theory
which could also be simplyfied, as lia.id will be null when tt.newest_id is also null, thus the INSERT_ALL could be simplefied more as:
INSERT ALL
WHEN lia.id IS NOT NULL THEN
INTO my_table VALUES( tt.newest_id, tt.current_date, tt.name, false)
WITH last_id_active AS (
SELECT x.id
FROM my_table AS x
QUALIFY
ROW_NUMBER() OVER (PARTITION BY x.id ORDER BY x.offset desc) = 1
AND x.active
)
SELECT * FROM TEST_TABLE as tt
LEFT JOIN last_id_active as lia
ON tt.newest_id = lia.id;

Oracle adding a subquery in a CTE

I have the following setup, which works fine and generates output as expected.
I'm trying to add the locations subquery into the CTE so my output will have a random location_id for each row.
The subquery is straight forward and should work but I am getting syntax errors when I try to place it into the 'data's CTE. I was hoping someone could help me out.
CREATE TABLE employees(
employee_id NUMBER(6),
emp_name VARCHAR2(30)
);
INSERT INTO employees(
employee_id,
emp_name
) VALUES
(1, 'John Doe');
INSERT INTO employees(
employee_id,
emp_name
) VALUES
(2, 'Jane Smith');
INSERT INTO employees(
employee_id,
emp_name
) VALUES
(3, 'Mike Jones');
CREATE TABLE locations AS
SELECT level AS location_id,
'Door ' || level AS location_name
FROM dual
CONNECT BY level <=
with rws as (
select level rn from dual connect by level <= 5 ),
data as ( select e.*,round (dbms_random.value(1,5)
) n from employees e)
select employee_id,
emp_name,
trunc (sysdate) + dbms_random.value (0, 5) AS random_date
from rws
join data d on rn <= n
order by employee_id;
-- trying to make this work
with rws as ( select level rn from dual connect by level <= 5 ),
data as ( select e.*, loc.location_id = (
select location_id
from locations order by dbms_random.value()
fetch first 1 row only
),
round (dbms_random.value(1,5)
) n from employees e )
select employee_id,
emp_name,
trunc (sysdate) + dbms_random.value (0, 5) AS random_date
from rws
join data d on rn <= n
order by employee_id;
You need to alias the subquery column expression, rather than trying to assign it to a [variable] name. So instead of this:
with rws as ( select level rn from dual connect by level <= 5 ),
data as ( select e.*, loc.location_id = (
select location_id
from locations order by dbms_random.value()
fetch first 1 row only
),
round (dbms_random.value(1,5)
) n from employees e )
you would do this:
with rws as (
select level rn
from dual
connect by level <= 5
),
data as (
select e.*,
(
select location_id
from locations
order by dbms_random.value()
fetch first 1 row only
) as location_id,
round (dbms_random.value(1,5)) as n
from employees e
)
db<>fiddle
But yes, you'll get the same location_id for each row, which probably isn't what you want.
There are probably better ways to avoid it (or to approach whatever you're actually trying to achieve) but one option is to force the subquery to be correlated by adding something like:
where location_id != -1 * e.employee_id
db<>fiddle
although that might be expensive. It's probably worth asking a new question about that specific aspect.
I am getting the same location_id for every employee_id, which I don't want either.
The subquery is in the wrong place then; move it to the main query, and correlate against both ID and n:
with rws as (
select level rn
from dual
connect by level <= 5
),
data as (
select e.*,
round (dbms_random.value(1,5)) as n
from employees e
)
select d.employee_id,
d.emp_name,
(
select location_id
from locations
where location_id != -1 * d.employee_id * d.n
order by dbms_random.value()
fetch first 1 row only
) as location_id,
trunc (sysdate) + dbms_random.value (0, 5) AS random_date
from rws r
join data d on r.rn <= d.n
order by d.employee_id;
db<>fiddle
Or move the location part to a new CTE, I suppose, with its own row number; and join that on one of your other generated values.

Leetcode Oracle - ORA-00923: FROM keyword not found where expected

I am trying to solve this problem using Oracle SQL but I keep getting this error -
ORA-00923: FROM keyword not found where expected
link to problem - https://leetcode.com/problems/department-top-three-salaries/submissions/
my solution until now - just to query the data -
with temp as
(
select d.Name as Department,
e.Name as Employee,
e.Salary as Salary
from employee e
join department d
on e.DepartmentId = d.Id
)
select *
, rank() over (partition by department order by salary desc) as rr
from temp
but if i simply run this then it works fine -
with temp as
(
select d.Name as Department,
e.Name as Employee,
e.Salary as Salary
from employee e
join department d
on e.DepartmentId = d.Id
)
select *
from temp
And if I run this then it runs okay -
select department, employee, salary
from
(
select A.* , dense_rank() over (partition by department order by salary desc) as rr
from
(
select d.Name as Department,
e.Name as Employee,
e.Salary as Salary
from employee e
join department d
on e.DepartmentId = d.Id
) A
) B
where rr <= 3
does it mean i cannot use the cascading with statements in oracle ?
For instance, cant I write -
with temp as
(
select col1, col2 from table
)
, temp1 as
(
select *, "hello" as col3
from temp
)
select *
from temp1
in Oracle?
If you need to select only all the columns then * without the alias is fine. But if You need to give alias of the table wherever you want to select all the columns of the table using * and also another expression in SELECT clause.
with TEMP AS
( SELECT
COL1,
COL2
FROM table )
, TEMP1 AS
(SELECT
T.*, -- alias here
"hello" AS COL3
FROM TEMP T
)
select * FROM TEMP1;
with temp as
(
select d.Name as Department,
e.Name as Employee,
e.Salary as Salary
from employee e
join department d
on e.DepartmentId = d.Id
)
select T.* -- alias here
, rank() over (partition by department order by salary desc) as rr
from temp T;

How to get count by using UNION operator

i'm trying to get total count by using UNION operator but it gives wrong count.
select count(*) as companyRatings from (
select count(*) hrs from (
select distinct hrs from companyA
)
union
select count(*) financehrs from (
select distinct finance_hrs from companyB
)
union
select count(*) hrids from (
select regexp_substr(hr_id,'[^/]+',1,3) hrid from companyZ
)
union
select count(*) cities from (
select regexp_substr(city,'[^/]+',1,3) city from companyY
)
);
individual query's working fine but total count not matching.
individual results here: 12 19 3 6
present total count: 31
Actual total count:40.
so there is any alternate solution without UNION operator?
To add values you'd use +. UNION is to add data sets.
select
(select count(distinct hrs) from companyA)
+
(select count(distinct finance_hrs) from companyB)
+
(select count(regexp_substr(hr_id,'[^/]+',1,3)) from companyZ)
+
(select count(regexp_substr(city,'[^/]+',1,3)) from companyY)
as total
from dual;
But I agree with juergen d; you should not have separate tables per company in the first place.
Edit. Updated query using Sum
select sum(cnt) as companyRatings from
(
select count(*) as cnt from (select distinct hrs from companyA)
union all
select count(*) as cnt from (select distinct finance_hrs from companyB)
union all
select count(*) as cnt from (select regexp_substr(hr_id,'[^/]+',1,3) hrid from companyZ)
union all
select count(*) as cnt from (select regexp_substr(city,'[^/]+',1,3) city from companyY)
)
Previous answer:
Try this
SELECT (
SELECT count(*) hrs
FROM (
SELECT DISTINCT hrs
FROM companyA
)
)
+
(
SELECT count(*) financehrs
FROM (
SELECT DISTINCT finance_hrs
FROM companyB
)
)
+
(
SELECT count(*) hrids
FROM (
SELECT regexp_substr(hr_id, '[^/]+', 1, 3) hrid
FROM companyZ
)
)
+
(
SELECT count(*) cities
FROM (
SELECT regexp_substr(city, '[^/]+', 1, 3) city
FROM companyY
)
)
AS total_count
FROM dual

Please help me to optimize the below mentioned script as the same table(i.e. Incident_Audit_log) has utilized multiple times?

select A.*
from Incident_Audit_log a where incident_audit_log_id in
(select top 1 incident_audit_log_id from Incident_Audit_log b
where b.incident_id=a.incident_id and b.status_did=a.status_did
and b.tracking_code_did = (select tracking_code_did
from Incident_Audit_log where update_date = (select MAX(update_date)
from Incident_Audit_log where Status_did in (103, 1035)
and incident_id = b.incident_id)
and incident_id = b.incident_id)
order by update_date asc)
I am not sure what you want to achieve but I guess that you want to extract row with new newest update and status_did equal to 13 and 1035.
In that case this should work:
select *
from (
select ROW_NUMBER() OVER(ORDER BY update_date DESC) AS rn,
*
from Incident_Audit_log
where status_did in (103, 1035)
) as SubQueryAlias
where rn = 1
In case not , provide more info.

Resources