Dynamic grouping and hirearchical results in sql - sql

I need to produce Hierarchical summary in sql.
The user will send the columns to group by as parameters to the stored procedure ,so group by is dynamic
For eg. User will send to group records by DeptNo and then by Job columns
I need to produce the result as below
Final output
The individual query for group by deptno
Select d.DEPTNO,Count(EmpNo) EmpCount, Sum(Sal) Total from Emp e inner join Dept d
on e.DEPTNO=d.DEPTNO GROUP BY d.DEPTNO;
The query for group by DEPTNO,Job
select Deptno, Job, Count(EmpNo) EmpCount, Sum(Sal) Total from emp group by DEPTNO,Job order by deptno
DeptnobyJob
Currently the query i used to produce the result is as below
select Deptno, Job,
Count(EmpNo) EmpCount,
Sum(Sal) Total
from emp group by DEPTNO,Job with rollup order by Deptno, EmpCount desc
Is there any other better option to get summary results.
It is to display in mvc web and no ssrs

An alternate solution to GROUPING SETS and with Rollupis Dynamic SQL. Build up a query string and execute it using Execute. If anyone one wants to try, Do something like this:
declare #Query nvarchar(max) = 'select column1,column2,Column3, Count(*) from table1 group by ' + #param1 + ',' + #param2 + ',' + #param3;
Execute(#Query)
you can build a dynamic query according to your liking using your parmeters and then execute it to get the desired results. Be ware of SQL Injection Though!

Related

Write a query to display the job with the lowest average salary in the company

So basically the emp table has emp(job,sal....)
I have tried so many things including min(avg(sal)) but combining two aggregate functions is not possible.
trying something like SELECT min(x.avg) FROM (SELECT AVG(sal)as avg FROM emp group by job)x; it will only display the min(sal) but rather i want to display the job. Please help
You can use order by and limit. But critically, you need to select job!
SELECT job
FROM emp
GROUP BY job
ORDER BY AVG(sal)
LIMIT 1;
You only need to select 1 column from Gordon's answer -
SELECT job
FROM (SELECT job, AVG(sal)
FROM emp
GROUP BY job
ORDER BY AVG(sal)
LIMIT 1) T;

DB2 SQL Having Clause with a Nested Table Expression

Return the department number and the total payroll for the department that has the highest payroll. Payroll is the sum of all salaries and commissions of the department. Use a having clause with a nested table expression.
select e0.deptno,
(select sum(sal+com) FROM emp
group by deptno
having sum(sal+com) >= all(select sum(sal+com)
from emp
group by deptno) )as top
from emp as e0
group by e0.deptno
;
But my result is not correct. Im not so sure if my nested table expression combined with the having clause is done the right way. Can someone, try to help me? Thanks in advance.
As far as concerned, you don't need a having clause for this. You can just aggregate by department, order the results by payroll and fetch the first record:
select deptno, sum(sal + com) payroll
from emp e
group by deptno
order by payroll desc
fetch first 1 rows only
If you do want to use having (or if you are using a version of db2 that does not support the fetch ... rows ... clause), then we can build on your initial idea as follows:
select deptno, sum(sal + com) payroll
from emp
group by deptno
having sum(sal + com) >= all (
select sum(sal + com) from emp group by deptno
)

can't we use select another column with max() in sql

I have a table emp (ename,eid,did,sal,sex) where column did is foreign key with another table dept (did,dname).
I want to get the max sal of the company along with name and did of the person who is getting it.
I am executing following query
select did,ename ,max(sal) from emp;
But the Result is :
ORA-00937: not a single-group group function
so My question is can't I have more than 1 columns ?
You can't use MAX aggregate function like this. Using MAX without a GROUP BY clause will just return one record with the maximum sal value. You are not allowed to place any more non-aggregated fields in the SELECT clause.
If you want to get the record having the maximum sal value, then you have to do a self-join:
select e1.*
from emp as e1
inner join (
select max(sal) as max_sal
from emp
) as e2 on e1.sal = e2.max_sal
Note: The above query will return more than one records from table emp, in case more than one records share the same maximum sal value.
Edit:
If you want to get the maximum salary per department then you have to include a GROUP BY clause in the derived table used:
select e1.*
from emp as e1
inner join (
select did, max(sal) as max_sal
from emp
group by did
) as e2 on e1.did = e2.did and e1.sal = e2.max_sal
You would have to group the results by the sal column:
select did,ename,max(sal) from emp group by sal
MAX operates over the group specified in a GROUP BY clause and finds the maximum value for each group. When not specifying a set of columns to group by, it finds the max for the entire results set.
You need to find the max sal, then you can query the rows that match:
select did, ename, sal
from emp
where sal = (select max(sal) from emp)

Oracle Display the values of columns in single row without using connect by clause

This is my query
select deptno,ename from emp_task;
Output
I want the output like this
eno ename
20 TRINATH/RABHA
8 SAIKIRAN/KISHORE
10 KUMAR/VICKY/DAFNI
select deptno,
listagg(ename,'/') within group (order by ename) as names
from temp_task
group by deptno
order by deptno;
This sql query should work -
select deptno,wm_concat(ename)
from emp_task
group by deptno
order by deptno

SQL: aggregate function and group by

Consider the Oracle emp table. I'd like to get the employees with the top salary with department = 20 and job = clerk. Also assume that there is no "empno" column, and that the primary key involves a number of columns. You can do this with:
select * from scott.emp
where deptno = 20 and job = 'CLERK'
and sal = (select max(sal) from scott.emp
where deptno = 20 and job = 'CLERK')
This works, but I have to duplicate the test deptno = 20 and job = 'CLERK', which I would like to avoid. Is there a more elegant way to write this, maybe using a group by? BTW, if this matters, I am using Oracle.
The following is slightly over-engineered, but is a good SQL pattern for "top x" queries.
SELECT
*
FROM
scott.emp
WHERE
(deptno,job,sal) IN
(SELECT
deptno,
job,
max(sal)
FROM
scott.emp
WHERE
deptno = 20
and job = 'CLERK'
GROUP BY
deptno,
job
)
Also note that this will work in Oracle and Postgress (i think) but not MS SQL. For something similar in MS SQL see question SQL Query to get latest price
If I was certain of the targeted database I'd go with Mark Nold's solution, but if you ever want some dialect agnostic SQL*, try
SELECT *
FROM scott.emp e
WHERE e.deptno = 20
AND e.job = 'CLERK'
AND e.sal = (
SELECT MAX(e2.sal)
FROM scott.emp e2
WHERE e.deptno = e2.deptno
AND e.job = e2.job
)
*I believe this should work everywhere, but I don't have the environments to test it.
In Oracle I'd do it with an analytical function, so you'd only query the emp table once :
SELECT *
FROM (SELECT e.*, MAX (sal) OVER () AS max_sal
FROM scott.emp e
WHERE deptno = 20
AND job = 'CLERK')
WHERE sal = max_sal
It's simpler, easier to read and more efficient.
If you want to modify it to list list this information for all departments, then you'll need to use the "PARTITION BY" clause in OVER:
SELECT *
FROM (SELECT e.*, MAX (sal) OVER (PARTITION BY deptno) AS max_sal
FROM scott.emp e
WHERE job = 'CLERK')
WHERE sal = max_sal
ORDER BY deptno
That's great! I didn't know you could do a comparison of (x, y, z) with the result of a SELECT statement. This works great with Oracle.
As a side-note for other readers, the above query is missing a "=" after "(deptno,job,sal)". Maybe the Stack Overflow formatter ate it (?).
Again, thanks Mark.
In Oracle you can also use the EXISTS statement, which in some cases is faster.
For example...
SELECT name, number
FROM cust
WHERE cust IN
( SELECT cust_id FROM big_table )
AND entered > SYSDATE -1
would be slow.
but
SELECT name, number
FROM cust c
WHERE EXISTS
( SELECT cust_id FROM big_table WHERE cust_id=c.cust_id )
AND entered > SYSDATE -1
would be very fast with proper indexing. You can also use this with multiple parameters.
There are many solutions. You could also keep your original query layout by simply adding table aliases and joining on the column names, you would still only have DEPTNO = 20 and JOB = 'CLERK' in the query once.
SELECT
*
FROM
scott.emp emptbl
WHERE
emptbl.DEPTNO = 20
AND emptbl.JOB = 'CLERK'
AND emptbl.SAL =
(
select
max(salmax.SAL)
from
scott.emp salmax
where
salmax.DEPTNO = emptbl.DEPTNO
AND salmax.JOB = emptbl.JOB
)
It could also be noted that the key word "ALL" can be used for these types of queries which would allow you to remove the "MAX" function.
SELECT
*
FROM
scott.emp emptbl
WHERE
emptbl.DEPTNO = 20
AND emptbl.JOB = 'CLERK'
AND emptbl.SAL >= ALL
(
select
salmax.SAL
from
scott.emp salmax
where
salmax.DEPTNO = emptbl.DEPTNO
AND salmax.JOB = emptbl.JOB
)
I hope that helps and makes sense.