Merge the common rows in SQL - 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.

Related

How to do pivoting on this layered data

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;

exactly picking a string using Substring or any other function

I have been struck while writing a query to pick a string . What I'm posting the query is sample data
Declare #tbl table (tblname varchar(20),Query VARCHAR(MAX))
Insert into #tbl (tblname,Query) values ('Employee','select EmpId,
(Select top 1 Dept_ID from Stg.Dept
where Deptid = Deptid) Dept_ID,
(Select top 1 Dept_ID from Stg.Sub_dept
where Deptid = D.Deptid) SubDept_ID
from stg.Employee E
left join stg.Dept D
ON D.EMPID = E.EmpID
WHERE EMPID = (Select EMPID from stg.dept where Deptid = Deptid)')
Query :
select tblname,SUBSTRING(LTRIM(SUBSTRING(Query, CHARINDEX('FROM', Query)+4, LEN(Query))),
CHARINDEX('.', LTRIM(SUBSTRING(Query, CHARINDEX('FROM', Query)+4, LEN(Query))))+1,
CHARINDEX(' ',
SUBSTRING(LTRIM(SUBSTRING(Query, CHARINDEX('FROM', Query)+4, LEN(Query))),
CHARINDEX('.', LTRIM(SUBSTRING(Query, CHARINDEX('FROM', Query)+4, LEN(Query))))+1, LEN(Query)))-1) from
#tbl
Result :
tblname Req_tbl
Employee Dept
Actually this query is picking the stg.Dept table name from sub query in that query column. What I exactly want is to pick up stg.Employee table which is the main table.
Output :
tblname Req_tbl
Employee Employee
Can you please suggest on this
This is not 100% clear but what you're trying to do appears to parse a query to determine is the object defined by #tbl.tblname. exists in that query with a FROM clause in it. For example - for "Employee" you are looking for "stg.employee" (excluding the schema).
If I understand this correctly, you can do this easily with a splitter such as DelimitedSplit8K and do something like this:
Declare #tbl table (tblname varchar(20),Query VARCHAR(MAX))
Insert into #tbl (tblname,Query) values ('Employee','select EmpId,
(Select top 1 Dept_ID from Stg.Dept
where Deptid = Deptid) Dept_ID,
(Select top 1 Dept_ID from Stg.Sub_dept
where Deptid = D.Deptid) SubDept_ID
from stg.Employee E
left join stg.Dept D
ON D.EMPID = E.EmpID
WHERE EMPID = (Select EMPID from stg.dept where Deptid = Deptid)')
SELECT TOP (1) t.tblname, req_table = t.tblname
FROM #tbl AS t
CROSS APPLY dbo.delimitedSplit8K(t.Query,CHAR(10)) AS s
WHERE PATINDEX('%[^a-zA-Z]from %'+t.tblname+'%', s.item) > 0;
The problem here, however, is that, depending on how uniform your T-SQL code is - this can get complicated and hairy.

Change of "standings" in a table and converting consecutive rows into a single row

This is source table emp ,
look into the table and required output below:
EMP:
HERE IS THE SOURCE TABLE:
EMPID ENAME STANDING DATE
101 ABC ACTIVE 10-06-91
101 ABC INACTIVE 01-07-2002
102 EFG INACTIVE 02-09-2009
102 EFG ACTIVE 01-10-2011
102 EFG INACTIVE 07-10-2017
103 XYZ ACTIVE 08-07-2010
103 XYZ ACTIVE 08-07-2010
103 XYZ INACTIVE 09-10-2011
OUTPUT:
EMPID ENAME BEFORESTANDING CURRENTSTANDING DATE
101 ABC ACTVE INACTIVE 01-07-2002
102 EFG INACTIVE ACTIVE 01-10-2011
102 EFG ACTIVE INACTIVE 07-10-2017
103 XYZ ACTIVE INACTIVE 09-10-2011
Transformation rule logic is if the empid is same and it has got change of STANDING from active to inactive,
the output will be in a single row and date coulmn will be from latest record.
if the table has got three standings of inactive to active and active to inactive we will maintain 2 rows in the output[(n-1) for output]
with the change of standings
if the table has got same standings in the consequitive rows then will skip the first one and only consider the change of standings.
If you have SQL Server 2012 or newer, you can easily do this using LAG() function
WITH CTE_Source AS
(
SELECT *
, LAG(STANDING) OVER (PARTITION BY EmpID, Ename ORDER BY DATE) AS BEFORE_STANDING
FROM Emp
)
SELECT *
FROM CTE_Source
WHERE BEFORE_STANDING <> STANDING
SQLFiddle DEMO
LAG is the best approach if you are using SQL SERVER 2012+, if in case you are using older version here is some approaches to do it
Cross Apply :
SELECT a.EMPID,
a.ENAME,
BEFORESTANDING= cs.STANDING,
CURRENTSTANDING= a.STANDING,
a.date
FROM Yourtable a
CROSS apply (SELECT TOP 1 *
FROM Yourtable b
WHERE a.EMPID = b.EMPID
AND a.DATE > b.DATE
ORDER BY b.Date DESC) AS cs
SELF JOIN :
WITH CTE
AS (SELECT *,
Rn = Row_number()OVER(partition BY EMPID ORDER BY DATE)
FROM Yourtable)
SELECT a.EMPID,
a.ENAME,
BEFORESTANDING = b.STANDING,
CURRENTSTANDING= a.STANDING,
a.DATE
FROM CTE a
JOIN CTE b
ON a.EMPID = b.EMPID
AND a.Rn = b.Rn + 1
WHERE a.STANDING <> b.STANDING
ORDER BY a.EMPID,
a.DATE
Assuming you are using SQL Server 2012 or later, the following should do the trick...
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #TestData;
CREATE TABLE #TestData (
EMPID INT NOT NULL,
ENAME CHAR(3) NOT NULL,
STANDING VARCHAR(10) NOT NULL,
[DATE] DATE NOT NULL
);
INSERT #TestData (EMPID, ENAME, STANDING, [DATE]) VALUES
('101', 'ABC', 'ACTIVE ', '10-06-1991'),
('101', 'ABC', 'INACTIVE', '01-07-2002'),
('102', 'EFG', 'INACTIVE', '02-09-2009'),
('102', 'EFG', 'ACTIVE ', '01-10-2011'),
('102', 'EFG', 'INACTIVE', '07-10-2017'),
('103', 'XYZ', 'ACTIVE ', '08-07-2010'),
('103', 'XYZ', 'ACTIVE ', '08-07-2010'),
('103', 'XYZ', 'INACTIVE', '09-10-2011');
--=============================================
WITH
cte_AddBeforeStanding AS (
SELECT
td.EMPID,
td.ENAME,
BEFORESTANDING = LAG(td.STANDING, 1) OVER (PARTITION BY td.EMPID ORDER BY td.DATE),
CURRENTSTANDING = td.STANDING,
td.DATE
FROM
#TestData td
)
SELECT
bs.EMPID,
bs.ENAME,
bs.BEFORESTANDING,
bs.CURRENTSTANDING,
bs.DATE
FROM
cte_AddBeforeStanding bs
WHERE
bs.BEFORESTANDING <> bs.CURRENTSTANDING;
HTH, Jason

How to update specific values in a column with other values from same column in the same table? - SQL Server

So lets say I have a Table "Stuff".
It has 3 columns.
So Job Code is supposed to be the same for both manager and the employee under that manager(like it is where jobcode= 000 ). That is the normal scenario.
However in some cases, The Manager will have "ABC" as jobcode.
In those cases, I need to replace "ABC" with the jobcode value of the most recent employee under that manager.
For example, for Manager A1, I need to replace his jobcode of ABC with 234,considering B1 is the most recent employee under him.
For manager A, his jobcode of ABC will be replaced with 121 since B is the only employee under him.
I wrote this query but it doesn't seem to work.
Update X
Set X.JobCode=Y.JobCode
FROM STUFF X
INNER JOIN STUFF Y
ON X.MGRCODE=Y.MGRCODE
AND X.JOBCODE = 'ABC"
AND Y.JOBCODE = ( SELECT TOP 1 JOBCODE FROM STUFF WHERE EMPCODE<>Y.MGRCODE AND
Y.MGRCODE IN (SELECT MGRCODE FROM STUFF WHERE EMPCODE=MGRCODE AND JOBCODE='ABC')
;WITH cteJobCodeRowNum AS (
SELECT
ManagerCode
,JobCode
,ROW_NUMBER() OVER (PARTITION BY ManagerCode ORDER BY UpdateDate DESC) as RowNumber
FROM
#Table
)
UPDATE t
SET JobCode = r.JobCode
FROM
#Table t
INNER JOIN cteJobCodeRowNum r
ON t.EmpCode = r.ManagerCode
AND r.RowNumber = 1
AND t.JobCode <> r.JobCode
WHERE
t.JobCode = 'ABC'
Use a partitioned window function to generate a Row Number to choose the Job Code you want. Partition by ManagerCode and order by UpdateDate descending. Then join that common table expression (or derived table if you nest it) to your table based on EmpCode = ManagerCode To Update The Managers record. You can also then constrain it only to when the Manager has a Job Code of 'ABC' and the job code returned by the row number is different so you only update a specific set of rows.
Another similar method is to create your own row number by using a related cross apply such as:
UPDATE t1
SET JobCode = NewJobCode
FROM
#Table t1
CROSS APPLY (SELECT TOP 1 JobCode as NewJobCode FROM #Table t2 WHERE t1.EmpCode = t2.ManagerCode ORDER BY t2.UpdateDate DESC) n
WHERE
t1.JobCode = 'ABC'
AND t1.JobCode <> n.NewJobCode
Here is a full working example of the window function method:
DECLARE #Table AS TABLE (JobCode CHAR(3), EmpCode VARCHAR(2), ManagerCode VARCHAR(2), UpdateDate DATETIME)
INSERT INTO #Table (JobCode, EmpCode, ManagerCode, UpdateDate)
VALUES ('ABC','A','A',GETDATE()-1),('121','B','B',GETDATE()-1)
,('ABC','A1','A1',GETDATE()-1)
,('234','B1','A1',GETDATE()+1)
,('342','C1','A1',GETDATE()-1)
,('000','A2','A2',GETDATE()-1)
,('000','B2','B2',GETDATE()-1)
SELECT *
FROM
#Table
;WITH cteJobCodeRowNum AS (
SELECT
ManagerCode
,JobCode
,ROW_NUMBER() OVER (PARTITION BY ManagerCode ORDER BY UpdateDate DESC) as RowNumber
FROM
#Table
)
UPDATE t
SET JobCode = r.JobCode
FROM
#Table t
INNER JOIN cteJobCodeRowNum r
ON t.EmpCode = r.ManagerCode
AND r.RowNumber = 1
AND t.JobCode <> r.JobCode
WHERE
t.JobCode = 'ABC'
SELECT *
FROM
#Table

Dividing one SQL column into subgroups with headings

I have two tables
STATUS
SNO | STATUS | DEPARTMENT_ID
1 In progress 1
2 Assigned 2
3 Quoted 2
4 Development 3
DEPARTMENTS
SNO | DEPARTMENT |
1 DESIGNING
2 MARKETING
3 PRODUCTION
Now I want a result like this using SQL stored procedure
Some Custom Column Name | DEPARTMENT_ID
DESIGNING -
In Progress 1
MARKETING -
Assigned 2
Quoted 2
PRODUCTION -
Development 3
The custom column will be used to populate a Telerik RadComboBox with DESIGNING, MARKETING and PRODUCTION acting as separators between statuses.
Select Department, -1 from Department_Table
Union
Select StatusName, Department_ID from Status_Table
Please elaborate your question so that we can provide better answer. Currently it seems you just want to return the joined data of both tables.
Often, this type of operation is more easily done at the application level. You can do it in SQL, using union all and order by, however:
select status as CustomColumnName, department
from ((select s.status, d.department, 1 as ordering
from status s join
departments d
on s.department_id = d.sno
) union all
(select d.department, NULL, 0 as ordering
from departments d
)
) dd
order by CustomColumnName, ordering;
Note: this treats the - as NULL.
Try this.Is it ok with other sample data ?
DECLARE #STATUS TABLE (
SNO INT
,[STATUS] VARCHAR(50)
,DEPARTMENT_ID INT
)
INSERT INTO #STATUS
VALUES (1,'In progress' ,1)
,(2,'Assigned',2)
,(3,'Quoted',2)
,(4,'Development',3)
DECLARE #DEPARTMENT TABLE (SNO INT,DEPARTMENT VARCHAR(50))
INSERT INTO #DEPARTMENT
VALUES ( 1,'DESIGNING'),(2,'MARKETING')
,(3,'PRODUCTION')
--select * from #STATUS
--select * from #DEPARTMENT
;
WITH CTE
AS (
SELECT DEPARTMENT [CustomeColumn]
,'-' DEPARTMENT_ID
,sno
FROM #DEPARTMENT
UNION ALL
SELECT [STATUS]
,cast(DEPARTMENT_ID AS VARCHAR(10))
,(
SELECT sno
FROM #DEPARTMENT
WHERE sno = a.DEPARTMENT_ID
)
FROM #STATUS A
)
SELECT *
FROM CTE
ORDER BY sno