How do we remove the column that is created by LEAD function - sql

When I run run below query
SELECT
*
FROM
(
SELECT
LEAD(hire_date)
OVER(PARTITION BY department_id
ORDER BY
hire_date
) AS recent_joinee,
a.*
FROM
employees a
)
WHERE
recent_joinee IS NULL;
I am getting below results
But i don't need the first column "Recent_joinee" as this is created by the lead function , Rest of "n" number of columns i need.
For this scenario what i need to do ?

You should specify the columns you need.
You can either get all fields (*) or specify the fields you want.
More info

It can be cumbersome to list all the columns. Oracle doesn't have a simple way to remove columns. For your example, you can incur the overhead of an extra join (which isn't too much if the key is the primary key):
SELECT e.*
FROM employees e JOIN
(SELECT LEAD(hire_date) OVER (PARTITION BY department_id
ORDER BY hire_date
) AS recent_joinee,
e2.*
FROM employees e2
) e2
USING (employee_id)
WHERE e2.recent_joinee IS NULL;
Alternatively, you could use a correlated subquery:
select e.*
from employee e
where not exists (select 1
from employee e2
where e2.department_id = e.department_id and
e2.hire_date > e.hire_date
);

Related

subquery and join not giving the same result

1
select *
from employees
where salary > (select max(salary) from employees where department_id=50)
2
select *
from employees e left join
employees d
on e.DEPARTMENT_ID =d.DEPARTMENT_ID
where d.salary > (select max(salary) from employees where department_id=50)
why the second query is giving multiple record
i want achieve the same result as of 1st query using join.....
Thanks in Advance......
Rocky, the first select is correct. Why do you want to do any join? Without further information the objective of the second select is not clear (nonsense).
I can't see the point about joining against the same table by DEPARTMENT_ID. Anyway, the problem about duplicates is because you are joining the same two tables by a key is not pk, basically you are multiplyng each employee for all the employees of the same department. This version eliminate duplicates but still has no improvement from the first one.
select *
from employees e left join
employees d
on e.employee_ID = d.employee_ID
where d.salary > (select max(salary) from employees where department_id=50)
You are probably looking for an anti join. This is a pattern mainly used in a young DBMS where IN and EXISTS clauses are slow compared to joins, because the developers focused on joins only.
You are looking for all employees whose salaries are greater than all salaries in department 50. With other words: WHERE NOT EXISTS a salary greater or equal in department 50.
Your query can hence be written as:
select *
from employees e
where not exists
(
select null
from employees e50
where e50.department_id = 50
and e50.salary >= e.salary
);
As an anti join (an outer join where you dismiss all matches):
select *
from employees e
left join employees e50 on e50.department_id = 50 and e50.salary >= e.salary
where e50.salary is null;

How to `insert into` with select using `group by` and having a unique constraint key in Oracle?

I am trying to insert into a table, 5 values from other 2 tables, but when I
try to insert them, Oracle sends me a message that I am violating the unique key restriction, what I do not understand why I have this problem if I am using the distinct clause in the select.
Thanks
My query:
insert into grados_salariales (Department_id,
Department_name,Sumatoria,Sal_minimo,Sal_maximo)
Select distinct departments.department_id,
departments.department_name,
sum(employees.salary),min(employees.salary),
max(employees.salary)
from employees,departments
group by salary,department_name,
departments.department_id;
This is the table that already exist and the unique key statement
create table
grados_salariales(
Department_id number,
Department_name varchar(50),
Sumatoria number,
Sal_minimo number,
Sal_maximo number);
Alter table grados_salariales
add constraint Department_id_pk
primary key ( Department_id);
I would expect inserting the department_id without problems.
This is your query:
select distinct d.department_id, d.department_name,
sum(e.salary), min(e.salary),
max(e.salary)
from employees e join
departments d
on e.department_id = d.department_id
group by e.salary, d.epartment_name, d.department_id;
The problem is the salary in the group by. If you want one row per department, then you can do:
select d.department_id, d.department_name,
sum(e.salary), min(e.salary),
max(e.salary)
from employees e join
departments d
on e.department_id = d.department_id
group by d.department_name, d.department_id;
Notes:
Never use commas in the FROM clause.
Always use proper, explicit, standard JOIN syntax.
Use table aliases so your queries are easier to write and read.
SELECT DISTINCT is almost never appropriate with GROUP BY.

For each employee with dependent(s), list their last name and the name of their oldest dependent. Display the results in sequence by dependent’s name

SELECT E.Lname,D.Dependent_name
FROM EMPLOYEE as E , DEPENDENT as D
WHERE
E.ssn=D.Essn
AND
D.Bdate = (SELECT MIN(Bdate) FROM DEPENDENT
WHERE Bdate IS NOT NULL)
This does work but it just show for 1 employee
Link to DB:
https://www.db-fiddle.com/f/xhEj2sAgdTMABBkCtJvmoC/0#&togetherjs=z3CKywAccH
As it is, your subquery returns the minimum bdate over the whole table, resulting in only the empolyee that has the oldest dependant being filtered in. As commented by #PM77-1, you would need to correlate your subquery with the employee that you are currently processing.
SELECT E.Lname,D.Dependent_name
FROM
EMPLOYEE as E
INNER JOIN DEPENDENT as D ON E.ssn=D.Essn
WHERE D.Bdate = (
SELECT MIN(Bdate)
FROM DEPENDENT
WHERE Essn = E.ssn
)
ORDER BY 2, 1;
Another, probably more efficient option, is to use Postgres window function ROW_NUMBER() to assign a row number to each record in the group made of all dependents of a given employee, ordered by date of birth. An outer query can then filter in the first record in each group :
SELECT x.lname, x.dependent_name
FROM (
SELECT
e.lname,
d.dependent_name,
ROW_NUMBER() OVER(PARTITION BY e.ssn ORDER BY d.bdate) rn
FROM employee e
INNER JOIN dependent d ON e.ssn = d.essn
) x WHERE x.rn = 1
ORDER BY 2, 1;
Demo on DB Fiddle
select a.lname, a.dependent_name
from
(
select e.lname, d.dependent_name, d.bdate, e.ssn
from employee e, dependent d
where e.ssn=d.essn
order by d.dependent_name, e.lname
)a
where a.bdate= (select min(bdate) from dependent
where dependent.essn= a.ssn)
order by a.lname;
This displays the names of three employees > Smith, Wallace, Wong with their corresponding eldest dependents > Elizabeth, Abner, Joy.
Hope this solves the problem.

How do I use a value from the superquery inside a subquery?

I need to create a query that shows the last name of an employee, the employee id, the last name of the manager of that employee and the id of that manager.
The last name, id, and the manager id of that employee is easy to do because it is already in one row, which means that the following is sufficient:
SELECT last_name, employee_id, manager_id FROM employees WHERE manager_id IS NOT NULL;
But to get the last_name of the manager, you have to search the same table by the manager id you got from the employee. The solution I found is:
SELECT last_name,
employee_id,
(SELECT last_name FROM employees WHERE employee_id = manager_id),
manager_id
FROM employees
WHERE manager_id IS NOT NULL;
However, it seems that 'manager_id' doesn't work in the subquery (although I expected that) and the output is NULL (for the manager id, all the other columns do have values).
So my question is, how can I use the manager_id in the subquery?
Side note: The manager_id can be different for each employee, so using a constant value doesn't work.
What you need is a correlated subquery. I strongly, strongly recommend that you use table aliases and qualified column names in all your queries. However, these are particularly important with correlated subqueries.
You should write this query as:
SELECT e.last_name, e.employee_id,
(SELECT m.last_name
FROM employees m
WHERE m.employee_id = e.manager_id
),
e.manager_id
FROM employees e
WHERE e.manager_id IS NOT NULL;
The alias e is an abbreviation for the table reference to employees in the outer query. The alias m is an abbreviation for the table reference in the subquery.
Notice that all column references use the table alias. This makes the query unambiguous, can prevent unexpected errors, and makes the query much easier for you and others to understand.
You could use a self inner join ( a join with the same table)
SELECT
a.last_name
, a.employee_id
, b.last_name
, a.manager_id
FROM employees a
INNER JOIN employees b ON b.employee_id = a.manager_id;
The inner join work only if a.manager_id is not null so you can avoid this where condition
When you want to refer to a table in the outer query, you need to either use the full table name like table.field or, as in your case, if the outer query table is same as the subquery table, you need to assign an alias to the outer query table and use it in the subquery like this:
SELECT
last_name, employee_id,
(SELECT last_name FROM employees WHERE employee_id = emp_outer.manager_id),
manager_id
FROM employees emp_outer
WHERE manager_id IS NOT NULL;

Limit the data set of a single table within a multi-table sql select statement

I'm working in an Oracle environment.
In a 1:M table relationship I want to write a query that will bring me each row from the "1" table and only 1 matching row from the "many" table.
To give a made up example... ( * = Primary Key/Foreign Key )
EMPLOYEE
*emp_id
name
department
PHONE_NUMBER
*emp_id
num
There are many phone numbers for one employee.
Let's say I wanted to return all employees and only one of their phone numbers. (Please forgive the far-fetched example. I'm trying to simulate a workplace scenario)
I tried to run:
SELECT emp.*, phone.num
FROM EMPLOYEE emp
JOIN PHONE_NUMBER phone
ON emp.emp_id = phone.emp_id
WHERE phone.ROWNUM <= 1;
It turns out (and it makes sense to me now) that ROWNUM only exists within the context of the results returned from the entire query. There is not a "ROWNUM" for each table's data set.
I also tried:
SELECT emp.*, phone.num
FROM EMPLOYEE emp
JOIN PHONE_NUMBER phone
ON emp.emp_id = phone.emp_id
WHERE phone.num = (SELECT MAX(num)
FROM PHONE_NUMBER);
That one just returned me one row total. I wanted the inner SELECT to run once for each row in EMPLOYEE.
I'm not sure how else to think about this. I basically want my result set to be the number of rows in the EMPLOYEE table and for each row the first matching row in the PHONE_NUMBER table.
Obviously there are all sorts of ways to do this with procedures and scripts and such but I feel like there is a single-query solution in there somewhere...
Any ideas?
I'd use a rank (or dense_rank or row_number depending on how you want to handle ties)
SELECT *
FROM (SELECT emp.*,
phone.num,
rank() over (partition by emp.emp_id
order by phone.num) rnk
FROM EMPLOYEE emp
JOIN PHONE_NUMBER phone
ON emp.emp_id = phone.emp_id)
WHERE rnk = 1
will rank the rows in phone for each emp_id by num and return the top row. If there could be two rows for the same emp_id with the same num, rank would assign both a rnk of 1 so you'd get duplicate rows. You could add additional conditions to the order by to break the tie. Or you could use row_number rather than rank to arbitrarily break the tie.
All above answers will work beautifully with the scenario you described.
But if you have some employees which are missing in phone tables, then you need to do a left outer join like below. (I faced similar scenario where I needed isolated parents also)
EMP
---------
emp_id Name
---------
1 AA
2 BB
3 CC
PHONE
----------
emp_id no
1 7555
1 7777
2 5555
select emp.emp_id,ph.no from emp left outer join
(
select emp_id,no,
ROW_NUMBER() OVER (PARTITION BY emp_id ORDER BY emp_id) as rnum
FROM phone) ph
on emp.emp_id = ph.emp_id
where ph.rnum = 1 or ph.rnum is null
Result
EMP_ID NO
1 7555
2 5555
3 (null)
If you want only one phone number, then use row_number():
SELECT e.*, p.num
FROM EMPLOYEE emp JOIN
(SELECT p.*,
ROW_NUMBER() OVER (PARTITION BY emp_id ORDER BY emp_id) as seqnum
FROM PHONE_NUMBER p
) p
ON e.emp_id = p.emp_id and seqnum = 1;
Alternatively, you can use aggregation, to get the minimum or maximum value.
This is my solution. Simple but maybe wont scale well for lot of columns.
Sql Fiddle Demo
select e.emp_id, e.name, e.dep, min(p.phone_num)
from
EMPLOYEE e inner join
PHONE_NUMBER p on e.emp_id = p.emp_id
group by e.emp_id, e.name, e.dep
order by e.emp_id;
And this fix the query you try
Sql Fiddle 2
SELECT emp.*, phone.num
FROM EMPLOYEE emp
JOIN PHONE_NUMBER phone
ON emp.emp_id = phone.emp_id
WHERE phone.num = (SELECT MAX(num)
FROM PHONE_NUMBER p
WHERE p.emp_id = emp.emp_id );