Sql Server In statement with Null values - sql

I have the following:
select Firstname, LastName, Department
from Employee
where
Department is null
or Department in (
select Department
from Employee
group by Department
having COUNT(1) < 5)
This takes a /long/ time (over 10 minutes) and I'm assuming it's eventually time out.
I can change it to:
select Firstname, LastName, Department
from Employee
where
coalesce(Department,'') in (
select coalesce(Department,'')
from Employee
group by Department
having COUNT(1) < 5)
which, in this case, gives me what I need. It returns in 2 seconds.
If I just doing the first query with either part of the where clause returns quick. I also unioned them and it return quickly as well. Any insight on why it's freaking out when i combine them?

Related

SQL select groups where values are exclusively in a list

Given a table with two columns, department and employee, where every employee belongs to 1 department.
Given a list of employee ids, how do I select departments where all employees are in the list?
Department
Employee
finance
1
finance
2
marketing
3
marketing
4
IT
5
IT
6
given (2,3,4,5,6), returns ('marketing', 'IT')
(Note: DB flavor does not matter to me, you may use standard or DB-specific SQL)
Put your input id's into a table, join to the existing table, and check counts between input and the existing employees. If they match, output.
Change this to whatever RDBMS you're using
CREATE TEMP TABLE input_vals (id int);
insert into input_vals
values
(2),
(3),
(4),
(5),
(6);
with cte_joins AS (
select ot.department,
count(ot.employee) AS employee_count1,
sum(case when iv.id is not null then 1 else 0 end) AS employee_count2
from orig_table ot
left join input_vals iv
on ot.employee = iv.id
group by ot.department
)
select department
from cte_joins
where employee_count1 = employee_count2
You can use a couple of derived tables - one to get the count of employees in each department and one to get the count of employees in each department limited by your list of employees. Return the ones that match.
select
distinct full_list.department
from (
select
department,
count (*) as cnt
from
<your table>
group by department) full_list
inner join (
select
department,
count (*) as cnt
from
<your table>
group by department
where
employee in (2,3,4,5,6)
) limited_list
on full_list.department = limited_list.department
and full_list.cnt = limited_list.cnt
-- departments
select department from t where employee in (2,3,4,5,6);
-- departments containing extra persons
select department from t
where department in (select department from t where employee in (2,3,4,5,6))
and employee not in (2,3,4,5,6);
--- departments, employees from the list, with no extra
select department from t
where employee in (2,3,4,5,6) and department not in (
select department from t
where department in (
select department from t where employee in (2,3,4,5,6)
) and employee not in (2,3,4,5,6)
)
Assume you employee list is captured as a column in a table. Since you didn't provide any detail on the tool set you are using.
Something like this is a common pattern, depending on your dbms there's other more simple syntax.
select distinct
de.department
from department_employee de
where not exists
( select 1
from department_employee de2
where not exists
( select 1
from employee_list el
where de2.employee = el.employee
)
and de.department = de2.department
);

Not Sure How NOT EXISTS works

This is just homework currently, but I am having trouble writing a SELECT statement for this question:
Produce a result set showing department name, department description,
employee first name, and employee last name for ALL departments,
including those for whom no employee has been assigned. Sort by
department name.
I believe I have the SELECT, FROM, WHERE, and ORDER BY down, but the NOT EXISTS is where I am struggling.
Here is the table:
SELECT deptName, deptDesc, empFirstName, empLastName
FROM department, employee
WHERE department.deptID=employee.deptID
AND NOT EXISTS (
SELECT deptName, deptDesc
FROM
ORDER BY deptName ;
At this point I am just trying to include those for whom no employee has been assigned.
I believe you are looking for a LEFT JOIN instead: https://www.w3schools.com/sql/sql_join_left.asp
You want to include everything from department, and also anything that matches from employee, but not just the intersection of the two.
NOT EXISTS will just return a boolean true or false if that inner query produces results with at least one row. I don't think that's what you want.
SELECT deptName, deptDesc, empFirstName, empLastName
FROM department
LEFT JOIN employee on department.deptID=employee.deptID
ORDER BY deptName;

SQL query where Employee works in more than one Department

I have employee table with emp id (emp_id) and department (dep_id) fields. An employee could be working in more than one Department. I want to write a sql query to display unique emp_ids who work in more than one department.
Pl help me to write sql query.
Thx
Answered here: SQL query for finding records where count > 1
You need to use count, group by and having like this.
select emp_id, count(dep_id)
from employee_department
group by emp_id
having count(dep_id)>1
Query
SELECT COUNT(*)
FROM
(
SELECT id_employee, COUNT(*) AS CNT
FROM Department_Employee
GROUP BY id_employee
) AS T
WHERE CNT > 1

Find the name of the employees, whose names are same but salary are different

I have an 'employee' table with
create table employee
(
e_number int,
e_name varchar(20),
salary money,
hire_date date
)
Now I want to display only the name of the employees who have the same name but different salary.
I tried select e_name,count(*) from employee group by e_name having count(*)>1;
but cannot combine it with "the same salary" section. Any help?
This assumes that you want the names of both of the people listed:
SELECT e1.e_name
FROM employee e1, employee e2
WHERE e1.e_name = e2.e_name
AND e1.salary <> e2.salary;
If you only want each name listed once, you would use a SELECT DISTINCT instead of the SELECT.
If your goal is to express this in the having clause:
Select name
from employee
group by name
having
count(*) > 1
and min(salary) != max(salary)
order by name
SELECT employee1.e_name, employee1.Salary, Employee2.Salary
FROM Employee employee1
JOIN Employee employee2
on employee1.name = employee2.name
AND Employee1.Salary <> Employee2.Salary
AND Employee1.E_Number <> employee2.E_Number
Basically get every employee, join it to every other employee via name, where the employee number is different (so don't join to yourself) and salary is different.
You probably don't need to check that employee number is different because 1 employee can only have 1 salary in your table design
Use a join, but importantly use a greater-than comparison, rather than a not-equals, to avoid duplicates:
SELECT e1.e_name as name1, e2.e_name as name2
FROM employee e1
JOIN employee e2 ON e1.e_name = e2.e_name
AND e1.salary > e2.salary;
Note also that the ON condition contains the salary comparison. It is a common misconception that the join on condition may only contain key-related comparisons. Doing this can have significant performance benefits, especially when further joins are made, because the ON condition is executed as the rows are joined - which discards non-matches immediately, whereas WHERE conditions are executed as a filter on the entire result set of the joins.
You just want the count of distinct salaries, not the count of all records.
select e_name,count(distinct salary)
from employee
group by e_name
having count(distinct salary)>1
(Drop the count in the select if unneeded - included since it was in your example)
First filter salary not double (not in), then grouping by e_name having count > 1
SELECT A.e_name
FROM employee A
WHERE A.salary NOT IN (SELECT salary FROM employee WHERE id != A.id)
GROUP BY A.e_name
HAVING COUNT(A.e_name) > 1

ID value is lost after performing MIN then GROUP BY. Why?

To simplify, if I had a table called 'employee':
id INT NOT NULL PRIMARY KEY,
salary FLOAT,
department VARCHAR(255)
I want to perform and query where I retrieve the minimum salary in each department.
So my query is something like this:
SELECT employee.ID, MIN(employee.salary), employee.department
FROM employee
GROUP BY employee.department
But regardless of which records are found. The ID values in the result set are renamed to 1,2,3.... up to however many records (departments) exist in the result set.
How can I maintain the actual ID's of the employees after performing the AGGREGATE function and GROUP BY?
You can't. Think about it, If a Department has 20 employees, and for that department, there are three employees that have the same minimum salary, which EmployeeId do you want the the query output to display? if it was guaranteed that there was only one employee in each dept with that lowest salary, then it can be done by selecting the specific employee records where the salary is the minimum value for each Department:
Select EmployeeID
From Employee e
Where Salary =
(Select Min(Salary) From EMployee
Where DepartmentId = e.DepartmentId)
but this will return multiple records per department when more than one employee has that min salary level.
I would guess you're using MySQL or SQLite, because your query is ambiguous and isn't allowed by standard SQL or most brands of RDBMS. MySQL and SQLite are more permissive, so it's your responsibility to resolve the ambiguity.
Here's my usual fix:
SELECT e1.ID, e1.salary, e1.department
FROM employee e1
LEFT OUTER JOIN employee e2 ON (e1.department = e2.department
AND e1.salary > e2.salary)
WHERE e2.department IS NULL;
Here's another solution that gives the same result:
SELECT e1.ID, e1.salary, e1.department
FROM employee e1
JOIN (SELECT e2.department, MIN(e2.salary) AS min_salary
FROM employee e2 GROUP BY e2.department) d
ON (e1.salary = d.min_salary);
Both of these give multiple rows per department if there are multiple employees in the department with identical minimal salaries. You need to decide how to resolve that case, because it's not clear from your problem description.
Your script is invalid:
SELECT employee.ID, MIN(employee.salary), employee.department
FROM employee
GROUP BY employee.department
Instead, look at this:
SELECT MIN(employee.salary), employee.department
FROM employee
GROUP BY employee.department
If you need the employee id as well, then you need to use a subquery.
This will do the trick:
SELECT employee.department, MIN(employee.salary), employee.ID
FROM employee
GROUP BY 1
In modern SQL Server releases (and other reasonably powerful and modern SQL engines), SQL "Window functions" are probably the best alternatives (to be preferred over subqueries and self-joins) to do what you desire:
SELECT ID, salary, department
FROM employee
WHERE 1 = ROW_NUMBER() OVER(PARTITION BY department ORDER BY salary ASC)
This works when, if multiple employees have the same (department-minimal) salary, you want just a "random-ish" one of them (you can add criteria to the ORDER BY if you want one picked by some specific criteria); look into RANK, instead of ROW_NUMBER, if you want all.