Selecting same column twice from a single table but with different conditions - sql

I would like to display names and numbers of employees with numbers and names of their bosses, like below:
There is only one table:
I tried this so far:
SELECT
ID,
Name,
Boss,
(SELECT Name FROM Employees WHERE ID IN (SELECT Boss FROM Employees))
FROM Employees
But it gives me an error:
"Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as
an expression"

You need a self-join; something like:
Select a.ID, a.Name, b.ID as Boss, b.Name as BossName
from Employees A
left join Employees B
on a.Boss = b.ID

One way to do this is to limit the sub-query to return the first result only using a TOP clause. You need to join it to the main table as well:
SELECT
e.ID,
e.Name,
e.Boss,
(SELECT top 1 Name FROM Employees b where b.ID = e.Boss) as BossName
FROM Employees e

I actually had to create a similar split in my Azure DB. Separating genders (men & Women) using their title.
Here is the bit of code.
select
(Select count(b.TITLE) from SalesLT.Customer b Where Title LIKE 'Mr%') as women
,(Select count(c.TITLE) from SalesLT.Customer c Where Title LIKE 'Ms%') as men
,(Select count(d.TITLE) from SalesLT.Customer d Where Title NOT LIKE 'Ms%' AND Title NOT LIKE 'Mr%' ) as unidentified
,count(a.TITLE) as Total
from SalesLT.Customer a
Result:

APH's answer is definitely the most effective solution. However, I would like to add a solution with a correlated subquery to show that you have been quite close to a working query.
SELECT
E.ID,
E.Name,
E.Boss,
(SELECT Name FROM Employees WHERE ID = E.Boss) AS BossName
FROM Employees E
The problem with your original query was that the condition in the subquery did not depend on the specific record. The subquery returned the name of every employee who is the boss of someone else.
Please note that this solution usually has a worse performance than the join because it has to execute the subquery for every item. Although the result is equivalent to that of the join many optimizers do not realize this because they do not touch subqueries.

Related

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

SQL: Distinct and OrderBy issue

I'm currently working on a query that should return all the rows from CONCENTRATOR table. However, it has to be sortable by all concentrator's columns, as well as department name and type name.
Here are the concentrator's columns :
CONCENTRATOR_ID
NAME
INTERNALADDRESS
TYPE_ID
DEPARTMENT_ID
TYPE_ID and DEPARTMENT_ID are linked to respectively DEPARTMENT table and TYPE table, with both a NAME column.
Here are the constraints :
concentrators are sortable by id, name, address, type's name and department's name
distinct department names (if a same concentrator has 2 department, return only one row)
To resume, I would need something like SELECT * on concentrator columns, and DISTINCT department.name as well but seems complicated... I tried lots of requests but couldn't find any one working.
Could anybody help me please ?
The request I'm looking for should be something like this:
SELECT DISTINCT d.NAME as "department.name", t.NAME as "type.name", *
FROM "CONCENTRATOR" c
LEFT OUTER JOIN "CONCENTRATOR_GROUP" USING(CONCENTRATOR_ID)
LEFT OUTER JOIN "GROUP" g USING(GROUP_ID)
LEFT OUTER JOIN "TYPE" t USING(TYPE_ID)
LEFT OUTER JOIN "DEPARTMENT" d USING(DEPARTMENT_ID)
ORDER BY TRIM(UPPER(c.name)) ASC
There are a few things to note here. I'm really not fond of "natural joins" as they simply disguise useful detail in my view, so I have not used them. I had to assume that the table "GROUP" joins via CONCENTRATOR_GROUP for an example of that missing detail.
The table name "GROUP" isn't a great idea as it is a very commonly used reserved word. I'd not recommend using such a word as a table name. Due to this "GROUP" is quoted (it isn't normal to quote object names in Oracle my experience).
You talk about "distinct" as if it has some magical quality that I should intuitively understand. It doesn't, and I don't. Let's say there are just 2 departments both are also "distinct"
DeptX
DeptY
So now let's assume there are 2 concentrators, both of these are "distinct" too:
ConcenA
ConcenB
Both concentrators are used in both departments, so we produce this query:
select distinct
c.name as c_name, d.name as d_name
from concentrators c
inner join departments d on c.dept_id=d.dept_id
The result is:
ConcenA DeptX
ConcenB DeptX
ConcenA DeptY
ConcenB DeptY
All 4 rows are "distinct"
The point is that "select distinct" is a "row operator", i.e. it considers the entire row to determine if any part of the row is different to all other rows. There are no subtleties or options to "select distinct", it always works the same way (over the entire row). So, with this in mind, we now know that "select distinct" simply is not going to be the right technique (and due to the technical definition of distinct you might also sense it isn't a good way to describe your problem either).
So, as "select distinct" isn't the right technique typically one can turn to these as techniques: "group by" or "row_number()"
because these do give us subtleties and options.
Now you haven't explained why or how you would choose just one department (in fact, to me, it sounds weird you would choose just one) but below I offer you A way to do this using row_number() and the "subtlety" being used is the ORDER BY which gives the number 1 to the first Department Name in alphabetic order, all other departments get more than 1; and this occurs for each CONCENTRATOR_ID because row_number() is "partitioned by" that field.
SELECT
department_name
, type_name
, NAME
, CONCENTRATOR_ID
, INTERNALADDRESS
, TYPE_ID
, DEPARTMENT_ID
FROM (
SELECT
d.NAME AS department_name
, t.NAME AS type_name
, c.CONCENTRATOR_ID
, c.NAME
, c.INTERNALADDRESS
, c.TYPE_ID
, c.DEPARTMENT_ID
, ROW_NUMBER() OVER (PARTITION BY c.CONCENTRATOR_ID
ORDER BY d.NAME, t.NAME, c.NAME) AS RN
FROM CONCENTRATOR c
LEFT OUTER JOIN CONCENTRATOR_GROUP cg
ON c.CONCENTRATOR_ID = cg.CONCENTRATOR_ID
LEFT OUTER JOIN "GROUP" g
ON cg.GROUP_ID = g.GROUP_ID
LEFT OUTER JOIN TYPE t
ON c.TYPE_ID = t.TYPE_ID
LEFT OUTER JOIN DEPARTMENT d
ON c.DEPARTMENT_ID = c.DEPARTMENT_ID
) sq
WHERE RN = 1 /* HERE is where we restrict output to one department per concentrator */
ORDER BY
NAME ASC
, CONCENTRATOR_ID
;
I have no reason to change the type of joins as you can see they remain as left outer joins - but I suspect there may be no valid reason for all or some of these. Do use the more efficient INNER JOIN if you can.

correlated subquery execution when it has more than one subquery

I have doubts about correlated sub-query execution When there is more than one sub-query.
I know about correlated sub-query execution when there is only one sub-query. But, if there is more than one I am not able to understand.
I have referred to many books and web sites but I could not find a suitable answer.
Below are two queries. I know how query1 executes but how does query2?
query 1:
select e.ename, e.city
from emp1 e
where exists ( select f.ename
from emp2 f
where f.ename = ’ajay’
and e.city = f.city
)
query2:
select e.ename
from employee e
where exists ( select ‘x’
from emp_company c
where e.ename = c.ename
and exists ( select ‘x’
from company m
where c.cname = m.cname
and m.city = ’bombay’
)
)
In the first step of execution of query1 the employee table’s first row ename, city considered.
In the second step of execution of query1, the considered city from main query is compared with every row of emp2.
The, while comparing city from the main query with every row of emp2; if any row satisfies condition that rows ename is added to a list.
The formed list is returned to the main query.
If the returned list is not empty then exists evaluates to true. Then emp1 table’s ename, city added output.
The ename, city from emp1 are selected from second row of emp1 table.
Can you please explain the execution of query2 as I explained query1?
What your second query says is: give me employees if there exits companies they work in (first subquery) and if such companies (taken from the first subquery) exist in Bombay (second subquery). You would get the same result with the following, probably more effecient, select statement:
select e.ename
from employee e,
emp_company ec,
company c
where e.ename = ec.ename
and ec.cname = c.cname
and c.name = 'bombay'
That's for the logic. If you are interested in details on how Mr. Oracle fetches the rows you could take a look at Query Explain Plan.

How to use "IN" for more than one column

This question might be trivial or even silly but I was wondering if there is a way to use "IN" on more than one column on one to one matching.
For example I use
select emp_id from employee where emp_id IN (select emp_id from employee_other)
How could I achieve something like
select emp_id from employee where emp_id,emp_org IN (select emp_id,emp_org from employee_other)
I know I cant be using the following because it will simply do the union whereas I want a selection based on one to one record matching.
select emp_id from employee where emp_id IN (select emp_id from employee_other) and emp_org in (select emp_org from employee)
Please note that I am reluctant to use EXCEPT.
Thanks guys
You may want to use the EXISTS operator
select e.emp_id
from employee e
where EXISTS
(
SELECT *
FROM employee_other eo
WHERE e.emp_id = eo.emp_id
AND e.emp_org = eo.emp_org
)
IN in Microsoft SQL Server only works with a single column, ie. you can only write X IN (...), never anything remotely like X,Y IN (...).
There are two ways to handle this, depending on your data:
Joining with a sub-query
Using EXISTS
To JOIN, do this:
select emp_id
from employee
inner join (select emp_id,emp_org from employee) as x
on employee.emp_id = x.emp_id and employee.emp_org = x.emp_org
Your example is a bit lousy, however, since you're using the same table.
To use EXISTS, do this:
select emp_id
from employee
where exists (
select emp_id,emp_org from employee e2
where e2.emp_id = employee.emp_id and e2.emp_org = employee.emp_org)
This, in the same way as the join, links the main table to the "sub-query" table, but whereas the join will produce duplicate rows if the "sub-query" produces multiple hits, the EXISTS clause will not.
I don't understand what you are trying to accomplish with emp_org in (select emp_org from employee) isn't that always true?
does this work?
select emp_id from employee e
where exists (select 1 from employee_other eo
WHERE e.emp_id =eo.emp_id and
AND e.emp_org = eo.emp_org )
You had it almost completely right in your second example. You just need to add parens around your column names.
select emp_id
from employee
where (emp_id,emp_org) IN (select emp_id,emp_org from employee)
Use Inner Join
select e1.emp_id from employee e1
inner join employee_other e2 on e1.emp_id = e1.emp_id and e1.emp_org = e2.emp_org
You may have to use Distinct in case the employee_other table causes dups.
select Distinct e1.emp_id from employee e1
inner join employee_other e2 on e1.emp_id = e1.emp_id and e1.emp_org = e2.emp_org

Convert SQL-query with subselect in select section to HQL

I have trouble with converting native SQL query to HQL.
Query is something like follows:
select count(*)
, sum(select count(*) from employee e where e.company_id=c.id))
from company c where c.id = someID
First returned value is count of companies, second - amount of employees for specified company.
I.e. I have to get this two values for company with id=someID.
The trouble is hibernate doesn't support subselects in SELECT section, only in WHERE - by specification.
Actually I can:
1) use native query to run this through EntityManager
2) do the same dividing this "complex" query to two simpler SQL queries
But may be there are exist more convenient methods to realize initial query in HQL? - this is a question.
Thank you.
this might work:
select count(c.*) as count_companies
, c as count_emp
from company c
inner join
(
select company_id, count(*) as c
from employee
where employee.company_id = someID
group by company_id
) e
on e.company_id=c.id
group by c.id