How to do pivoting on this layered data - sql

Hi I have sample data
declare #emp table(id int identity(1,1),E_Name varchar(20),E_company varchar(20),Emp_Val VARCHAR(10))
insert into #emp(E_Name,E_company,Emp_Val)VALUES('Rahim','WELLS','A')
insert into #emp(E_Name,E_company,Emp_Val)VALUES('Jag','collebra',NULL)
insert into #emp(E_Name,E_company,Emp_Val)VALUES('Vasu','nunet',NULL)
insert into #emp(E_Name,E_company,Emp_Val)VALUES('Kiran','crystal',NULL)
insert into #emp(E_Name,E_company,Emp_Val)VALUES('Sajan','tiato',NULL)
insert into #emp(E_Name,E_company,Emp_Val)VALUES('RAM','WELLS','A')
insert into #emp(E_Name,E_company,Emp_Val)VALUES('Srinu','Cognizant','B')
insert into #emp(E_Name,E_company,Emp_Val)VALUES('Raju','Cognizant','B')
Sample data :
id E_Name E_company Emp_Val
1 Rahim WELLS A
2 Jag collebra NULL
3 Vasu nunet NULL
4 Kiran crystal NULL
5 Sajan tiato NULL
6 RAM WELLS A
7 Srinu Cognizant B
8 Raju Cognizant B
script :
SELECT [WELLS],[Cognizant],[NULL] from (
select E_Name,E_company,Emp_Val from #emp)T
PIVOT (MAX(E_Name)FOR E_company IN([WELLS],[Cognizant],[NULL]))PVT
output :
WELLS Cognizant NULL
Rahim Srinu collebra
RAM Raju tiato
NULL Srinu crystal
NULL NULL NUNET

You can use conditional aggregation:
select max(case when e_company = 'WELLS' then e_name end) as wells,
max(case when e_company = 'Cognizant' then e_name end) as cognizant,
max(case when e_company not in ('WELLS', 'Cognizant') then e_name end) as nulls
from (select e.*,
row_number() over (partition by (case when e_company in ('WELLS', 'Cognizant') then e_company end) order by id) as seqnum
from #emp e
) e
group by seqnum
order by seqnum;
Here is a db<>fiddle.

your mistake is in the last select statement, it should be like this:
SELECT *
from (
select * from #emp)T
PIVOT (MAX(Emp_Val)FOR E_company IN([WELLS],[Cognizant],[NULL]))PVT
order by 1

This approach uses a self join within the pivot to enumerate the companies with multiple employes and values. It then uses a right join back on to the table to enumerate the companies that do not have those employees. The difference in the output is that all of the null permutations are preserved. Other than that this should cover what you are looking for.
declare #emp table(id int identity(1,1),E_Name varchar(20),E_company
varchar(20),Emp_Val VARCHAR(10))
insert into #emp(E_Name,E_company,Emp_Val)VALUES('Rahim','WELLS','A')
insert into #emp(E_Name,E_company,Emp_Val)VALUES('Jag','collebra',NULL)
insert into #emp(E_Name,E_company,Emp_Val)VALUES('Vasu','nunet',NULL)
insert into #emp(E_Name,E_company,Emp_Val)VALUES('Kiran','crystal',NULL)
insert into #emp(E_Name,E_company,Emp_Val)VALUES('Sajan','tiato',NULL)
insert into #emp(E_Name,E_company,Emp_Val)VALUES('RAM','WELLS','A')
insert into #emp(E_Name,E_company,Emp_Val)VALUES('Srinu','Cognizant','B')
insert into #emp(E_Name,E_company,Emp_Val)VALUES('Raju','Cognizant','B')
select distinct WELLS, Cognizant,case E_Company when 'Wells' then NULL when
'Cognizant' then null else E_Company end as [NULL] from
(
SELECT [WELLS],[Cognizant],[collebra], [nunet], [crystal], [tiato] from (
select e.E_Name,e2.E_name as E2_Name, e.E_company,e2.Emp_Val as Emp2_Val, e.Emp_Val
from #emp e inner join #emp e2 on e.id=e2.id)T
PIVOT (MAX(E_Name)FOR E_company IN([WELLS],[Cognizant],[collebra], [nunet],
[crystal], [tiato]))PVT) stagingtable
right join (select E_Company, E_Name from #emp) c on stagingtable.Cognizant=c.E_Name
or stagingtable.WELLS=c.E_Name
order by 1 desc, 2 desc, 3 desc;

Related

Not able to add row wise data in column form

EmpID (Primary Key) Sale Items Paid
ABC chair Yes
WXY chair Under Review
PER Laptop Yes
ABC Chair Yes
Now i want to create another table where i want to insert data Like below
Emp ID Chair Laptop
ABC 2 0
WXY 1 0
My query to insert is
Select Emp Id from EMP,count(sales_item) as chair where Sales_Item = 'chair'
it is working now how to add Laptop (3rd Column ) . can you please suggest
You would use conditional aggregation:
Select EmpId,
sum(case when sales_item = 'chair' then 1 else 0 end) as chairs,
sum(case when sales_item = 'laptop' then 1 else 0 end) as laptops
from EMP
group by EmpId;
There is no reason to store this in a separate table. If you like, you can create a view. Then when you access the view, you know the data is up-to-date.
You could use pivot for the expected result:
DECLARE #t TABLE(
EmpID varchar(3)
,SaleItems varchar(10)
,Paid varchar(20)
)
INSERT INTO #t VALUES
('ABC', 'chair', 'Yes')
,('WXY', 'chair', 'Under Review')
,('PER', 'Laptop', 'Yes')
,('ABC', 'Chair', 'Yes')
SELECT piv.EmpID, ISNULL(piv.chair, 0) AS chair, ISNULL(piv.Laptop, 0) AS Laptop
FROM(
SELECT EmpID, SaleItems, 1 cnt
FROM #t
) x
PIVOT
(
SUM(cnt)
FOR SaleItems IN ([chair], [Laptop])
) piv

Joining tables on ID and closest date

I have two tables, one showing when someone left and one showing when they came back (sometimes when they come back, they may forget to enter that they came back. I am tryint to join the tables so that they look like the desired table from the image.
You can try this.
DECLARE #TableA TABLE(ID INT, Leave DATE)
INSERT INTO #TableA VALUES
(62175, '11/29/2019'),
(62175, '11/11/2019'),
(62175, '3/29/2019'),
(62175, '8/22/2019'),
(68454, '11/29/2019'),
(68454, '12/13/2019')
DECLARE #TableB TABLE(ID INT, [Return] DATE)
INSERT INTO #TableB VALUES
(62175, '4/4/2019'),
(62175, '11/16/2019'),
(62175, '11/30/2019'),
(68454, '11/30/2019'),
(68454, '12/14/2019')
SELECT TA.*, CASE WHEN ROW_NUMBER()OVER(PARTITION BY X.ID, X.[Return] ORDER BY TA.Leave DESC) = 1 THEN X.[Return] ELSE NULL END [Return]
FROM #TableA TA
OUTER APPLY (SELECT TOP 1 * FROM #TableB TB
WHERE TA.ID = TB.ID
AND TB.[Return] > TA.Leave
ORDER BY TB.[Return] ) X
ORDER BY TA.ID, TA.Leave
Result:
ID Leave Return
----------- ---------- ----------
62175 2019-03-29 2019-04-04
62175 2019-08-22 NULL
62175 2019-11-11 2019-11-16
62175 2019-11-29 2019-11-30
68454 2019-11-29 2019-11-30
68454 2019-12-13 2019-12-14
These tables are invalid, they should be in one table with 3 columns. ID, Leave, Return
Very tricky question. I think this does what you want:
with ab as (
select id, leave, null as return
from a
union all
select id, null, return
from b
)
select distinct id, coalesce(leave, prev_leave), coalesce(return, next_return)
from (select ab.*,
(case when leave is null
then lag(leave) over (partition by id order by coalesce(leave, return))
end) as prev_leave,
(case when leave is null
then lead(leave) over (partition by id order by coalesce(leave, return))
end) as next_return
from ab
) ab

How to the result set form the below table

Table 1 contains certain set of data's. I need to get the following result set form the Table 1
Table1
Id Desc ParentId
1 Cloths 0
2 Mens 1
3 Womens 1
4 T-Shirt_M 2
5 Casual Shirts_M 2
6 T-Shirt_F 3
7 Education 8
If I pass a parameter as "Casual Shirts_M" I should get the below result set.
Result Set
Id Desc ParentId
1 Cloths 0
2 Mens 1
5 Casual Shirts_M 2
As mentioned in comments, there are plenty of Recursive Common Table Expressions examples for this, here's another one
DECLARE #Desc NVARCHAR(50) = 'Casual Shirts_M'
;WITH cteX
AS
( SELECT
B.Id, B.[DESC], B.ParentId
FROM
Table1 b
WHERE
B.[Desc] = #Desc
UNION ALL
SELECT
E.Id, E.[DESC], E.ParentId
FROM
Table1 E
INNER JOIN
cteX r ON e.Id = r.ParentId
)
SELECT * FROM cteX ORDER BY ID ASC
SQL-Fiddle provided by #WhatsThePoint
The question comes under the concept of Building hierarchy using Recursive CTE:
CREATE TABLE cloths
(
id INT,
descr VARCHAR(100),
parentid INT
);
insert into cloths values (1,'Cloths',0);
insert into cloths values (2,'Mens',1);
insert into cloths values (3,'Womens',1);
insert into cloths values (4,'T-Shirt_M',2);
insert into cloths values (5,'Casual Shirts_M',2);
insert into cloths values (6,'T-Shirt_F',3);
insert into cloths values (7,'Education',8);
DECLARE #variety VARCHAR(100) = 'Casual Shirts_M';
WITH
cte1 (id, descr, parentid)
AS (SELECT *
FROM cloths
WHERE descr = #variety
UNION ALL
SELECT c.id,
c.descr,
c.parentid
FROM cloths c
INNER JOIN cte1 r
ON c.id = r.parentid)
SELECT *
FROM cte1
ORDER BY parentid ASC;

Sum the number of occurrence by id

Is it possible to COUNT the number of times a value occurs in a table, however, use the count of 1 if the value appears more than once for each id.
Take the below table as an example. We want to see if either {5,6} occurred for p_id. If more than 1 occurrence of {5,6} is found, treat it as 1. For eg. p_id 1, the total count is 1.
p_id status
1 5
1 6
1 2
2 5
2 5
3 4
3 2
4 6
4 2
4 5
..transforms to..
p_id count
1 1
2 1
3 0
4 1
COUNT(CASE status IN (5,6) THEN 1 END) does an overall count.
Use the CASE...WHEN... as follows:
SELECT a.id, ISNULL(b.cnt, 0)
FROM
(
SELECT DISTINCT id FROM tab
) a
LEFT JOIN
(
SELECT id, CASE COUNT(*) WHEN 1 THEN 0 ELSE 1 END 'cnt'
FROM tab WHERE val in (5, 6) GROUP BY id
) b
ON a.id = b.id
SQLFiddle
This solution provides a quick setup and a simple two-step explanation of how I do this, using your example. The second query provides the desired result:
CREATE TABLE #temp (p_id INT, [status] INT);
INSERT #temp VALUES (1,5);
INSERT #temp VALUES (1,6);
INSERT #temp VALUES (1,2);
INSERT #temp VALUES (2,5);
INSERT #temp VALUES (2,5);
INSERT #temp VALUES (3,4);
INSERT #temp VALUES (3,2);
INSERT #temp VALUES (4,6);
INSERT #temp VALUES (4,2);
INSERT #temp VALUES (4,5);
-- Simple two-step tutorial
-- First, group by p_id so that all p_id's will be shown
-- run this to see...
SELECT A.p_id
FROM #temp A
GROUP BY A.p_id;
-- Now expand your query
-- Next, for each p_id row found, perform sub-query to see if 1 or more exist with status=5 or 6
SELECT A.p_id
,CASE WHEN EXISTS(SELECT 1 FROM #temp B WHERE B.p_id=A.p_id AND [status] IN (5,6)) THEN 1 ELSE 0 END AS [Count]
FROM #temp A
GROUP BY A.p_id;
Use the SIGN() function. It is exactly what you are looking for.
SELECT
[p_id],
SIGN(COUNT(CASE WHEN [status] IN (5,6) THEN 1 END)) AS [count]
FROM #temp
GROUP BY p_id
You can translate 5,6 = 1 and rest to 0 then do max()
with cte as (
select p_id, case when status in (5,6) then 1 else 0 end status
from FROM #tem)
select p_id, max(status) status
from cte
group by p_id

Merge the common rows in SQL

I am getting the following output
Code Manager Employee
1 A Emp1
1 A Emp2
1 A Emp3
2 B Emp4
2 B Emp5
but I want result as
Code Manager Employee
1 A Emp1
Emp2
Emp3
2 B Emp4
Emp5
Code and manager columns should not repeat.It should be blank.
select case when Code = lag(Code) over(order by Code, Manager, Employee)
then null
else Code
end as Code,
case when Manager = lag(Manager) over(order by Code, Manager, Employee)
then null
else Manager
end as Manager,
Employee
from YourTable Y
order by Y.Code, Y.Manager, Y.Employee
Try on SQL Fiddle
You need something like Access or Crystal Reports to do this sort of formatting. Its not possible in plain SQL.
That is not possible by SQL. You should manually loop the data in code after receiving it from database.
Edit
After comments by Vashh and Lieven, I realized that it is possible. So if he needs for display purpose he can either use Func (suggested by Vaassh), Join with null (s. by Lieven) or may be loop and add to datagridview or table or whatever he wants to use.
For the fun of it, following is one way to do it but in the end, this is better done in the end-user application
;WITH YourTable (Code, Manager, Employee) AS(
SELECT * FROM (VALUES
(1, 'A', 'Emp1')
, (1, 'A', 'Emp2')
, (1, 'A', 'Emp3')
, (2, 'B', 'Emp4')
, (2, 'B', 'Emp5')
) a (b, c, d)
)
, q AS (
SELECT rn = ROW_NUMBER() OVER (ORDER BY Code, Manager, Employee), *
FROM YourTable
)
SELECT Code = CASE WHEN q1.Code = q2.Code THEN NULL ELSE q1.Code END
, Manager = CASE WHEN q1.Code = q2.Code THEN NULL ELSE q1.Manager END
, q1.Employee
FROM q q1
LEFT OUTER JOIN q q2 ON q1.rn = q2.rn + 1
I know you're asking for an answer in Oracle, but maybe this SQL Server example will help you (if you really, really need to do it like this and not in a reporting environment):
DECLARE #TBL TABLE(
Code INT,
Manager CHAR(1),
Employee VARCHAR(4))
INSERT #TBL VALUES (1,'A','Emp1')
INSERT #TBL VALUES (1,'A','Emp2')
INSERT #TBL VALUES (1,'A','Emp3')
INSERT #TBL VALUES (2,'B','Emp4')
INSERT #TBL VALUES (2,'B','Emp5')
;WITH cte
AS (SELECT Code
,Manager
,Employee
,ROW_NUMBER() OVER(ORDER BY Code) rownum
FROM #TBL)
SELECT CASE curr.Code
WHEN prev.Code THEN ''
ELSE CAST(curr.Code AS VARCHAR(20))
END AS _Code
,CASE curr.Manager
WHEN prev.Manager THEN ''
ELSE curr.Manager
END AS _Manager
,curr.Employee
FROM cte curr
LEFT JOIN cte prev
ON curr.rownum = prev.rownum + 1
If you're just using SQL Server 2005/2008, you can achieve this with the following.
declare #table table (
Code int,
Manager Varchar(1),
Employee varchar(10)
)
insert into #table values
(1,'A','Emp1'),
(1,'A','Emp2'),
(1,'A','Emp3'),
(2,'A','Emp4'),
(2,'A','Emp5')
select * from #table
select
case when number=1 then Code else null end as Code,
case when number=1 then Manager else null end as Manager,
employee
from (
select *,
row_number() over (partition by code, manager order by code,manager) as number
from #table
) x
Which will give you:
(5 row(s) affected)
Code Manager Employee
----------- ------- ----------
1 A Emp1
1 A Emp2
1 A Emp3
2 A Emp4
2 A Emp5
(5 row(s) affected)
Code Manager employee
----------- ------- ----------
1 A Emp1
NULL NULL Emp2
NULL NULL Emp3
2 A Emp4
NULL NULL Emp5
(5 row(s) affected)
Done:)
You can change NULL values to '' if you want to.
WITH
CTE1 AS (
SELECT DISTINCT [Code], [Manager],
( SELECT TOP 1 Employee
FROM [dbo].[table] t2
WHERE t1.Code = t2.Code AND t1.Manager = t2.Manager
ORDER BY Employee) AS [Employee]
FROM [dbo].[table] t1)
,
CTE2 AS (
SELECT * FROM [dbo].[table]
EXCEPT
SELECT * FROM CTE1)
SELECT * FROM CTE1
UNION
SELECT NULL as Code, NULL as Manager, Employee
FROM CTE2
ORDER BY Employee
My SQL*Plus is a little rusty but if that's the tool that you're using then it should be fairly simple but using the BREAK command.
As mentioned in one of the comments about, this is best left to your reporting tool rather than doing in the actual SQL because an individual row without all the values doesn't make any sense outside the context of the result set.
BREAK on code on manager
SELECT code, manager, employee
FROM yourTable y
order by code, manager;
Please not that my SQL*Plus is a little rusty so this might not work exactly but details of the BREAK command can be found in the Oracle documentation.