SQL order by with group by query - sql

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;

Related

Selecting month in each year with maximum number of projects

I have the following scenario
img
For each year I would like to display the month with the highest number of projects that have ended
I have tried the following so far:
SELECT COUNT(proj.projno) nr_proj, extract(month from proj.end_date) month
, extract(year from proj.end_date) year
FROM PROJ
GROUP BY extract(month from proj.end_date)
,extract(year from proj.end_date)
I am getting the information about the number of projects per month, per year.
Could any one give me hints how for each of the years I would select only the records with the highest count of projects?
You can use this solution using max analytic function to get max nr_proj value per year (partition by clause), then keep only rows where nr_proj = mx.
select t.nr_proj, t.month, t.year
from (
SELECT COUNT(proj.projno) nr_proj
, extract(month from proj.end_date) month
, extract(year from proj.end_date) year
, max( COUNT(proj.projno) ) over(partition by extract(year from proj.end_date)) mx
FROM PROJ
GROUP BY extract(month from proj.end_date), extract(year from proj.end_date)
) t
where nr_proj = mx
;
demo
I think the following will give you what you are after (if I understood the requirements). It fist counts the projects for each month then ranks the months by year, finally it selects the first rank.
select dt "Most Projects Month", cnt "Monthly Projects"
from ( -- Rank month Count by Year
select to_char( dt, 'yyyy-mm') dt
, cnt
, rank() over (partition by extract(year from dt)
order by cnt desc) rnk
from (-- count number of in month projects for each year
select trunc(end_date,'mon') dt, count(*) cnt
from projects
group by trunc(end_date,'mon')
)
)
where rnk = 1
order by dt;
NOTE: Not tested, no data supplied. In future do not post images, see Why No Images.

Determine which year-month has the highest and lowest value [duplicate]

This question already has answers here:
Oracle SELECT TOP 10 records [duplicate]
(6 answers)
Oracle SQL - How to Retrieve highest 5 values of a column [duplicate]
(5 answers)
How do I limit the number of rows returned by an Oracle query after ordering?
(14 answers)
Closed 1 year ago.
Here's my first query to shows the number of customers added per year-month
select count(name) AS CUSTOMER,
extract(year from create_date) as yr,
extract(month from create_date) as mon
from x
group by extract(year from create_date),
extract(month from create_date)
order by yr desc, mon desc;
CUSTOMER
YR
MON
3
2019
07
4
2015
02
100
2014
09
3
2014
04
I tried the query
SELECT MAX(count(*))
FROM x
GROUP BY create_date;
in the results I have;
MAX(COUNT(*))
100
need to see the year and month in the result.
How to do this?
The way I understood the question, you'd use rank analytic function in a subquery (or a CTE) and fetch rows whose count is either minimum or maximum. Something like this:
with temp as
(select to_char(create_date, 'yyyymm') yyyy_mm,
count(*) cnt,
--
rank() over (order by count(*) asc) rnk_min,
rank() over (order by count(*) desc) rnk_max
from x
group by to_char(create_date, 'yyyymm')
)
select yyyy_mm,
cnt
from temp
where rnk_min = 1
or rnk_max = 1;
You can use two levels of aggregation and put the results all in one row using keep (which implements a "first" aggregation function):
select max(num_customers) as max_num_customers,
max(yyyymm) keep (dense_rank first order by num_customers desc) as max_yyyymm,
min(num_customers) as max_num_customers,
max(yyyymm) keep (dense_rank first order by num_customers asc) as in_yyyymm,
from (select to_char(create_date, 'YYYY-MM') as yyyymm,
count(*) AS num_customers
from x
group by to_char(create_date, 'YYYY-MM'
) ym
From Oracle 12, you can use FETCH FIRST ROW ONLY to get the row with the highest number of customers (and, in the case of ties, the latest date):
SELECT count(name) AS CUSTOMER,
extract(year from create_date) as yr,
extract(month from create_date) as mon
FROM x
GROUP BY
extract(year from create_date),
extract(month from create_date)
ORDER BY
customer DESC,
yr DESC,
mon DESC
FETCH FIRST ROW ONLY;
If you want to include ties for the highest number of customers then:
SELECT count(name) AS CUSTOMER,
extract(year from create_date) as yr,
extract(month from create_date) as mon
FROM x
GROUP BY
extract(year from create_date),
extract(month from create_date)
ORDER BY
customer DESC
FETCH FIRST ROW WITH TIES;

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;

How to write query for the cumulative salary for each employee within each year

This table has columns employee, month, year and salary.
I need to write sql query for this.
It should reset when we reach to December and restart calculating running sal for next year for the same employee.
As per now I am manage to write below Query but it is not enough:
SELECT Employee, Month, Year, Salary, SUM(Salary) OVER(ORDER BY rownum) AS "CUM SALARY"
FROM emp
group by Employee, Month, Year, Salary,rownum;
I don't know which database you are using, but this will work in Oracle database
SELECT Employee, Month, Year, Salary, SUM(Salary) OVER(partition by Employee, Year ORDER BY to_date(Year||Month, 'YYYYMon', 'nls_date_language = english')) AS "CUM SALARY"
FROM emp
;
You can use the sum analytical function as follows:
select t.*,
sum(salary) over (partition by empployee, year
order by case month when 'Jan' then 1
when 'Feb' then 2
.....
when 'Dec' then 12
end) as cum_sal
from employee t
Or you can use try_cast as follows:
select t.*,
sum(salary) over (partition by empployee, year
order by TRY_CAST (concat(month,' ', year)) as cum_sal
from employee t
Sample Query
WITH CTE
AS(
SELECT
EmpName,
Sal_Date,
Salary,
(ROW_NUMBER() over (PARTITION BY YEAR(Sal_Date) order by EmpName,Sal_Date asc) ) ROWNO
FROM tbl_EmpTB
GROUP BY EmpName,Sal_Date, Salary
)
SELECT EmpName,Datename(MONTH,sal_date) [Month],YEAR(sal_date) [YEAR],Salary,--CUMSAL,
SUM (Salary) OVER (PARTITION BY YEAR(Sal_Date) order by EmpName,Sal_Date asc) [CUM SALARY]
FROM CTE
ORDER BY Sal_Date

ORACLE 12c - "not a single-group group function"

I have a table o employees that contains names, date of employment and some more information.
I want to check which year the most employees were employed.
I write a query which count employment for each year:
SELECT EXTRACT (YEAR FROM e1.empl_date) AS YEAR, COUNT(e1.id_empl) AS EMPL_NUMBER
FROM employees e1
GROUP BY EXTRACT (YEAR FROM e1.empl_date);
And result of this query are tuples:
YEAR | EMPL_NUMBER
1993 | 3
1997 | 2
and so on...
And now I want to get max of EMPL_NUMBER:
SELECT YEAR, MAX(EMPL_NUMBER)
FROM (SELECT EXTRACT (YEAR FROM e1.empl_date) AS YEAR, COUNT(e1.id_empl) AS EMPL_NUMBER
FROM employees e1
GROUP BY EXTRACT (YEAR FROM e1.empl_date));
And then I get an error:
ORA-00937: not a single-group group function
I don't understand why I get an error because subquery returns tuple with 2 columns.
You are using an aggregation function on the select result so If you need all the distinct YEAR you ust group by
SELECT T.YEAR, MAX(T.EMPL_NUMBER)
FROM (
SELECT EXTRACT (YEAR FROM e1.empl_date) AS YEAR, COUNT(e1.id_empl) AS EMPL_NUMBER
FROM employees e1
GROUP BY EXTRACT (YEAR FROM e1.empl_date)
) T
GROUP BY T.YEAR ;
Otherwise if you need the year of the MAX(EMPL_NUMBER) you could
SELECT T.YEAR, T.EMPL_NUMBER
FROM (
SELECT EXTRACT (YEAR FROM e1.empl_date) AS YEAR, COUNT(e1.id_empl) AS EMPL_NUMBER
FROM employees e1
GROUP BY EXTRACT (YEAR FROM e1.empl_date)
) T
WHERE (T.EMPL_NUMBER) IN (SELECT MAX(EMPL_NUMBER)
FROM (
SELECT EXTRACT (YEAR FROM e1.empl_date) AS YEAR, COUNT(e1.id_empl) AS EMPL_NUMBER
FROM employees e1
GROUP BY EXTRACT (YEAR FROM e1.empl_date)
) T1 )
In Oracle 12C, you can do:
SELECT EXTRACT(YEAR FROM e1.empl_date) AS YEAR, COUNT(e1.id_empl) AS EMPL_NUMBER
FROM employees e1
GROUP BY EXTRACT(YEAR FROM e1.empl_date)
ORDER BY COUNT(e1.id_empl) DESC
FETCH FIRST 1 ROW ONLY;
One way to do this is to use an aggregate query as you were doing already, and then to use aggregate functions to their full extent. For example, using the FIRST/LAST function (and using the SCOTT schema, EMP table for illustration):
select min(extract(year from hiredate)) keep (dense_rank last order by count(empno)) as yr,
max(count(empno)) as emp_count
from emp
group by extract(year from hiredate)
;
YR EMP_COUNT
---- ---------
1981 10
There are two problems with this solution. First, many developers (including many experienced ones) seem unaware of the FIRST/LAST function, or otherwise unwilling to use it. The other, more serious problem is that in this problem it is possible that there are several years with the same, highest number of hires. The problem requirement must be more detailed than in the Original Post. What is the desired output when there are ties for first place?
The query above returns the earliest of all the different years when the max hires were achieved. Change MIN in the SELECT clause to MAX and you will get the most recent year when the highest number of hires happened. However, often we want a query that, in the case of ties, will return all the years tied for most hires. That cannot be done with the FIRST/LAST function.
For that, a compact solution would add an analytic function to your original query, to rank the years by number of hires. Then in an outer query just filter for the rows where rank = 1.
select yr, emp_count
from (
select extract(year from hiredate) as yr, count(empno) as emp_count,
rank() over (order by count(empno) desc) as rnk
from emp
group by extract(year from hiredate)
)
where rnk = 1
;
Or, using the max() analytic function in the SELECT clause of the subquery (instead of a rank-type analytic function):
select yr, emp_count
from (
select extract(year from hiredate) as yr, count(empno) as emp_count,
max(count(empno)) over () as max_count
from emp
group by extract(year from hiredate)
)
where emp_count = max_count
;
I assume you want a single row showing the year most people where hired:
SELECT * FROM (
SELECT EXTRACT (YEAR FROM e1.empl_date) AS YEAR,
COUNT(e1.id_empl) AS EMPL_NUMBER
FROM employees e1
GROUP BY EXTRACT (YEAR FROM e1.empl_date)
ORDER BY COUNT(*))
WHERE ROWNUM=1;