Query regarding NVL in SQL - sql

Write a query to display department Id, department name, manager id of all departments. If manager id is not available then display 'NA' and give alias name as 'MANAGER_ID'. Sort the result based on department Id. The type of manager_id is number.
select
department_id, department_name, manager_id, NVL(manager_id, 'na') "BONUS_AMT"
from
departments
order by 1;
is my query but the error is ERROR at line 1:
ORA-01722: invalid number
NEED HELP!

You are using an NVL to get a string when a NUMBER is null. If you need a string result, you need to convert your number to string; for example:
SQL> select nvl(x, 'a') from (select 1 x from dual);
select nvl(x, 'a') from (select 1 x from dual)
*
ERROR at line 1:
ORA-01722: invalid
SQL> select nvl(to_char(x, '999'), 'a') from (select 1 x from dual);
NVL(TO_CHAR(X,'999'),'A')
--------------------------------------------------------------------------------
1

select
department_id, department_name, manager_id,
NVL(cast(manager_id as varchar2(10)), 'na') "BONUS_AMT"
from
departments
order by 1;

Related

SQL: Trying to find total of one column and who has the highest value in that column

Link of tables:
https://docs.google.com/document/d/1df4zUTI6e5Rw8mJxZKkOWLBuRYzjAOPkkf8zsC_2mRo/edit?usp=sharing
I'm trying to write a query that displays the total (sum) of all the salaries in the salary column and the name of the individual who has the highest salary.
I used:
select name as highest_paid,sum(salary) as total_salary
from uscis,employer
where uscis.alienno=employer.alienno and salary=(select max(salary) from employer) group by name
I did get the result of name of the highest salary paid but i did not get the sum of the salary columns. I instead got 280,000 which is the highest salary.
My apologies in advance if I worded this question poorly.
So you do not want just the one persons salary? You want to sum the whole salary column from the Employer table? Assuming a 1:1 relationship between USCIS table and Employer, try this.
SELECT name as highest_paid, sum(salary) OVER() as everybodys_total_salary
FROM uscis
INNER JOIN employer
on uscis.alienno=employer.alienno
ORDER BY salary DESC
FETCH FIRST ROW ONLY
Description of oracle sum as an analytic function found here https://oracle-base.com/articles/misc/sum-analytic-function
Want to get the sum(salary) and max(salary) together?
with total as (
select sum(salary) as sum_salary from employer
), max as (
select a.name, b.salary from uscis a, employer b where a.alienno = b.alienno and b.salary = (select max(salary) from employer)
)
select * from max, total;
look at the dbfiddle
Use SUM as an analytic function and the ORDER BY the salary in DESCending order and get only the first row:
SELECT name as highest_paid,
SUM(salary) OVER () as total_salary
FROM uscis
INNER JOIN employer
ON ( uscis.alienno=employer.alienno )
ORDER BY salary DESC
FETCH FIRST ROW WITH TIES;
-- or FETCH FIRST ROW ONLY if you only ever want one row.
So, for your test data:
CREATE TABLE uscis ( AlienNo, Name, Nationality, City ) AS
SELECT 'A1023', 'Jeff', 'India', 'Atlanta' FROM DUAL UNION ALL
SELECT 'A1024', 'David', 'China', 'NY' FROM DUAL UNION ALL
SELECT 'A1025', 'Mark', 'UK', 'Charlotte' FROM DUAL UNION ALL
SELECT 'A2050', 'Shown', 'Germany', 'Astoria' FROM DUAL;
CREATE TABLE employer ( AlienNo, SSN, Dept, Salary ) AS
SELECT 'A1023', '111-22-4567', 'EE', 280000 FROM DUAL UNION ALL
SELECT 'A1024', '333-32-8767', 'CS', 180000 FROM DUAL UNION ALL
SELECT 'A1025', '444-45-3454', 'CE', 140000 FROM DUAL UNION ALL
SELECT 'A2050', '234-34-2234', 'ME', 180.000 FROM DUAL;
This outputs:
HIGHEST_PAID | TOTAL_SALARY
:----------- | -----------:
Jeff | 600180
db<>fiddle here

How do i change the result of an ORACLE select query for specific type of data?

I have a table named EMPLOYEES with columns EMPLOYEE_ID, NAME and DEPARTMENT_ID. I have to list all employees, changing the output of the attribute NAME to "---" if the department id is 10.
How can I do that? Thank you!
select
employee_id,
case when department_id = 10 then '---' else name end as name,
department_id
from employees;
Another solution using union all
select
employee_id, name, department_id
from employees WHERE department_id != 10
UNION all
select
employee_id, '---' name, department_id
from employees WHERE department_id = 10

Oracle SQL - Get name from table based on group by

I have a table called employee and has columns as follows
emp_id number
emp_name varchar(30)
salary float
dept_id number
I want to get the output as any one name of employee within that department and employee count from each department. I tried the below, but didn't work well
SELECT emp_name, count(*) FROM emp
GROUP BY dept_id, emp_name;
Expected output:
emp_name, count(*)
abc, 4
def, 2
xyz, 10
Can anyone suggest?
You can try this if you want just a basic "random employee" shown for each department.
select emp_name, emp_count
from (
select emp_name, dept_id,
count(*) over (partition by dept_id) emp_count,
row_number() over (partition by dept_id
order by dbms_random.value ) rnum
from employee
)
where rnum = 1
/
This uses analytic function to calculate the counts, and then pick off 1 random row to display.

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

conditional order by clause in sql

I have a query that should order the result in asc or desc depending upon a column value.
e.g.
if employee of type manager exists THEN order by joining_date, bith_date ASC
else if employee is developer THEN order by joining_date, birth_date DESC.
I would like to achieve something like below, but can't achieve that.
ORDER BY CASE WHEN employee_type = 'm'
THEN joining_date, birth_date ASC;
WHEN employee_type = 'd'
THEN joining_date, birth_date DESC;
Well I got the answer after some research.
We can add multiple columns in where clause conditionally as follows :
ORDER BY DECODE(employee_type, 'm', joining_date, birth_date, salary) ASC,
DECODE(employee_type, 'd', joining_date, birth_date, salary) DESC
This will order the result on the basis of employee_type.
I suspect you want something like this:
ORDER BY
employee_type DESC -- first all the managers, then the developers
-- and in every one of these two groups
, joining_date -- first order by joining date
, CASE WHEN employee_type = 'm' -- and then either by
THEN birth_date -- birth date ascending for managers
ELSE NULL
END -- or
, birth_date DESC ; -- birth date descending for the rest (devs)
The question is a little bit poor specified.
order the result in asc or desc depending upon a column value.
A column takes many values (as there are multiple rows).
Now, order by clause use an expression and order rows upon it.
That expression should be morphotropic(;))
So, assuming stardard oracle's employee schema, managers are:
select *
from emp e
where exists (select emp_id from emp where e.id=emp.mgr_id)
An workaround query may be:
Select e.id, e.name, e.birth_date,
case
when (select count(*)
from emp e
where exists (select emp_id from emp where e.id=emp.mgr_id)
) --existence of manager
> 0 then birth_date - to_date('1-Jan-1000','dd-mon-yyyy')
else to_date('1-Jan-1000','dd-mon-yyyy') - birth_date
end as tricky_expression
from emp A
order by 4;
That exexpresion is the case; Using a constant(subquery that decides there are managers) it changes values from positive to negative, that is, change the order direction.
UPDATE: with the details in the comments:
select id, name, birth_date emp_type
from (
Select id, name, birth_date, emp_type,
case when cnt_mgr > 0 then birth_date - to_date('1-Jan-1000','dd-mon-yyyy')
else to_date('1-Jan-1000','dd-mon-yyyy') - birth_date
end as tricky_expression
from(
Select e.id, e.name, e.birth_date, emp_type,
count(case when emp_type='M' then 1 else 0 end) over() as mgr_count
from emp A
where your_conditions
)
order by tricky_expression
)
where rownum=1;
If there is a manager in the company this query returns the oldest manager, otherwise - the youngest developer.
select
id, name, birth_date, emp_type
from emp
where
id = (select
max(id) keep (dense_rank first order by
decode(emp_type, 'M', 1, 'D', 2),
joining_date,
decode(emp_type, 'M', 1, 'D', -1) * (birth_date - to_date('3000','yyyy')))
from emp)