Display count of subquery in outer query - sql

I want to see the number of departments from Query2 as a new column in Query1. How can I do that?
Query 1:
SELECT
location_id,
street_address,
postal_code,
city,
state_province,
country_id
FROM
locations;
Query 2:
SELECT
location_id,
COUNT(department_id)
FROM
departments
group by location_id;

One method is a correlated subquery:
select l.*,
(select count(*) from departments d where d.location_id = l.location_id
) as num_departments
from locations l;

Related

List the employees (name, base_salary) that earn an average value (with accuracy up to 30%) from the average earnings in teams of >2 employees

Here's what I got so far:
SELECT surname, base_salary from emp p LEFT JOIN (select id_team, avg(base_salary) as s, count(*) as c from emp group by id_team) as o ON(p.id_team = o.id_team)
where p.base_salary between o.s*0.7 and o.s*1.3 and o.c >=2
On Oracle LIVE SQL I'm getting ORA-00905: missing keyword error.
Here's what the table looks like.
The problem with your is the use of the as keyword to alias the subquery. Oracle does not support that. Your query should just work of you remove it:
select ...
from emp p
left join (
select id_team, avg(base_salary) as s, count(*) as c from emp group by id_team
) as o on p.id_team = o.id_team)
--^-- here
where ...
On the other hand, I think that you query could be phrased more efficiently using window functions:
select surname, base_salary
from (
select
surname,
base_salary,
avg(base_salary) over(partition by id_team) avg_base_salary,
count(*) over(partition by id_team) no_emps
from emp
) e
where no_emps > 2 and base_salary between avg_base_salary * 0.7 and avg_base_salary * 1.3

UNION vs. JOIN, b and c not defined? (sqlite3)

select city, prov, a, b, c from (
select count(city) as a, city, prov from locations left outer join rides on src=lcode
group by city
union
select count(city) as b, city, prov from locations left outer join rides on dst=lcode
group by city
union
select count(city) as c, city, prov from locations l left outer join enroute e on l.lcode=e.lcode
group by city
) group by city;
So I got an error saying "no such column: b" (and also column c). Is it possible to solve this without changing a lot of this query?
Your subquery is returning three columns, a, city, and prov. That is, for a union/union all query, the column names come from the first subquery. There is no b or c.
Presumably, you want some sort of JOIN, not UNION ALL. However, your question does not explain what you are trying to do. And it doesn't have sample data or desired results. So, it is really hard to say what you actually want.
It strikes me that you actually want:
select city, prov, sum(a), sum(b), sum(c)
from ((select city, prov, count(city) as a, null as b, null as c
from locations left outer join
rides on src = lcode
group by city
) union all
(select city, prov, null, count(city), 0
from locations left outer join
rides
on dst = lcode
group by city
) union all
(select city, prov, null, null, count(city)
from locations l left outer join
enroute e
on l.lcode = e.lcode
group by city
)
) abc
group by city;
The expressions that are not encapsulated within an aggregate function and must be included in the GROUP BY clause. For Unions, all of the select statements in those unions must have the same column names.
Below would do the required tasks.
select * from (
select count(city) as cityCount, prov from locations left outer join rides on src=lcode
group by prov
union
select count(city) as cityCount, prov from locations left outer join rides on dst=lcode
group by prov
union
select count(city) as cityCount,prov from locations l left outer join enroute e on l.lcode=e.lcode
group by prov
);
Doesn't require an additional groupby for Outside subquery. It will already be grouped by provinces of locations.

Subquery using 3 Tables SQL

I'm trying to display the last name of the lowest paid employees from each city. The city column falls under a table titled LOCATIONS while employee information(salary, last name) falls under EMPLOYEES. Both of these tables are related share no common table, so I have to rely on a third table, DEPARTMENTS to connect the two as DEPARTMENTS contains a department_id that it shares with EMPLOYEES as well as a LOCATION_ID that it shares with LOCATIONS. This is what I have so far, but I'm having trouble with this as I've mostly worked with only two tables in the past.
SELECT LAST_NAME
FROM EMPLOYEES
WHERE (DEPARTMENT_ID) IN
(SELECT DEPARTMENT_ID
FROM DEPARTMENTS
WHERE LOCATION_ID IN
(SELECT LOCATION_ID
FROM LOCATIONS
GROUP BY CITY
HAVING MIN(SALARY)));
This seems to be an assignment in an intro course in SQL. So let's assume you can't use analytic functions, match_recognize clause, etc. Just joins and aggregates.
In the subquery in the WHERE clause below, we compute the min salary for each city. We need to join all three tables for this. Then in the overall query we join the three tables again, and we use the subquery for an IN condition (a semi-join). The overall query looks like this:
select e.last_name
from employees e join departments d
on e.department_id = d.department_id
join locations l
on d.location_id = l.location_id
where ( e.salary, l.city ) in
(
select min(salary), city
from employees e join departments d
on e.department_id = d.department_id
join locations l
on d.location_id = l.location_id
group by city
)
;
You should separate out the concept of table joins from WHERE clauses.
Use WHERE for filtering data, use JOIN for connecting data together.
I think this is what you are wanting. By the way, lose the ALL CAPS if you can.
SELECT
LAST_NAME
FROM
EMPLOYEES
INNER JOIN (
SELECT
DEPARTMENTS.DEPARTMENT_ID,
CITY,
MIN(SALARY) AS LOWEST_SALARY
FROM
EMPLOYEES
INNER JOIN DEPARTMENTS ON EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID
INNER JOIN LOCATIONS ON DEPARTMENTS.LOCATION_ID = LOCATIONS.LOCATION_ID
GROUP BY
DEPARTMENTS.DEPARTMENT_ID,
LOCATIONS.CITY
) AS MINIMUM_SALARIES
ON EMPLOYEES.DEPARTMENT_ID = MINIMUM_SALARIES.DEPARTMENT_ID
AND EMPLOYEES.SALARY = MINIMUM_SALARIES.LOWEST_SALARY
First of all join the tables, so you see city and employee in one row. If we group by city we get the minimum salary per city.
with city_employees as
(
select l.city, e.*
from locations l
join departments d using (location_id)
join employees e using (department_id)
)
select last_name
from city_employees
where (city, salary) in
(
select city, min(salary)
from city_employees
group by l.city
);
It is easier to achieve the same, however, with window functions (min over or rank over here).
select last_name
from
(
select
e.last_name,
e.salary,
min(e.salary) over (partition by l.city) as min_salary
from locations l
join departments d using (location_id)
join employees e using (department_id)
)
where salary = min_salary;

SELECT clause with MINUS INTERSECT and UNION

In my database, i created a table name DEPTLOC
//DEPTLOC
DNAME CITY
----------------------------
RESEARCH BOSTON
IT PARIS
SCIENCE LONDON
RESEARCH LONDON
SALES NEWYORK
RESEARCH PARIS
RESEARCH NEWYORK
MARKETING NEWYORK
So i used the following query
SELECT CITY FROM DEPTLOC
INTERSECT
(
SELECT CITY FROM DEPTLOC WHERE DNAME='SALES'
UNION
SELECT CITY FROM DEPTLOC WHERE DNAME='RESEARCH'
);
But my output is all the CITY will be displayed. My question is want find which of the DNAME='SALES' OR DNAME='RESEARCH' has its location in all cities.
So from the table above, all distinct city are
CITY
-------
BOSTON
PARIS
LONDON
NEWYORK
Since 'RESEARCH' have all the location but 'SALES' Only have some, my output should be display like this
DNAME
---------
RESEARCH
What should i change for my query in order to get the correct output
Here's a shorter (than my previous answer) query that checks only between DNAMES SALES and RESEARCH to see which DNAME has all CITIES. Set operators UNION ALL and MINUS are used.
SELECT DNAME
FROM
(
SELECT 'SALES' DNAME, COUNT(*) MISSING_CITIES
FROM
(
SELECT DISTINCT CITY FROM DEPTLOC
MINUS
SELECT CITY FROM DEPTLOC WHERE DNAME = 'SALES'
)
UNION ALL
SELECT 'RESEARCH', COUNT(*) FROM
(
SELECT DISTINCT CITY FROM DEPTLOC
MINUS
SELECT CITY FROM DEPTLOC WHERE DNAME = 'RESEARCH'
)
)
WHERE MISSING_CITIES = 0;
One way of doing this is to count the distinct locations, and join it on the departments query:
SELECT dname
FROM (SELECT dname, COUNT(*) AS dept_city_count
FROM deptloc
GROUP BY dname) d
JOIN (SELECT COUNT (DISTINCT city) AS city_count
FROM deptloc) ON city_count = dept_cirt_count
Here is a query using joins. First all Cities are selected. Then, a CROSS JOIN is made between all Departments and all Cities. Then, a LEFT JOIN is made to select Departments that do not have all Cities. Then, another LEFT JOIN is made to select Departments that do have all Cities.
SELECT DISTINCT
DL.DNAME
FROM
DEPTLOC DL
LEFT JOIN
(
SELECT
ALL_COMBOS.DNAME dname
FROM
(
SELECT DISTINCT
D1.DNAME DNAME,
D2.CITY CITY
FROM
DEPTLOC D1
CROSS JOIN
(
SELECT DISTINCT
CITY
FROM
DEPTLOC
)
D2 --All distinct Cities
)
ALL_COMBOS --All Departments with all Locations
LEFT JOIN DEPTLOC D2
ON
ALL_COMBOS.DNAME = D2.DNAME
AND ALL_COMBOS.CITY = D2.CITY
WHERE
D2.DNAME IS NULL
)
NOT_ALL_LOCATIONS --Departments that do not have all Locations
ON DL.DNAME = NOT_ALL_LOCATIONS.DNAME
WHERE
NOT_ALL_LOCATIONS.DNAME IS NULL; --Departments that have all Locations
As it can be observed, this method does not rely on the count of the Cities, but on the actual values of the Cities themselves.
Assuming that the key in deptloc is (dname, city) then the following would work:
select dname
from deptloc
group by dname
having count(*) = (select count(distinct city) from deptloc);
The query works by creating one group for every department. The count(*) would be the nr of cities that the department is located in. Each such count is compared with the nr of unique Cities cited in the same table.
Edited: Saw your comment now, that this was the solution you did not want :)
Here is another version of "relational division"
select departments.dname
from (select distinct dname from deptloc) departments
where not exists(
select 'x'
from (select distinct city from deptloc) cities
where not exists(
select 'x'
from deptloc x
where x.city = cities.city
and x.dname = departments.dname));

How to add subquery as a column in SQL

How do I add a subquery as a column in my SQL script?
e.g.
Select emp_no, name,gender ,
(select department_name from departments where employees.emp_no = departments.emp_no)
from employees
PS: I'm using oracle 8
Going by the semantics, what I understand is that you want an employee's department name to be shown alongside his/her other information. I would suggest you do a join instead:
Select emp_no, name, gender, department_name
from employees emp, departments dept
where emp.emp_no = dept.emp_no;
That looks reasonably sound, I would suggest some (possible typos) cleaning up: add a comma after "gender" and declare the table names, also set the subquery alias
Select employees.emp_no, employees.name, employees.gender,
(select departments.department_name from departments where employees.emp_no = departments.emp_no) as dept_name
from employees
Alternatively, a nice join would would work too, if the other data is feasible:
Select employees.emp_no, employees.name, employees.gender, departments.department_name
from employees
inner join departments on employees.emp_no = departments.emp_no
left join is the best-practice, and should be faster in performance:
Select e.emp_no, e.name, e.gender , d.department_name
from employees e left join departments d on e.emp_no = d.emp_no;
You seem to be missing comma after gender.
Select emp_no, name,gender ,
(select department_name from departments where employees.emp_no = departments.emp_no) as dept_name from employees
The below is what you need. Just added a comma after gender. This subquery would need to return only one row for each result as well or else an error will be seen.
Select emp_no, name,gender,
(select department_name from departments where employees.emp_no = departments.emp_no)
from employees
This query is your answer but it will work only if there is one column mentioned in that if we use more than one column than it will retrun an error .
"Select employee_id,first_name,
(select department_name,manager_id from departments where employees.department_id = departments.department_id) as new_column
from employees;"
can you try this:
SELECT em.emp_no, em."name",em.gender ,
(SELECTdistinctdp.department_name
FROM departments dp
WHERE em.emp_no = dp.emp_no) my_sub
FROM employees em