I am using HR schema,Employees and Departments table. I was asked during the interview following query.
Write a query to print department_name,department_no, count of all employees in corresponding department,but the condition is I can use only one attribute with group by clause.
SQL> desc employees;
Name Null? Type
----------------------------------------------------- -------- ------------------------------------
EMPLOYEE_ID NOT NULL NUMBER(6)
FIRST_NAME VARCHAR2(20)
LAST_NAME NOT NULL VARCHAR2(25)
EMAIL NOT NULL VARCHAR2(25)
PHONE_NUMBER VARCHAR2(20)
HIRE_DATE NOT NULL DATE
JOB_ID NOT NULL VARCHAR2(10)
SALARY NUMBER(8,2)
COMMISSION_PCT NUMBER(2,2)
MANAGER_ID NUMBER(6)
DEPARTMENT_ID NUMBER(4)
SQL> desc departments
Name Null? Type
----------------------------------------------------- -------- ------------------------------------
DEPARTMENT_ID NOT NULL NUMBER(4)
DEPARTMENT_NAME NOT NULL VARCHAR2(30)
MANAGER_ID NUMBER(6)
LOCATION_ID NUMBER(4)
I tried various options to frame query, I used having clause with count(employee_id) but to no effect.Any help appreciated
Since you're getting two values from the same table, and one is the key (or more importantly, unique), you can just apply an aggregate function to the other one:
select d.department_id,
min(d.department_name) as department_name,
count(e.employee_id)
from departments d
left join employees e on e.department_id = d.department_id
group by d.department_id;
SQL Fiddle. Either min or maxof the name would work, and you only need to group by the ID. You could do it the other way around, but name is less likely to be unique.
Not sure if it would count, but as an exercise in futility, you could also just concatenate the values:
select d.department_id ||' '||d.department_name,
count(e.employee_id)
from departments d
left join employees e on e.department_id = d.department_id
group by d.department_id ||' '|| d.department_name;
SQL Fiddle.
You can lpad the department_id to maintain the illusion that they are separate columns, sort of. (It doesn't show up well as a Fiddle though).
Pointless hack, but then it's an interview question so maybe that's appropriate, and seems to just about fulfil the wording you used. Depending on how 'attribute' is defined - I'm taking it to mean a single expression here, which may be a stretch...
Related
I barely just started on database languages, so I do not know anything about JOIN, PARTITION, ETC. but this is what I have so far.
What I tried:
SELECT MIN(SALARY) AS "MIN SALARY", WORKING.DID, ENAME
FROM DEPARTMENT, EMPLOYEE, WORKING
WHERE WORKING.EID = EMPLOYEE.EID
GROUP BY WORKING.DID, ENAME;
Result:
MIN SALARY DID ENAME
---------- --- ------
10000 101 Dustin
15000 102 Bob
20000 102 David
10000 102 Dustin
10000 103 Alex
8000 103 Alice
7000 103 Mike
What I want:
MIN SALARY DID ENAME
---------- --- ------
10000 101 Dustin
10000 102 Dustin
7000 103 Mike
Table structures:
CREATE TABLE EMPLOYEE (
EID INTEGER NOT NULL,
ENAME VARCHAR(25),
SALARY DECIMAL,
PRIMARY KEY (EID)
);
CREATE TABLE WORKING (
EID INTEGER NOT NULL,
DID INTEGER NOT NULL,
STIME DATE,
FOREIGN KEY (EID) REFERENCES EMPLOYEE (EID),
FOREIGN KEY (DID) REFERENCES DEPARTMENT (DID),
PRIMARY KEY (EID, DID)
);
CREATE TABLE DEPARTMENT (
DID INTEGER NOT NULL,
DNAME VARCHAR(10),
DADDRESS VARCHAR(20),
PRIMARY KEY (DID)
);
ER Diagram
You can prefer using online analytical processing(OLAP) functions such as DENSE_RANK() to get the desired result easily
SELECT Q.SALARY, Q.DID, Q.ENAME
FROM
(
SELECT E.SALARY, W.DID, E.ENAME,
DENSE_RANK() OVER (PARTITION BY W.DID ORDER BY E.SALARY) AS DR
FROM WORKING W
JOIN DEPARTMENT D
ON D.DID = W.EID
JOIN EMPLOYEE E
ON E.EID = W.EID
) Q
WHERE DR = 1
in which explicit JOIN syntax, the syntax having comma-seperated tables is considered as old-deprecated one , is used. Using (INNER)JOIN is enough for your case, whereas there may be cases needing OUTER (LEFT-RIGHT or FULL) JOINs.
Within an analytic function, PARTITION Clause is used to express the GROUPING BY criteira. Based on the ascending ORDER for the salary, we'll get the minimum ones to be filtered out through the results coming from the DENSE_RANK() function with values equal to one.
By using this method, you can get all the employees with the minimum salaries even there are more than one person for each department.
Moreover, better to alias the tables by their name's first character(or by two or three chars contained within their names depending on the common chars. for the first letters) in order to qualify the columns within the query rather than using whole names.
There is no need to use the DEPARTMENT table in the statement.
While using OLAP functions is preferable way and generally provides better performance, there is another method not using OLAP functions based on rejoining base tables to a sub-select with grouped result, where we get department minimum salary. We rejoin these department minimums back to WORKING & EMPLOYEE picking up employees with the department minimum salary only.
SELECT G.DID, G.SALARY, E.ENAME
FROM
(
SELECT W.DID, MIN(E.SALARY) SALARY
FROM WORKING W
JOIN EMPLOYEE E ON E.EID = W.EID
GROUP BY W.DID
) G
JOIN WORKING W ON W.DID = G.DID
JOIN EMPLOYEE E ON E.EID = W.EID AND E.SALARY = G.SALARY;
CREATE TABLE DEPARTMENTS
( DEPARTMENT_ID NUMERIC(4,0),
"DEPARTMENT_NAME" VARCHAR(30 ) CONSTRAINT DEPT_NAME_NN NOT NULL ,
"MANAGER_ID" NUMERIC(6,0),
"LOCATION_ID" NUMERIC(4,0),
);
CREATE TABLE EMPLOYEES
( EMPLOYEE_ID NUMERIC(6,0),
FIRST_NAME VARCHAR(20 ),
LAST_NAME VARCHAR(25 ) CONSTRAINT "EMP_LAST_NAME_NN" NOT NULL ,
EMAIL VARCHAR(25 ) CONSTRAINT "EMP_EMAIL_NN" NOT NULL ,
PHONE_NUMBER VARCHAR(20 ),
HIRE_DATE DATE CONSTRAINT "EMP_HIRE_DATE_NN" NOT NULL ,
JOB_ID VARCHAR(10 ) CONSTRAINT "EMP_JOB_NN" NOT NULL ,
SALARY NUMERIC(8,2),
COMMISSION_PCT NUMERIC(2,2),
MANAGER_ID NUMERIC(6,0),
DEPARTMENT_ID NUMERIC(4,0),
);
I need to list the name of the department, average salary and number of employees working in that department who got commission.
SELECT DEPARTMENT_NAME, AVG(SALARY), COUNT(COMMISSION_PCT)
FROM DEPARTMENTS JOIN EMPLOYEES USING (DEPARTMENT_ID);
GROUP BY DEPARTMENT_NAME
This is what I've got so far, but it gives me an error:
"DEPARTMENT_ID" is not a recognized table hints option. If it is intended as a parameter to a table-valued function or to the CHANGETABLE function, ensure that your database compatibility mode is set to 90.
I don't know which DBMS did you use.
I think that will be work,when you use INNER JOIN instead of USING
SELECT DEPARTMENT_NAME, AVG(SALARY), COUNT(COMMISSION_PCT)
FROM DEPARTMENTS
INNER JOIN EMPLOYEES on DEPARTMENTS.DEPARTMENT_ID = EMPLOYEES.DEPARTMENT_ID
GROUP BY DEPARTMENT_NAME;
Problem:
The HR department needs a query that prompts the user for an employee
last name. The query then displays the last name and hire date of any employee
in the same department as the employee whose name they supply(excluding that employee).
For example, if the user enters Zlotkey, find all employees who work with
Zlotkey (excluding Zlotkey).
I managed to do this so far, but i have no clue how to finish it. For now it shows me all employees after i write the last_name excluding it. Any suggestions please how to continue?
SELECT last_name, TO_CHAR(hire_date,'DD-MON-YYYY') AS "HIRE_DATE"
FROM employees
WHERE last_name <>ALL (SELECT '&last_name'
FROM employees)
AND department_id IN (SELECT department_id
???....
P.S: This problem is from the Oracle tutorials (Oracle Database 11g: SQL Fundamentals 1 of 1 (year 2009), Practice 7 , exercise 1 for people who already done this:).
Something like this?
SELECT last_name, TO_CHAR(hire_date,'DD-MON-YYYY') AS "HIRE_DATE"
FROM employees a
JOIN (Select department_id from employees where last_name = :surname) b on a.department_id = b.department_id
and last_name <> :surname
EDIT
The only problem with this type of solution is that if there are two people with the same surname in different departments, so it might be useful to maybe use something like an employee number as a filter instead of surname.
SELECT LAST_NAME, HIRE_DATE
FROM EMPLOYEES
WHERE DEPARTMENT_ID= (SELECT DEPARTMENT_ID
FROM EMPLOYEES
WHERE LAST_NAME LIKE '&NAME')
AND LAST_NAME <> '&NAME';
I am using SQL Developer and I have this table structure:
Name Null Type
-------------- -------- ------------
EMPLOYEE_ID NOT NULL NUMBER(6)
FIRST_NAME VARCHAR2(20)
LAST_NAME NOT NULL VARCHAR2(25)
DEPARTMENT_ID NUMBER(4)
SALARY NUMBER(8,2)
What I want to do is print out the department_id which has AT LEAST 2 employees with salaries greater than 10000.
I though this would be the query
select department_id
from employees
having count(select * from employees where salary > 10000) > 2
group by department_id;
but, from what I found out, you can't put a SELECT statement inside COUNT so now I an stuck and I don't know how else am I supposed to do this query. Any suggestion is welcome.
UPDATE: Please note that I want AT LEAST 2 employees to have salary > 10000, not all of them
SELECT Department_Id,
COUNT(*)
FROM Employee
WHERE Salary > 10000
GROUP BY Department_Id
HAVING COUNT(*) > 1
SQL Fiddle example.
SELECT department_id FROM employees
WHERE salary > 10000 GROUP BY department_id HAVING COUNT(*) >= 2;
I am using oracle's SQL Developer. To begin with, I have this table:
Name Null Type
-------------- -------- ------------
EMPLOYEE_ID NOT NULL NUMBER(6)
FIRST_NAME VARCHAR2(20)
LAST_NAME NOT NULL VARCHAR2(25)
DEPARTMENT_ID NUMBER(4)
I would like to retrieve(select) the ID of the department in which are the most employees.
I managed through a statement to retrieve all the numbers of the employees in every department:
select count(employee_id), department_id
from employees
group by department_id;
It gives me something like:
count(employee_id) | department_id
---------------------|------------------
6 100
16 30
1 12
What I would like to do is ONLY to retrieve the department_id 30, which (in this case) has the most employees.
A typical way to do this in Oracle:
select department_id
from (select count(employee_id), department_id
from employees
group by department_id
order by count(employee_id) desc
) t
where rownum = 1;
If you have potential duplicates and want all the department ids, then a join to the max or analytic function is a better approach. For example:
select department_id
from (select count(employee_id), department_id,
rank() over (order by count(employee_id) desc) as seqnum
from employees
group by department_id
) t
where seqnum = 1;