Pivoting in sql [duplicate] - sql

This question already has answers here:
SQL Server: Examples of PIVOTing String data
(7 answers)
Closed 7 years ago.
i have sql table with data as given below
emp id empname dept
------- ---- ------
1 a Hr
2 b Hr
3 c Tech
4 d Hr
5 e Admin
7 f Tech
8 g Admin
Now i want to pivot the above table get the result like this
Hr Tech Admin
----- ----- -----
Empname a c e
Empname b f g
Empname d
I am just wondering is this even possible using pivoting in sql or there is any other mean to achieve this

You can use conditional aggregation with ROW_NUMBER:
SQL Fiddle
WITH Cte AS(
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY dept ORDER BY empid)
FROM tbl
)
SELECT
[Hr] = MAX(CASE WHEN dept = 'Hr' THEN empname END),
[Tech] = MAX(CASE WHEN dept = 'Tech' THEN empname END),
[Admin] = MAX(CASE WHEN dept = 'Admin' THEN empname END)
FROM cte
GROUP BY rn
Here is the PIVOT version:
SQL Fiddle
;WITH Cte AS(
SELECT *,
RN = ROW_NUMBER() OVER(PARTITION BY dept ORDER BY empid)
FROM tbl
)
SELECT *
FROM(
SELECT
dept, MAX(empname) AS empname,RN
FROM Cte
GROUP BY dept, RN
)t
PIVOT
(
MAX(empname)
FOR dept in ([Hr], [Tech], [Admin])
)piv

This type of manipulation is often best done in the presentation layer. It appears that you want three different lists in the columns, so the rows are not really related to each other.
That said, you can do this in SQL, but you need to get a "list position" for each column. For that, use row_number():
select h.empname as hr, t.empname as tech, a.empname as admin
from (select empname, row_number() over (order by empname) as seqnum
from table
where dept = 'Hr'
) h full outer join
(select empname, row_number() over (order by empname) as seqnum
from table
where dept = 'Tech'
) t
on t.seqnum = h.seqnum full outer join
(select empname, row_number() over (order by empname) as seqnum
from table
where dept = 'Admin'
) a
on a.seqnum in (h.seqnum, t.seqnum)

Related

Select only one employee from every department

I want one employee from every department (EmpDepartment), for example in my table there are:
3 employees with EmpDepartment 1
2 employees with EmpDepartment 2 and
1 Employee with EmpDepartment 3
I want EmployeeId, EmployeeName and EmpDepartment of any one employee from each separate department.
Use a windowing function like this:
SELECT *
FROM (
SELECT
E.*,
ROW_NUMBER() OVER (PARTITION BY EmpDepartment) AS RN
FROM Employee
) X
WHERE X.RN = 1
You can add an order clause the the windowing function if you have a business rule that you want to use in picking the employee
eg
ROW_NUMBER() OVER (PARTITION BY EmpDepartment order by EmployeeId) AS RN
This will get a random employee from each department due to ordering by NEWID()...
SELECT * FROM
(
SELECT EmployeeID, EmployeeName, EmployeeEmail
, ROW_NUMBER() OVER (PARTITION BY EmpDepartment ORDER BY NEWID()) AS rn
FROM dbo.Employee
) x
WHERE x.rn = 1
You can change the order by clause to something else if you want to.
This will return the employee from each department having the minimum EmployeeID in that department (since it is not important which employee will be in the results):
SELECT e.* FROM Employee e
WHERE NOT EXISTS (
SELECT 1 FROM Employee
WHERE EmpDepartment = e.EmpDepartment AND EmployeeID < e.EmployeeID
)
SELECT Top(1) EmployeeID, EmployeeName, EMPDeptartment FROM Employee WHERE EmpDetpartment = 1
UNION
SELECT Top(1) EmployeeID, EmployeeName, EMPDeptartment FROM Employee WHERE EmpDetpartment = 2
UNION
SELECT Top(1) EmployeeID, EmployeeName, EMPDeptartment FROM Employee WHERE EmpDetpartment = 3
You can either use rownumber to find any employee of a particular dept change rn to any value as 1,2,...etc
Select department, employee
from (
Select department, employee,
row_number() over (partition by department order by employee) rn
)
where rn =1;
or use simple group by
Select department, max(employee)
from table
group by department

Finding the max after Counting with Specific Conditions

I am trying to find the department with the maximum amount of null assigned.
Here is the table Class:
Dept Assigned
CSCE
CSCE
ELEG 4
ELEG
MATH
ELEG
So since CSCE and ELEG have the largest amount of null assigned, I want to output.
Dept Max(Count)
CSCE 2
ELEG 2
Here is what I have:
Select Dept, Max(Countt)
from (Select Dept, Count(Dept) as Countt
from Class
where assigned is null group by Dept
);
However, it is outputting the count for all the Dept including Math. How can I fix that?
I am using Oracle.
You can use window functions for this:
select d.Dept, cnt
from (select c.Dept, Count(*) as cnt,
rank() over (order by count(*) desc) as seqnum
from Class c
where c.assigned is null
group by c.Dept
) d
where seqnum = 1;
One other way:
select count(*), dept from table
where assigned is null
group by dept
having count(*) = (select max(c) from (select count(*) as c from table
where assigned is null
group by dept) as t)

oracle display customer who purchased most cars WITHOUT analytic functions

I'm currently trying to answer the following question:
Display the name of the customer who has purchased the most cars from Archie’s Luxury Motors.
Tables I'm working with:
Customer
(custID, name, DOB, streetAddress, suburb, postcode,
gender, phoneNo, email, type)
SalesTransaction
(VIN, custID, agentID, dateOfSale, agreedPrice)
My query
select *
from (
select customer.name
from customer, salestransaction
where customer.custID = salestransaction.custID
group by (customer.name), salestransaction.custID
order by count(*) desc
)
where rownum=1;
Now I've found out that I cannot use analytic functions (rownum & rank)
How would I go about doing this with using pure transactional SQL only?
You could use MAX and COUNT aggregate functions:
WITH data AS
(SELECT c.name cust_nm,
COUNT(*) cnt
FROM customer c,
salestransaction s
WHERE c.custID = s.custID
GROUP BY c.NAME
ORDER BY cnt DESC
)
SELECT cust_nm FROM data WHERE cnt =
(SELECT MAX(cnt) FROM DATA
);
An example from EMP and DEPT table:
SQL> WITH data AS
2 (SELECT e.deptno,
3 COUNT(*) cnt
4 FROM emp e,
5 dept d
6 WHERE e.deptno = d.deptno
7 GROUP BY e.deptno
8 ORDER BY cnt DESC
9 )
10 SELECT deptno FROM DATA WHERE cnt =
11 (SELECT MAX(cnt) FROM DATA
12 );
DEPTNO
----------
30
SQL>

Finding department having maximum number of employee

I have a table employee
id name dept
1 bucky shp
2 name shp
3 other mrk
How can i get the name of the department(s) having maximum number of employees ? ..
I need result
dept
--------
shp
SELECT cnt,deptno FROM (
SELECT rank() OVER (ORDER BY cnt desc) AS rnk,cnt,deptno from
(SELECT COUNT(*) cnt, DEPTNO FROM EMP
GROUP BY deptno))
WHERE rnk = 1;
Assuming you are using SQL Server and each record representing an employee. So you can use window function to get the result
WITH C AS (
SELECT RANK() OVER (ORDER BY dept) Rnk
,name
,dept
FROM table
)
SELECT TOP 1 dept FROM
(SELECT COUNT(Rnk) cnt, dept FROM C GROUP BY dept) t
ORDER BY cnt DESC
With common table expressions, count the number of rows per department, then find the biggest count, then use that to select the biggest department.
WITH depts(dept, size) AS (
SELECT dept, COUNT(*) FROM employee GROUP BY dept
), biggest(size) AS (
SELECT MAX(size) FROM depts
)
SELECT dept FROM depts, biggest WHERE depts.size = biggest.size
Based on one of the answer, Let me try to explain step by step
First of all we need to get the employee count department wise. So the firstly innermost query will run
select count(*) cnt, deptno from scott.emp group by deptno
This will give result as
Now out of this we have to get the one which is having max. employee i.e. department 30.
Also please note there are chances that 2 departments have same number of employees
The second level of query is
select rank() over (order by cnt desc) as rnk,cnt,deptno from
(
select count(*) cnt, deptno from scott.emp group by deptno
)
Now we have assigned ranking to each department
Now to select rank 1 out of it. we have a simplest outer query
select * from
(
select rank() over (order by cnt desc) as rnk,cnt,deptno from
(
select count(*) cnt, deptno from scott.emp group by deptno
)
)
where rnk=1
So we have the final result where we got the department which has the maximum employees. If we want the minimum one we have to include the department table as there are chances there is a department which has no employees which will not get listed in this table
You can ignore the scott in scott.emp as that is the table owner.
The above SQL can be practised at Practise SQL online

SQL Rank based on date

I am trying to link a customer to a "peferred" merchant based on number of visits within the last 18 months, with the tiebreaker being the most recent visit date. I'm having a bit of trouble with the tiebreaker. If there are two records both ranked 1 based on # of visits for a certain MemberID, I want to set the IsFirst bit column to 1 on the record with the MAX(EncounterDate) for that MemberID. How should I go about doing this?
This may help you... This is Oracle query based on existing emp table. I think it is a good idea to create structures when you posting a problem.
Replace first select with update etc...: UPDATE your table SET your date = max_date (max_hire_date in my example) WHERE your_field IN (select max date as in my example) AND rnk = 1 and rno = 1
SELECT * FROM
(
SELECT deptno
, ename
, sal
, RANK() OVER (PARTITION BY deptno ORDER BY sal desc) rnk
, ROW_NUMBER() OVER (PARTITION BY deptno ORDER BY sal desc) rno
, MAX(hiredate) OVER (PARTITION BY deptno ORDER BY deptno) max_hire_date
FROM emp_test
WHERE deptno = 20
ORDER BY deptno
)
WHERE rnk = 1
--AND rno = 1 -- or 2 or any other number...
/
SQL>
DEPTNO ENAME SAL RNK RNO HIREDATE MAX_HIRE_DATE
-----------------------------------------------------------
20 SCOTT 3000 1 1 1/28/2013 1/28/2013
20 FORD 3000 1 2 12/3/1981 1/28/2013
The following SQL gets the information you want, assuming the structure of certain tables:
select c.*, NumVisits, MaxVisitDate, MaxFirstVisitDate
(select count(*)
from visits v
where v.customerid = c.customerid and
v.visi
from customers c join
(select customerid,
sum(case when visitdate between getdate() - 365*1.5 and getdate()
then 1 else 0
end) as NumVisits,
max(visitdate) as MaxVisitDate,
max(case when IsFirst = 1 then visitdate end) as MaxFirstVisitDate
from visits
group by customerid
) v
on c.customerid = v.customerid
From this information, you can put together the logic for what you want to do. When MaxVisitDate = MaxFirstVisitDate, then the bit is set on the most recent date.
The answer to your update question is something like this:
update visits
set IsFirst = 1
where exists (select max(encounterDate)
from visits v2
where v2.customer_id = visits.customer_id
having max(encounterDate) = visits.encounterDate)
)