How to calculate Standard Deviation in Oracle SQL Developer? - sql

I have a table employees,
CREATE TABLE employees (
employeeid NUMERIC(9) NOT NULL,
firstname VARCHAR(10),
lastname VARCHAR(20),
deptcode CHAR(5),
salary NUMERIC(9, 2),
PRIMARY KEY(employeeid)
);
and I want to calculate Standard Deviation for salary.
This is the code I am using:
select avg(salary) as mean
,sqrt(sum((salary-avg(salary))*(salary-avg(salary)))/count(employeeid)) as SD
from employees
group by employeeid;
I am getting this error:
ORA-00979: not a GROUP BY expression
00979. 00000 - "not a GROUP BY expression"
*Cause:
*Action:
Error at Line: 260 Column: 12
Line 260 Column 12 is avg(salary)
How can I sort this out?

Oracle has a built-in function to calculate standard deviation: STDDEV.
The usage is as you'd expect for any aggregate function.
select stddev(salary)
from employees;

I'd just use the stddev function
SELECT avg(salary) as mean,
stddev(salary) as sd
FROM employees
It doesn't make sense to group by employeeid since that is, presumably unique. It doesn't make sense to talk about the average salary by employee, you want the average salary across all employees (or all departments or some other aggregatable unit)

The salary-avg(salary) can't be evaluated; avg(salary) is not available during execution of the query but only after all records are retrieved.
I would suggest to add AVG calculations in a subquery and JOIN it to the main one
select avg(salary) as mean,
sqrt(sum((salary-avg_res.avg)*(salary-avg_res.avg))/count(employeeid)) as SD
from employees JOIN
(select employeeid,avg(salary) as avg
from employees
group by employeeid) avg_res ON employees.employeeid=avg_res.employeeid
group by employeeid;

I thought you had to include the column in the GROUP BY in the SELECT:
select employeeid
,avg(salary) as mean
,sqrt(sum((salary-avg(salary))*(salary-avg(salary)))/count(employeeid)) as SD
from employees
group by employeeid;
But on further reflection the query doesn't make much sense unless it's historical data. An employee id ought to be unique to a single employee. Unless this is an average over time there should be only one salary per employee. Your mean will be the salary and the standard deviation will be zero.
A better query might be average of all salaries. In that case, remove the GROUP BY.
One more nitpick: the formula you're using is more properly called the population standard deviation. The sample deviation divides by (n-1).

Related

How to calculate Quantile 95 in Oracle SQL

How to calculate Quantile 95 value, using SQL or PL/SQL?
With the SQL aggregate or analytic function PERCENTILE_DISC() (or perhaps PERCENTILE_CONT()). Google for your version of Oracle and the function name to find the Oracle documentation for these functions.
Here is how I can find the 95th percentile salary in the HR.EMPLOYEES table (HR is the standard HR schema that is installed with many Oracle databases):
select percentile_disc(0.95) within group (order by salary) as sal_95th_pctile
from hr.employees
;
SAL_95TH_PCTILE
---------------
13000
If instead I wanted to find the 95th percentile salary in each department, I would use the analytic version:
select percentile_disc(0.95) within group (order by salary)
over (partition by department_id) as sal_95th_pctile
from hr.employees
;
For the HR.EMPLOYEES table this makes little sense, since each department has only a few employees, so "95th percentile" is meaningless; but that's how you would do it when all "departments" had many values from which to compute the 95th percentile.

Using ROUND, AVG and COUNT in the same SQL query

I need to write a query where I need to first count the people working in a department, then calculate the average people working in a department and finally round it to only one decimal place. I tried so many different variations.
That's what I got so far although it's not the first one I tried but I always get the same error message. (ORA-00979 - not a group by expression)
SELECT department_id,
ROUND(AVG(c.cnumber),1)
FROM employees c
WHERE c.cnumber =
(SELECT COUNT(c.employee_id)
FROM employees c)
GROUP BY department_id;
I really don't know what do to at this point and would appreciate any help.
Employees Table:
Try this (Oracle syntax) example from your description:
with department_count as (
SELECT department_id, COUNT(c.employee_id) as employee_count
FROM employees c
group by department_id
)
SELECT department_id,
ROUND(AVG(c.employee_count),1)
FROM department_count c
GROUP BY department_id;
But this query not make sense. Count is integer, and count return one number for one department in this case AVG return the same value as count.
Maybe you have calculate number of employee and averange of salary on department?

Select department id, seniority based on hire_time and the ealierst date of hire date

i have this task:
Select department id, the longest time of working based on months and time of the person who was hired as the first one
I wrote sthg like this:
SELECT department_id, min(hire_date) as earliest_hire_date, sysdate-hire_date dni
FROM employees
it generates the following error:
*ORA-00937: not a single-group group function
00937. 00000 - "not a single-group group function"*
i use oracle. Do you have any idea how to fix it?
You need to find a record that meets a certain criteria (which generally means you are going to need a WHERE clause). The criteria for the WHERE clause is the record with the min(hire_date). So:
SELECT department_id, hire_date, sysdate-hire_date
FROM employees
WHERE hire_date = (SELECT min(hire_date) FROM employees);
Or something along those lines. IF this returns more than one record, you could probably just toss a GROUP BY or DISTINCT in there. If that doesn't squash the multiples then you'll have to implement further logic to pick the winner (in the even that multiple departments share the same "earliest hiring date").
You could also do something similar to what you were doing (Aggregating at min(hire_date)) and then order by that field picking out the top most record.
As for the error you were facing in your attempt, when you use an Aggregate function like sum(), avg(), min() and similar, then fields that are not being aggregated must be in the GROUP BY clause, which was absent in your query.
You would fix your problem by using min() in the select:
select department_id, min(hire_date) as earliest_hire_date,
sysdate - min(hire_date) as dni
from employees
group by department_id;
I'm not sure if this does what you really need -- your question doesn't have sample data, desired results, or a clear explanation of what you want. On the other hand, it does fix the syntax error.

SQL Average calculation

Let's say there is an employer entity and it has an attribute of salary. And employer works in a department which is located in Oxford Building.(Building address is unique for building)
How can I write a SQL query that computes the average salary per building? Does it mean that salary is foreign key or what? Is it correct, if I do just
SELECT AVG(Salary) AS Averagesalary FROM Employee;
but in that case there will be no Building
That's why there's GROUP BY, to specify how you want to "group" your records. Right now you're doing an average of ALL records in table.
SELECT Building, AVG(SALARY)
FROM Employee
GROUP BY Building
The above query will also average all records, but do it per-building.

Using aggregate function for seprate rows

There is a table with name jobs where for different department number salary is giving. Same department can have more than one job so the salary may vary. Now i want to solve this query:
"Find the average salaries for each department without displaying the respective department numbers."
Here i should use avg but how to use it so that i can get my result of each department no separately?
I think what you're looking for is the GROUP BY clause. If I understand your question correctly then something like this should do the trick.
SELECT AVG(salary) FROM table_name GROUP BY department