Not a single-group group function, with aggregate functions in select - sql

I have the following query:
SELECT
COUNT(*) AS "Number of Employees",
EXTRACT(MONTH FROM hire_date) AS "Month Hired",
AVG(salary)
FROM
hr.employees;
It gives me the error: ORA-00937: not a single-group group function. I have tried other resources and answers from other people, but they don't work in my case.
How do I fix this?

Add a GROUP BY:
SELECT COUNT(*) AS "Number of Employees",
EXTRACT(MONTH FROM hire_date) AS "Month Hired",
AVG(salary)
FROM hr.employees
GROUP BY EXTRACT(MONTH FROM hire_date);
There may be reasons why you are only interested in the hire month. However, normally, you would also want the year. In Oracle, this would commonly be accomplished using TO_CHAR():
SELECT TO_CHAR(hire_date, 'YYYY-MM') as month_hired,
COUNT(*) AS num_empoyees,
AVG(salary)
FROM hr.employees
GROUP BY TO_CHAR(hire_date, 'YYYY-MM')
ORDER BY month_hired;

Related

I am trying to filter an alias column, which I know it wont work in the WHERE clause. I need to filter the YEARS >15

select department_id, last_name, round(((sysdate-hire_date)/30)/12, 0) as YEARS
from employees
where YEARS >15;
You can't refer to the alias in the WHERE clause. One option is to simply repeat the entire round expression:
SELECT department_id, last_name,
ROUND(((SYSDATE - hire_date) / 30) / 12, 0) AS YEARS
FROM employees
WHERE ROUND(((SYSDATE - hire_date) / 30) / 12, 0) > 15;
One option is to repeat the round expression and also you can use DATEDIFF which is another option.
SELECT department_id, last_name,
ROUND(((SYSDATE - hire_date) / 30) / 12, 0) AS YEARS
FROM employees
Where DATEDIFF(year, hire_date, getdate()) > 15
Thank you guys, I came up with a resolution this morning
select department_id, sum(salary) as "Summary"
from employees
where round(((sysdate-hire_date)/30)/12, 0)>15
group by department_id
order by department_id;
this is the right code
select department_id, sum(salary) as "Summary"
from employees
where round(((sysdate-hire_date)/30)/12, 0)>15
group by department_id
order by department_id;

Running total count with group by; SQL

I have a table that looks like this:
I want to group it by year and create a running total of number of empolyees in every year:
2001 1
2002 8
2003 12
I got to the part of grouping and counting in every year, however I don't know how to create running total with it. Tried adding over() etc, but I still have errors.
SELECT EXTRACT(year FROM HIRE_DATE) "Year",
COUNT(EMPLOYEE_ID)
FROM hr.Employees
GROUP BY EXTRACT(year FROM HIRE_DATE)
ORDER BY "Year" DESC;
Use window functions:
SELECT EXTRACT(year FROM HIRE_DATE) "Year", COUNT(*),
SUM(COUNT(*)) OVER (ORDER BY MIN(HIRE_DATE)) as running_count
FROM hr.Employees
GROUP BY EXTRACT(year FROM HIRE_DATE)
ORDER BY "Year" DESC;

SQL order by with group by query

I have an EMPLOYEES table :
employee_id(1) hire_date(15-2-2001)
employee_id(2) hire_date(2-2-1999)
employee_id(3) hire_date(11-2-2003)
employee_id(4) hire_date(6-7-2001)
I want to display the YEAR with the highest number of employees hired in, with the number of employees hired each month.
I tried this :
select extract (year from hire_date)
from employees
where max(count(employee_id))=count(employee_id)
order by extract (year from hire_date);
and I keep getting an "ORA-00934: group function is not allowed here"
What am I doing wrong?
I'm using ORACLE 10g Express.
The idea is that you can use aggregation and window functions to get the totals by month and year. Then you can choose the largest using row_number() or dense_rank().
select ym.*
from (select ym.*, dense_rank() over (order by year_cnt, year) as seqnum
from (select extract(year from hire_date) as yyyy,
extract(month from hire_date) as mm,
count(*) as cnt,
sum(count(*)) over (partition by extract(year from hire_date)) as year_cnt
from employees
group by extract(year from hire_date),
extract(month from hire_date)
) ym
) ym
where seqnum = 1
order by yyyy, mm;
Hmmmm, you can do this without so many subqueries:
with ym as (
select extract(year from hire_date) as yyyy
extract(month from hire_date) as mm,
count(*) as cnt,
sum(count(*)) over (partition by extract(year from hire_date)) as yearcnt
from employees
group by extract(year from hire_date), extract(month from hire_date)
)
select *
from ym
where yearcnt = (select max(yearcnt) from ym)
order by yyyy, mm;
Of course, this returns multiple years if two years have the same maximum value.
With PL/SQL I found this :
declare
recuperation float;
CURSOR newRequest(recuperationDonnes float) is select count(employee_id) as nombreEmployes,
extract(month from hire_date) as mois from employees where
extract(year from hire_date) = (recuperationDonnes) group by extract(month from hire_date);
a float;
a1 float;
begin
select extract (year from hire_date) as annee into recuperation from employees having count
(employee_id) >= all (select count (employee_id) as emp from employees group by extract(year
from hire_date)) group by extract(year from hire_date);
OPEN newRequest(recuperation);
LOOP
FETCH newRequest into a,a1;
Exit when newRequest%NotFound;
dbms_output.put_Line('Year: '||recuperation||' mois: '||a1||' nombreEmployes: '||a);
END LOOP ;
CLOSE newRequest;
end;

Combining multiple SELECT queries in one

As the title says the query needs to combine multiple select queries. The question is as follows:
Display the total number of employees, and of that total the number of employees hired in 1995,1996,1997,1998.
My query:
select (select count(*) from employees) as "Total",
(select count(*) from employees where hire_date between 'JAN-1-0095' and 'DEC-1-0095')as "1995",
(select count(*) from employees where hire_date between 'JAN-1-0096' and 'DEC-1-0096') as "1996",
(select count(*) from employees where hire_date between 'JAN-1-0097' and 'DEC-1-0097') as "1997",
(select count(*) from employees where hire_date between 'JAN-1-0098' and 'DEC-1-0098') as "1998"
from employees
but the issue is instead of returning only single record this query is being executed for all the records in the table and hence producing the following output:
You can use conditional counting:
select count(*) as total_count,
count(case when extract(year from hire_date) = 1995 then 1 end) as "1995",
count(case when extract(year from hire_date) = 1996 then 1 end) as "1996",
count(case when extract(year from hire_date) = 1997 then 1 end) as "1997",
count(case when extract(year from hire_date) = 1998 then 1 end) as "1997",
from employees;
this makes use of the fact that aggregate functions ignore NULL values and therefor the count() will only count those rows where the case expressions returns a non-null value.
Your query returns one row for each row in the employees table because you do not apply any grouping. Each select is a scalar sub-select that gets executed for each and every row in the employees table.
You could make it only return a single row if you replace the final from employees with from dual - but you'd still count over all rows within each sub-select.
You should also avoid implicit data type conversion like you did. 'JAN-1-0095' is a string and will implicitly be converted to a date depending on your NLS settings. Your query would not run if executed from my computer (because of different NLS settings).
As you are looking for a complete year, just comparing the year is a bit shorter to write and easier to understand (at least in my eyes).
Another option would be to use proper date literals, e.g. where hire_date between DATE '1995-01-01' and DATE '1995-12-31' or a bit more verbose using Oracle's to_date() function: where hire_date between to_date('1995-01-01', 'yyyy-mm-dd') and to_date('1995-12-31', 'yyyy-mm-dd')
Assuming the years are really what you want, the problem with your query is that you are selecting from employees, so you get a row for each one. You could use:
select (select count(*) from employees) as "Total",
(select count(*) from employees where hire_date between 'JAN-1-0095' and 'DEC-1-0095')as "1995",
(select count(*) from employees where hire_date between 'JAN-1-0096' and 'DEC-1-0096') as "1996",
(select count(*) from employees where hire_date between 'JAN-1-0097' and 'DEC-1-0097') as "1997",
(select count(*) from employees where hire_date between 'JAN-1-0098' and 'DEC-1-0098') as "1998"
from dual;
And I would use date '1998-01-01' for the date constants.
However, I prefer #a_horse_with_no_name's solution.
You should avoid using a lot of subqueries. You should try this:
SQL Server:
SELECT count(*) as Total, hire_date
FROM employees
WHERE year(hire_date) IN ('1995','1996','1997','1998')
GROUP BY hire_date WITH CUBE
In ORACLE
SELECT count(*) as Total, hire_date
FROM employees
WHERE extract(year from hire_date) IN ('1995','1996','1997','1998')
GROUP BY CUBE (hire_date)
In addition to the subtotals generated by the GROUP BY, the CUBE extension will generate subtotals for each hire_date.

order by month oracle

I'm trying to make a simple query to find the average car sales per month from a table called "salestransaction".
My code:
select to_char(st.dateofsale, 'MON') as mnth, count(*) / 2 as Average_Car_Sales
from salestransaction st
group by to_char(st.dateofsale, 'MON')
order by to_char(st.dateofsale, 'MON');
My order by month is not outputting the correct order.
What can I do to make it output by month? starting from JAN - DEC?
Thank you.
An easy approach is to add month number to group by, then order by this field:
select to_char(st.dateofsale, 'MON') as mnth,
count(*) / 2 as Average_Car_Sales
from salestransaction st
group by EXTRACT(month FROM st.dateofsale),
to_char(st.dateofsale, 'MON')
order by EXTRACT(month FROM st.dateofsale);
If you try to order without aggregate function or without adding month number in group by expression then you will get ORA-00979: not a GROUP BY expression error
To avoid Extract you can use to_char with MM pattern:
select to_char(st.dateofsale, 'MON') as mnth,
count(*) / 2 as Average_Car_Sales
from salestransaction st
group by to_char(st.dateofsale, 'MM'),
to_char(st.dateofsale, 'MON')
order by to_char(st.dateofsale, 'MM');
My recommendation:
order by min(st.dateofsale);
That is, just pull a value out for each group and use that for the ordering.
If you have data from multiple years, the above might not work. Instead:
order by min(extract(month from st.dateofsale))
It is because you order them according to the month name in this case, december comes before january. You need to get their numeric values.
Here is an example from oracle documantation:
SELECT EXTRACT(month FROM order_date) "Month",
COUNT(order_date) "No. of Orders"
FROM orders
GROUP BY EXTRACT(month FROM order_date)
ORDER BY EXTRACT(month FROM order_date)
You can check it from this link for more detailed information:
http://docs.oracle.com/cd/B19306_01/server.102/b14200/functions050.htm