How to solve this error - ORA-22818: subquery expressions not allowed? - sql

SELECT first_name, (select min(salary) from hr.employees where department_id = 103), (select max(salary) from hr.employees where department_id = 103)
from hr.employees where hr.employees.department_id = 103;
But it shows an error:
subquery expressions not allowed here
Do you know how to solve the error?

LEFT JOIN a derived table (aka inline view) that returns each department's min and max salary:
SELECT e.first_name,
e2.min_sal,
e2.max_sal
from hr.employees e
left join (select department_id, min(salary) min_sal, max(salary) max_sal
from hr.employees
group by department_id) e2
on e.department_id = e2.department_id
where e.department_id = 103;

The topic is kinda old but I've found neat "hack" for this oracle limitation.
If you create a normal view and then create a materialized view using that view then oracle will allow it.
Example1 (this will throw error):
DROP MATERIALIZED VIEW VW_TEST2;
CREATE MATERIALIZED VIEW VW_TEST2 AS
SELECT ROWNUM AS rowNumber, vw.* from (
SELECT 1 as ID, 'Test1' as Txt from dual
union all
select (select 2 from dual) as ID, 'Test2' as Txt from dual) vw;
Example2 (this will not throw error)
CREATE OR REPLACE VIEW VW_TEST1 AS
SELECT ROWNUM AS rowNumber, vw.* from (
SELECT 1 as ID, 'Test1' as Txt from dual
union all
select (select 2 from dual) as ID, 'Test2' as Txt from dual) vw;
DROP MATERIALIZED VIEW VW_TEST2;
CREATE MATERIALIZED VIEW VW_TEST2 AS
SELECT * FROM VW_TEST1;

Related

why query is showing 'invalid relational operator'

select *
from employees
where department_id,salary in (
select department_id,max(salary)
from employees group by department_id
)
You want tuple comparison - you need to surround the tuple of columns on the left side of in with parentheses:
select *
from employees
where (department_id,salary) in (
select department_id, max(salary) from employees group by department_id
)
Note that this top-1-per-group query can be more efficiently phrased with window functions:
select *
from (
select e.*, rank() over(partition by department_id order by salary desc nulls last) rn
from employees e
) t
where rn = 1

SQL Nested select in From caluse

I am confuse, can we write SELECT statement in FROM clause and if yes why can it be.
SELECT v.employee_id, v.last_name, v.lev
FROM
(SELECT employee_id, last_name, LEVEL lev
FROM employees v
START WITH employee_id = 100
CONNECT BY PRIOR employee_id = manager_id) v
WHERE (v.employee_id, v.lev) IN
(SELECT employee_id, 2 FROM employees);
The answer is yes, you can use. The select clause in the from will act as a inline view(consider it as a temporary table that databse creates to hold the results).
For example:
SELECT sdt sdat
FROM (SELECT SYSDATE sdt FROM dual);
In the above query, SELECT SYSDATE sdt FROM dual is executed first, and output would be like:
sdt
---
08-Dec-2016 16:20:56
Then, using this as a temp table(which is called an inline view in such cases), oracle will execute your outer select on this data. Hence SELECT sdt FROM... executes, giving the final output as:
sdat
----
08-Dec-2016 16:20:56
I think you are looking for a recursive cte in sql server which would be something like.....
WITH X (employee_id, last_name, lev )
AS (
SELECT employee_id, last_name, 0 AS lev
FROM employees
WHERE manager_id IS NULL
UNION ALL
SELECT e.employee_id, e.last_name , lev + 1
FROM employees e
INNER JOIN x ON x.employee_id = e.manager_id
)
SELECT v.employee_id, v.last_name, v.lev
FROM X
WHERE lev = 2

Applying Aggregate Functions On all Columns

I have an Employee table with Salary. I want to list Salary - Avg(Salary) for every Employees. Can someone please help me with the SQL query for the same.
You can do this using window functions:
select e.*,
(salary - avg(salary) over ()) as diff
from employees e;
You could use an inline view to return a single row with the average salary. and join that row back to employee table.
Something like this:
SELECT e.emp_id
, e.salary
, e.salary - a.avg_salary
FROM employee e
CROSS
JOIN ( SELECT AVG(t.salary) AS avg_salary
FROM employee t
) a
ORDER BY e.emp_id
Just a couple other variations; the others already posted should serve the purpose quite well -- assuming we've all correctly inferred the desired output, given no DDL nor sample data, nor the expected output from the given inputs.
If there is no requirement to include the averaged salary [as the average over all rows] in addition to the calculated difference, then the following [shown with an optional casting to a decimal result] uses a scalar subselect to get the value to subtract from each employee salary:
select emp.*
, dec( salary - ( select avg(salary)
from employee_table )
, 11, 0 ) as saldif
from employee_table as emp
Or to use the averaged salaries in both the difference and as a column by itself, then again the scalar subselect, but made available for lateral reference in the [explicitly joined-to] subquery; again, optional casting for decimal results:
select x.*
from table
( select avg(salary)
from employee_table
) as a ( avgsal )
cross join lateral
( select emp.*
, dec( salary - avgsal , 11 ) as saldif
, dec( avgsal , 11 ) as salavg
from employee_table as emp
) as x
other solution ;)
with avgsalary as (
select avg(salary) avgsal from employee_table
)
select emp.*, case when emp.salary is null then cast(null as decimal) else round(emp.salary - avgs.avgsal, 2) end as diffsal, avgs.avgsal
from employee_table as emp cross join avgsalary avgs
And even another variation:
with EmpAvg (avgSalary)
as ( SELECT avg( salary ) from employee_table )
select e.*, a.avgSalary,
(e.salary - a.avgSalary) as diffAvg
from employee_table e cross join EmpAvg a
There may be many forms of queries that can give equivalent results. (Although I left off casting the calculated values, so not exactly equal result values.)

How to Join Tables with UNION without displaying null when no matched UNION

how do i join two tables to show the department_id, department_name and last_name. It has to be with UNION.
I only found this solution (below) but that will show the department_name with the last_name as NULL and the last_name with the department_name as null. Is there any method to show everything without Null?
SELECT department_id, department_name, TO_CHAR(NULL)
FROM departments
WHERE department_id IN (10,20)
UNION ALL
SELECT department_id, TO_CHAR(NULL), last_name
FROM employees
WHERE department_id IN (10,20)
Below is the code which displays what i want to display with UNION:
SELECT d.department_id, d.department_name, l.last_name
FROM departments d JOIN employees l
ON d.department_id = l.department_id
WHERE d.department_id IN (10,20)
Thank you in advance :)
It doesn't make much sense, to do it this way, but so does the whole task.
select *
from(
select department_id, max(department_name) over (partition by deptno), last_name
from(
SELECT department_id, department_name, TO_CHAR(NULL) last_name
FROM DEPT
WHERE department_id IN (10,20)
UNION ALL
SELECT department_id, TO_CHAR(NULL), last_name
FROM emp
WHERE department_id IN (10,20)))
where last_name is not null
Just add a where clause with COALESCE to get NOT NULL in an outer query.
SELECT * FROM (<your union query>) WHERE COALESCE(column1, column2, column3) IS NOT NULL
You could use CTE for your inner query, i.e. your union query. And then use my suggestion to select NOT NULL rows.

replace WITH clause with VIEW in Oracle

I have a query like this.
with test1 as (
select emp.empno, emp.deptno, emp.name, emp.hiredate, dept.deptname
from emp, dept
where
emp.deptno = dept.deptno
and emp.deptno = 72
and emp.salary > 5000
)
select inner1.*
from (
select 'abc' as title,
1 emp_order,
name, hiredate, deptname
from test1
UNION ALL
select 'xyz' as title,
2 emp_order,
name, hiredate, deptname
from test1
) inner1
I am trying to remove the WITH clause completely and create a VIEW instead.The only problem I have is the dynamic value in the WITH clause.
I tried this:
CREATE VIEW testview as
select emp.empno, emp.deptno, emp.name, emp.hiredate, dept.deptname
from emp, dept
where
emp.deptno = dept.deptno
and emp.deptno = 72
and emp.salary > 5000
Updated query
select inner1.*
from (
select 'abc' as title,
1 emp_order,
name, hiredate, deptname
from testview
UNION ALL
select 'xyz' as title,
2 emp_order,
name, hiredate, deptname
from testview
) inner1
In this case how can I pass bind values for salary and deptno cols in the view?
If I understand correctly then you want to use parameters in your view.
To do this you can use either of the below approaches:
a.) If your parameters are more or less static and not changing frequently you can use a Table to store the value of this parameter. The values in this table can be updated by using a PL/SQL package or procedure. This table can then be refrenced in your view.
b.) If you require different parameter values for different sessions you can also Try using Application Context as given here
Hope it helps
Vishad
You do not need to remove the common table expression in order to use this query in a view. If you remove the predicates and apply them to a query against the view they can pushed inside the CTE anyway.
e.g.
CREATE VIEW testview as
select emp.empno, emp.deptno, emp.name, emp.hiredate, dept.deptname, emp.salary
from emp join dept ON emp.deptno = dept.deptno;
select inner1.*
from (
select 'abc' as title,
1 emp_order,
name, hiredate, deptname
from testview
where deptno = 72 and salary > 5000
UNION ALL
select 'xyz' as title,
2 emp_order,
name, hiredate, deptname
from testview
where deptno = 72 and salary > 5000
) inner1