Oracle : It give a error in a dynamic view - oracle

I have two table one is employee and one is department. I am creating the dynamic view that will rank all departments by salary. The view should pull information from Department and Employee, sum the salary by department, and rank the department by salary.
CREATE TABLE DEPARTMENT
(DEPARTMENT_ID NUMBER PRIMARY KEY,
DEPARTMENT_NAME VARCHAR(30) NOT NULL
);
CREATE TABLE JOBS
(JOB_ID NUMBER PRIMARY KEY,
JOB_TITLE VARCHAR(35) NOT NULL,
MIN_SALARY DECIMAL NOT NULL,
MAX_SALARY DECIMAL NOT NULL
);
CREATE TABLE EMPLOYEES
(EMPLOYEE_ID NUMBER PRIMARY KEY,
FIRST_NAME VARCHAR(20) NOT NULL,
LAST_NAME VARCHAR(25) NOT NULL,
EMAIL VARCHAR(25) NOT NULL,
PHONE_NUMBER VARCHAR(20) NOT NULL,
HIRE_DATE DATE NOT NULL,
JOB_ID NUMBER NOT NULL,
SALARY DECIMAL NOT NULL,
DEPARTMENT_ID NUMBER NOT NULL,
CONSTRAINT emp_job_fk FOREIGN KEY(JOB_ID) REFERENCES JOBS(JOB_ID),
CONSTRAINT emp_department_fk FOREIGN KEY(DEPARTMENT_ID) REFERENCES DEPARTMENT(DEPARTMENT_ID)
);
INSERT INTO DEPARTMENT (DEPARTMENT_ID,DEPARTMENT_NAME)
VALUES(1,'IT');
INSERT INTO DEPARTMENT (DEPARTMENT_ID,DEPARTMENT_NAME)
VALUES(2,'Sales');
INSERT INTO JOBS (JOB_ID,JOB_TITLE,MIN_SALARY,MAX_SALARY)
VALUES (1,'IT Administrator',250000.00,50000.00);
INSERT INTO JOBS (JOB_ID,JOB_TITLE,MIN_SALARY,MAX_SALARY)
VALUES (2,'Salesman',200000.00,40000.00);
Here is I create so far but it give me a error
ORA-00979: not a GROUP BY expression
00979. 00000 - "not a GROUP BY expression"
*Cause:
*Action:
Error at Line: 4 Column: 9
Here is my code
select department_id,department_name,total_salary
from(
select department_id,department_name, SALARY, count(*) as total_salary from(
select dep.department_id , dep.department_name ,emp.SALARY,
DENSE_RANK() OVER (PARTITION BY department_name ORDER BY salary)
from departments dep
inner join employees emp on dep.DEPARTMENT_ID = emp.DEPARTMENT_ID
)
GROUP BY SALARY)

Your query needs to join EMPLOYEES (to get the salaries) to DEPARTMENT (to get the DEPARTMENT_NAME). Calculate the total salary for each department by summing the employee salaries, not counting them. The GROUP BY needs to include the non-aggregated columns.
Then you need to rank the departments by the total salary per department. This query ranks the departments with highest salary = 1. It uses a left join to cater for departments with no employees.
select department_id
, department_name
, total_salary
, rank() over (order by total_salary desc) as dept_rank
from (
select d.department_id
, d.department_name
, sum(e.SALARY) as total_salary
from department d
left join employees e
on e.department_id = d.department_id
group by d.department_id
, d.department_name
)
/

Related

SQL Oracle Select, Count, and Inner Join

Having issues creating a query that displays the customerID, customerFirName, and customerZip from the customers table, based on when the customer has purchased more than one vehicle within the sales table. Here are the table creations:
CREATE TABLE CUSTOMERS
(customerID INT PRIMARY KEY,
customerFirName VARCHAR(20) NOT NULL,
customerLasName VARCHAR(20) NOT NULL,
customerMiName VARCHAR(1) NOT NULL,
customerStreet VARCHAR(40) NOT NULL,
customerState VARCHAR(15) NOT NULL,
customerCity VARCHAR(20) NOT NULL,
customerZip VARCHAR(15) NOT NULL);
CREATE TABLE SALES
(saleID INT PRIMARY KEY,
grossSalePrice DECIMAL(9,2),
vehicleStatus VARCHAR(10) NOT NULL CHECK (lower(vehicleStatus) IN ('available', 'sold', 'pending')),
saleDate DATE,
saleMileage INT,
customerID INT,
salespersonID INT,
vehicleVIN VARCHAR(25),
CONSTRAINT SALES_FK1 FOREIGN KEY (customerID) REFERENCES CUSTOMERS(customerID);
Here is the desired output:
customerID customerFirName customerZip Number_of_Sales
1 Bob 12345 2
2 Jim 94949 3
3 Tom 99330 4
Here is what I've tried....I'm having issues creating a single SELECT statement that has an inner join to combine the SALES.customerID field on the CUSTOMERS.customerID field. Where am I going wrong? Thanks!
SELECT CUSTOMERS.customerFirName, CUSTOMERS.CustomerID, CUSTOMERS.customerZip, COUNT(SALES.customerID)
FROM CUSTOMERS
INNER JOIN SALES ON CUSTOMERS.customerID=SALES.customerID
GROUP BY SALES.customerID
HAVING COUNT(SALES.customerID) > 1;
AND
SELECT COUNT (CUSTOMERS.customerID), customerFullName, customerZip
FROM CUSTOMERS
INNER JOIN SALES ON CUSTOMERS.customerID=SALES.customerID
GROUP BY SALES.customerID
HAVING COUNT(SALES.customerID) > 1;
I guess the issus is on group by field.
SELECT CUSTOMERS.customerFirName, CUSTOMERS.CustomerID, CUSTOMERS.customerZip,
COUNT(SALES.customerID)
FROM CUSTOMERS
INNER JOIN SALES ON CUSTOMERS.customerID=SALES.customerID
GROUP BY CUSTOMERS.customerFirName, CUSTOMERS.CustomerID, CUSTOMERS.customerZip
HAVING COUNT(SALES.customerID) > 1;
Maybe just do a count in a sub select:
select * from
(SELECT CUSTOMERS.customerFirName, CUSTOMERS.CustomerID, CUSTOMERS.customerZip, COUNT(*) num_sales
FROM CUSTOMERS
INNER JOIN SALES ON CUSTOMERS.customerID=SALES.customerID
GROUP BY CUSTOMERS.customerFirName, CUSTOMERS.CustomerID, CUSTOMERS.customerZip)
where num_sales > 1;

How to bind horizontal values of a table to a vertical values of another table in oracle database

i have 2 tables .
The columns start with attributes are change based on department. the description of attributes are here
My requirement is to get the values of each attributes with its primary key based on the department as table bellow.
Honestly i am stuck on this problem in my program. I have no permission to change the tables and there is no common unique key column.i would appreciate if anyone could provide me a suggestion.
with a as (
select a.*, row_number() over (partition by department order by attributeID) rn
from attributes a),
e as (
select employeeId, department, attribute1, 1 rn from employees union all
select employeeId, department, attribute2, 2 rn from employees union all
select employeeId, department, attribute3, 3 rn from employees
)
select e.employeeId, a.attributeid, e.department, a.attribute, a.meaning,
e.attribute1 as value
from e join a on a.department=e.department and a.rn=e.rn
order by e.employeeId, a.attributeid
Test data and output:
create table employees (employeeID number(3), name varchar2(10), department varchar2(5), age number(3), attribute1 varchar2(10), attribute2 varchar2(10), attribute3 varchar2(10));
insert into employees values (1, 'john', 'IT', 22, 'attr1val1', 'attr2val2', null);
insert into employees values (2, 'jane', 'HR', 32, 'attr1val3', 'attr2val4', 'attr3val5');
insert into employees values (3, 'joe', 'HR', 23, 'attr1val6', 'attr2val7', 'attr3val8');
insert into employees values (4, 'jack', 'IT', 45, 'attr1val9', 'attr2val10', null);
create table attributes (attributeID number(3), department varchar2(10), attribute varchar2(10), meaning varchar2(10));
insert into attributes values (1, 'IT', 'attribute1', 'laptoptype');
insert into attributes values (2, 'IT', 'attribute2', 'networkloc');
insert into attributes values (3, 'HR', 'attribute1', 'location');
insert into attributes values (4, 'HR', 'attribute2', 'position');
insert into attributes values (5, 'HR', 'attribute3', 'allocation');
EMPLOYEEID ATTRIBUTEID DEPARTMENT ATTRIBUTE MEANING VALUE
---------- ----------- ---------- ---------- ---------- ----------
1 1 IT attribute1 laptoptype attr1val1
1 2 IT attribute2 networkloc attr2val2
2 3 HR attribute1 location attr1val3
2 4 HR attribute2 position attr2val4
2 5 HR attribute3 allocation attr3val5
3 3 HR attribute1 location attr1val6
3 4 HR attribute2 position attr2val7
3 5 HR attribute3 allocation attr3val8
4 1 IT attribute1 laptoptype attr1val9
4 2 IT attribute2 networkloc attr2val10
Edit: Explanation
In answer I used with
clause just to divide solution into readable steps. You can move them into from clause of main query if it is
more comfortable for you. Anyway: subquery a reads data from table attributes and adds number for rows,
so for each department they are allways numbered from 1. I used row_number() for that. Subquery e unions (all) required attributes and numbers
them accordingly. Numbers generated in both subqueries are then used in main join: a.department=e.department and a.rn=e.rn.
Alternative 1 - if you are using Oracle 11g you could use the unpivot. See what is generated by subquery, and how it is joined with attributes table:
with e as (
select employeeId, name, department, attribute, value from employees
unpivot (value for attribute in ("ATTRIBUTE1", "ATTRIBUTE2", "ATTRIBUTE3"))
)
select e.employeeId, a.attributeid, e.department, a.attribute,
a.meaning, e.value
from e join attributes a on a.department=e.department
and lower(a.attribute)=lower(e.attribute)
order by e.employeeId, a.attributeid;
Alternative 2 - with hierarchical subquery generator (subquery r), realised by connect by which simple creates numbers from 1, 2, 3 which are next joined with employees and proper attribute
is attached as value in case clause. Rest is made in similiar way like in original answer.
with a as (
select a.*, row_number() over (partition by department order by attributeID) rn
from attributes a),
r as (select level rn from dual connect by level<=3),
e as (
select employeeId, department, rn,
case when r.rn = 1 then attribute1
when r.rn = 2 then attribute2
when r.rn = 3 then attribute3
end value
from employees cross join r
)
select e.employeeId, a.attributeid, e.department, a.attribute,
a.meaning, e.value
from e join a on a.department=e.department and a.rn=e.rn
order by e.employeeId, a.attributeid
All three versions gave me the same output. I also tested first option on similiar table with 100k rows and get output in few seconds (for 5 attributes). Please test all solutions and try to understand them. If you can use unpivot version I would prefer this.
Sorry for delayed explanation and any language mistakes.
The WITH clause was added with Oracle 9.2 and should do the trick. For the other attributes just add more sub queries where the filter is att.attribute = 'attribute2' or 'Attribute3'...
WITH e AS
(SELECT emp.employee_ID, emp.department, emp.attribute1
FROM employee emp),
a AS (SELECT att.attribute_id, att.attribute, att.meaning
FROM attribute_TYPE att
WHERE att.attribute = 'attribute1')a
SELECT e.employeeid, att.attributeid, e.department, a.attribute,
a.meaning e.attribute1
FROM e JOIN a ON e.department = a.department

ORA-00918: column ambiguously defined with [group by]

in SQL I have these table
create table employee(
emp_ssn number(10),
first_name varchar2(10) not null ,
second_name varchar2(10),
last_name varchar2(10) not null ,
address varchar2(20) ,
birthdate date not null ,
super_ssn number(10),
job_no number (2),
constraint employee_pk primary key (emp_ssn));
create table job (
job_no number (2),
job_name varchar2(11) ,
constraint job_pk primary key (job_no ));
and I write a query that display how many employee in each
job using group by
SELECT job_no, job_name,COUNT(emp_ssn)
FROM job j JOIN employee e
ON (j.job_no = e.job_no)
GROUP BY j.job_no, j.job_name;
and the output is
SELECT job_no, job_name,COUNT(emp_ssn)
FROM job j JOIN employee e
ON (j.job_no = e.job_no)
GROUP BY j.job_no, j.job_name;
SELECT job_no, job_name,COUNT(emp_ssn)
ERROR at line 1: ORA-00918: column ambiguously defined
can you help me please !
SELECT j.job_no --<-- you are missing alias here
, j.job_name --<-- here
,COUNT(e.emp_ssn) --<-- and here
FROM job j JOIN employee e
ON (j.job_no = e.job_no)
GROUP BY j.job_no, j.job_name;

ORACLE: multiple column subqueries

This is from Oracle Database 10g: SQL Fundamentals.
Original Oracle textbook.
Francly speaking, this all is actual so far.
There is a task which troubles me:
Display the last name, department name, and salary of any employee whose salary and commission match the salary and commission of many employee located in location ID 1700.
The topic to be learned is multiple column subqueries. This mean that we can't deviate from the offered model which is:
SELECT column, column, ...
FROM table
WHERE (column, column, ...) IN
(SELECT column, column, ...
FROM table
WHERE condition);
Well, my database:
describe 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)
describe departments
Name Null Type
--------------- -------- ------------
DEPARTMENT_ID NOT NULL NUMBER(4)
DEPARTMENT_NAME NOT NULL VARCHAR2(30)
MANAGER_ID NUMBER(6)
LOCATION_ID NUMBER(4)
There is a solution:
select
e.last_name,
d.department_name,
e.salary
from employees e, departments d
where e.department_id = d.department_id
and (salary, nvl(commission_pct, 0)) in
(select salary, nvl(commission_pct, 0)
from employees e, departments d
where e.department_id = d.department_id
and d.location_id = 1700);
SELECTED 36 ROWS.
Well, I started checking. It seems that employees from location 1700 duplicated against themselves.
Look: we take the whole list of employees and then juxtapose it with employees from location 1700. Of course, there will be duplicates.
I have prepared an excel file with some data (all employees, employees from location 1700, the result of my own desired result etc.). In the file it is more picturesque, with examples and so on. Please, have a look.
This is the file: https://skydrive.live.com/redir?resid=8CDFFBA921B002FE!150&authkey=!ADMRAE466BIunQM
Well, what I would like to do is to control that no employee is compared with themselves.
This was my variant bevore I checked the solution.
select lnme, dpt, slry
from
(
select
employee_id eid,
last_name lnme,
salary slry,
nvl(commission_pct,0) cpct,
d.department_name dpt,
location_id
from employees e
left join departments d
on e.department_id = d.department_id
)
where (slry, cpct) in
(select
employee_id ide,
salary slry,
nvl(commission_pct,0) cpct
from employees e
join departments d
on e.department_id = d.department_id and location_id = 1700)
and ide <> eid
I wanted to make sure that no employee is compared with the same employee. But I failed.
Could you comment on all this situation:
1. Whether I'm completely wrong and the solution of Oracle is perfect.
2. If Oracle is wrong, could you help me with this code?
Thank you in advance.
The question asks you to identify any employee whose salary and commission matches any combination of salary and salary of employees in Department 1700. The question doesn't say to exclude employees from that department. So it is quite correct for them to appear in the result set.
"One employee can't match themselves."
You're not matching employees, you're matching salaries . It just so happens that the list of target salaries is generated from a sub-query on department 1700: it could just as easily be a list of numbers from a CSV file.
Let's have a thought experiment. There are three employees in department 1700:
name | salary | comm
-----------+---------+-----
CHIP | 10,100 | 0
RUNCITER | 12,200 | 0
CONLEY | 10,500 | 0
Nobody else in the company earns above 10K.
So what is the answer to the question: how many employee have a salary and commission which match the salary and commission of any employee located in department 1700?
Three
Zero
Then Joe gets a modest raise and the salaries look like this:
name | salary | comm
-----------+---------+-----
CHIP | 10,500 | 0
RUNCITER | 12,200 | 0
CONLEY | 10,500 | 0
So what is the answer to the same question now?
Still three
Two
The answer is 'three' in both cases.
Why?
Because SQL is a set-based programming language, and the set of all employees who earn the exact salary and commission as employees in department 1700 must include all employees who work in department 1700.
There is a different set, which is the set of all employees who earn the exact salary and commission as employees in department 1700 who don't themselves work in department 1700. But that is not the set the SQL Fundamentals quiz is looking for.

detecting if a part of a string matches another string

hi guys this is the question:
Display the employee ID and email of employees whose email address has their full last name in it.
what do you think can I use? Can I use substr? if not, any other suggestions? Thanks guys peace!
I'd use regular expressions, for example
CREATE TABLE employees (
emp_id NUMBER PRIMARY KEY,
first_name VARCHAR2(30),
last_name VARCHAR2(30),
email VARCHAR2(255)
);
INSERT INTO employees VALUES (1, 'Joseph', 'Ratzinger', 'benedictxvi#vatican.va');
INSERT INTO employees VALUES (2, 'Tim', 'Cook', 'tcook#apple.com');
INSERT INTO employees VALUES (3, 'Larry', 'Ellison', 'Ellison#oracle.com');
INSERT INTO employees VALUES (4, 'Michael', 'Dell', 'Michael#dell.com');
SELECT emp_id, email FROM employees WHERE regexp_like(email, last_name);
3 Ellison#oracle.com
To include case independent results, use:
SELECT emp_id, email FROM employees WHERE regexp_like(email, last_name, 'i');
2 tcook#apple.com
3 Ellison#oracle.com
4 Michael#dell.com
If you need to exclude the hostname, either append an '#' to the pattern ...
SELECT emp_id, email FROM employees WHERE regexp_like(email, last_name||'#', 'i');
2 tcook#apple.com
3 Ellison#oracle.com
... or extract the hostname from the email:
SELECT emp_id, email
FROM employees
WHERE regexp_like(regexp_substr(email,'.*#'), last_name, 'i');

Resources