is this a subquery or just an inner join - sql

I am studying the subquery concept, below is one query which is extracted from wikipedia https://en.wikipedia.org/wiki/Correlated_subquery
SELECT employees.employee_number, employees.name
FROM employees INNER JOIN
(SELECT department, AVG(salary) AS department_average
FROM employees
GROUP BY department) AS temp ON employees.department = temp.department
WHERE employees.salary > temp.department_average;
the sql is a rewritten version of an correlated subquery as below
SELECT
employee_number,
name,
(SELECT AVG(salary)
FROM employees
WHERE department = emp.department) AS department_average
FROM employees AS emp;
And now my question :
Is the sql from the rewritten version a subquery? I am so confused on it
INNER JOIN
(SELECT department, AVG(salary) AS department_average
FROM employees
GROUP BY department) AS temp ON employees.department = temp.department
WHERE employees.salary > temp.department_average;

Welcome to Stackoverflow. This is certainly confusing, so I'd make it a little bit simpler by using two different tables and no table aliases.
I'd say if it's in the FROM clause, it's called a join:
SELECT employee_id, department_name
FROM employees JOIN departments USING (department_id);
If it's in the WHERE clause, it's called a subquery:
SELECT employee_id
FROM employees
WHERE employee_id = (
SELECT manager_id
FROM departments
WHERE employees.employee_id = departments.manager_id);
If it's in the SELECT clause, it's called a scalar subquery (thanks, #Matthew McPeak):
SELECT employee_id,
(SELECT department_name
FROM departments
WHERE departments.department_id = employees.department_id)
FROM employees;

Not exactly. The equivalent would be a left join. The correlated version keeps all rows in the employees table, even when there is no match. The inner join requires that there be a match.
In general, the execution plans are not going to be exactly the same, because the SQL engine does not know before-hand if all rows match.
With the additional filtering condition, the two versions are equivalent. Note that the filter for the correlated version requires a subquery or CTE because the where clause does not recognize column aliases.

Related

How to use count and order by with inner join in mysql?

employee table
department table
How to display department name and number of employees working for that department.
My SQL code:
SELECT department.dname , employee.count(*)
FROM employee
INNER JOIN department
ON dno=dnumber
ORDER BY department.dname;
The correct syntax is:
select d.dname, count(*)
from employee e inner join
department d
on d.dno = e.dnumber
group by d.dname
order by d.dname;
Notes:
You are describing an aggregation query with one row per department. That suggests GROUP BY.
Table aliases make the query easier to write and to read.
Qualify all column names. I have to guess what table dno comes from. There should be no reason to guess.

How can we rewrite a query with a subquery in SELECT clause?

How would you rewrite the following query into one without subquery as much as possible?
Select dept name,
(Select Count(*)
From instructor
Where department.dept name = instructor.dept name
) As num_instructors
From department;
I came up with the following. Is it a good equivalence to the above?
Select dept name, count(*)
From department, instructor
Where department.dept name = instructor.dept name
Group By department.dept_name;
Thanks.
The proper way to write the query uses explicit JOIN syntax:
select d.dept_name, count(i.dept_name)
from department d left join
instructor i
on d.dept_name = i.dept_name
group by d.dept_name;
If you only care about departments that have at least one instructor, then no join is necessary at all:
select i.dept_name, count(*)
from instructor i
group by i.dept_name;
Your attempt is really close, just a couple things..
You should use explicit joins (ie. JOIN, LEFT JOIN etc.) instead of implicit joins (commas in the FROM clause). Implicit joins are 25+ years depreciated.
Also, in this case you will want a LEFT JOIN or no departments will be displayed that don't have instructors. LEFT JOIN will retain departments without instructors and give you a 0 count (like the first query), where a JOIN would not display those at all.
SELECT d.dept_name, COUNT(i.dept_name) as num_instructors
FROM department d
LEFT JOIN instructors i on d.dept_name = i.dept_name
GROUP BY d.dept_name

Converting SQL query from using join to correlated nested query?

I am new to SQL and working on an assignment using Oracle SQL Developer. The assignment asks to solve the problems using correlated nested queries, but I am having a lot of trouble doing so. I have been able to solve the problems using join statements. I understand a correlated nested query to be where the subquery references the same relation used in the outer query. Is this correct line of thinking and how does this differ from using join operations? Also, in my use of SQL, I've noticed that to SELECT an attribute, it must be a column of the outer query (opposed to inner query). Does this mean all aggregate functions must be in the outer query if using correlated nested queries? Any advice would be appreciated.
For example, one problem asks to create a view with employee names, salary, department name, department manager name, department manager salary, and average department salary. My solution using joins:
CREATE VIEW DEPT_EMP_MGR_SALARIES
AS
SELECT
E.Fname AS EMP_FNAME,
E.Minit AS EMP_MINIT,
E.Lname AS EMP_LNAME,
E.Salary AS EMP_SALARY,
Dname,
DMgr_Fname,
DMgr_Minit,
DMgr_Lname,
DMgr_Salary,
DEPT_AVG_SALARY
FROM
EMPLOYEE E
JOIN (
SELECT
D.Dname,
D.Dnumber,
M.Fname AS DMgr_Fname,
M.Minit AS DMgr_Minit,
M.Lname AS DMgr_Lname,
M.Salary AS DMgr_Salary
FROM
EMPLOYEE M,
DEPARTMENT D
WHERE
D.Mgr_SSN = M.SSN
) ON E.Dno = Dnumber
NATURAL JOIN (
SELECT
D1.Dname,
AVG(E1.SALARY) AS DEPT_AVG_SALARY
FROM
EMPLOYEE E1,
DEPARTMENT D1
WHERE
E1.Dno = D1.Dnumber
GROUP BY
D1.Dname
);

Oracle: single-row subquery returns more than one row

SELECT e.name
from emp e, departm d, salery s
WHERE e.dep=d.depid
AND s.emp= e.empid
AND s.sal > (SELECT round(avg(s.sal)) as AVGSAL
from emp e, departm d, salery s
WHERE e.dep=d.depid AND s.emp= e.empid
GROUP BY d.depid
);
MY Tables are:
emp (empid, name, dep)
departm (depid, name, headdep)
salery (emp, cost, sal, fdate)
I have some foreign keys:
departm: FOREIGN KEY (headdep) REFERENCES departm(depid)
emp: FOREIGN KEY(dep) REFERENCES departm(depid)
salery: FOREIGN KEY(emp) REFERENCES emp(empid)
I want to print a list of all employees which earn more money than the average of theyr department but when i run this query i have an error: single-row subquery returns more than one row
Can anyone help me? What's the problem with my query?
Finally I want to create a procedure, but first of all i have to write the query.
Thank you guys...
Analytic functions are the way to go on this query, but your version has several problems. You would see these problems much more readily if you used proper join syntax. You seem to be learning SQL, so just remember one simple rule: NEVER use commas in from clauses.
Apart from the syntax problem, you have a problem with your correlated subquery. Here is a version that should work:
SELECT e.vollername
from emp e join
salery s
on s.emp= e.empid
WHERE s.sal > (select round(avg(s2.sal)) as AVGSAL
from emp e2 join
salery s2
on s2.emp= e2.empid
where e2.dep = e.depid
);
Note the removal of the departm table from both the inner and the outer queries. In the outer one, it was merely superfluous. In the inner one, it prevented the query from producing the correct results because it removed the correlation.
This is somewhat easier with analytic functions (I also removed the join with departm, since it is not needed):
SELECT e.vollername
FROM (
SELECT
e.vollername,
s.sal,
round(avg(s.sal) over (partition by e.dep)) as avg_dep_sal
FROM
emp e
JOIN salery s ON e.empid = s.emp
)
where sal > avg_dep_sal

sql query null data was not retrieved

Table DEPARTMEnT
TABLE EMPLOYEE
There is the Operations Department which has not any employee. So, i believed that the query would retrieved also the row(image 1):
Department_ID=10 , Department_Name =Operations, Employee=0
Why doesnt happen???
SELECT EMPLOYEE.Department_ID, DEPARTMENT.Department_Name, Count(*) AS Employees
FROM EMPLOYEE right JOIN DEPARTMENT ON DEPARTMENT.Department_ID = EMPLOYEE.Department_ID
GROUP BY DEPARTMENT.Department_Name,.EMPLOYEE.Department_ID
Since the principal data you care about for this query is coming from the DEPARTMENT table, you may want to consider rewriting your query to be:
SELECT DEPARTMENT.Department_ID, DEPARTMENT.Department_Name, Count(EMPLOYEE.Employee_ID) As Employees
FROM DEPARTMENT
LEFT JOIN EMPLOYEE ON EMPLOYEE.Department_ID = DEPARTMENT.Department_ID
GROUP BY DEPARTMENT.Department_ID, DEPARTMENT.Department_Name
The default join is an inner join, which only returns rows for which at least one row is found on both sides. Replace join with left join to retrieve departments without employees.
Example code:
SELECT e.Department_ID
, d.Department_Name
, count(e.Employee_ID) AS Employees
FROM Department d
LEFT JOIN
Employee e
ON d.Department_ID = e.Department_ID
GROUP BY
d.Department_ID
, d.Department_Name
This should do the trick. You could put in a RIGHT JOIN if you have the EMPLOYEE table first, but the reason this is not good is because soon your queries will start being a mix of LEFT and RIGHT joins, which becomes very hard to read, even for seasoned SQL professionals. By sticking with LEFT JOIN you keep the query maintainable and understandable. (In very rare circumstances RIGHT JOIN may simplify a query that has a complex order of precedence but I have only done it something like twice to avoid having to add parentheses around groups of joins).
SELECT
D.Department_ID,
D.Department_Name,
Employees = Count(*)
FROM
dbo.DEPARTMENT D
LEFT JOIN dbo.EMPLOYEE E
ON D.Department_ID = E.Department_ID
GROUP BY
D.Department_ID,
D.Department_Name
Also, I recommend that you use aliases for your tables instead of full table names. The query becomes much easier to scan and understand when there is consistent use of aliases. Spelling out the entire table name all too often obscures other parts of the query.