I have the following data:
Table:
CREATE TABLE tblLoop
(
person1 varchar(20),
person2 varchar(20),
ColDate date,
);
INSERT INTO tblLoop VALUES('A','B','2020-01-01'),('A','C','2020-01-01'),('A','D','2020-01-01'),
('B','E','2020-01-02'),('B','F','2020-01-02'),
('D','G','2020-01-03'),('D','H','2020-01-03'),
('F','i','2020-01-04'),
('G','J','2020-01-05'),
('i','A','2020-01-06'),
('J','D','2020-01-07'),
('X','Y','2020-01-08'),('X','Z','2020-01-08'),
('Z','X','2020-01-09'),
('Y','W','2020-01-09');
Records Look like:
Requirement: I need to find the persons which forms a cycle. For an example in the given data we found 3 Cycle's:
Cycle 1: A connected with B connected with F connected with i connected with A.
Cycle 2: A connected with D connected with G connected with J connected with D.
Cycle 3: X connected with Z connected with X.
Expected result:
LoopFound
--------------------
A->B->F->i->A
A->D->G->J->D
X->Z->X
My try:
;WITH CTE AS
(
SELECT Person1, Person2,
CONVERT(VARCHAR(MAX), (','+ Person1+ ','+ Person2+ ',')) AS nodes, 1 AS lev,
(CASE WHEN Person1 = Person2 THEN 1 ELSE 0 END) AS has_cycle
FROM tblLoop e
UNION ALL
SELECT cte.Person1, e.Person2,
CONVERT(VARCHAR(MAX), (cte.nodes+ e.Person2+ ',')), lev + 1,
(CASE WHEN cte.nodes LIKE ('%,'+ e.Person2+ ',%') THEN 1 ELSE 0 END) AS has_cycle
FROM CTE
JOIN tblLoop e ON e.Person1 = cte.Person2
WHERE cte.has_cycle = 0
)
SELECT *
FROM CTE
WHERE has_cycle = 1;
NOTE: Getting multiple combination of cycles from above query.
As per the comment by Kevin, the answer lies in including a flag to indicate that a given node is a valid start point, and can be included in the query:
In this instance, 'A' and 'X' are given as start points, therefore we will mark all records where the originator is either 'A' or 'X':
CREATE TABLE #tblLoop
(
person1 varchar(20),
person2 varchar(20),
ColDate date,
isRoot INT
);
INSERT INTO #tblLoop VALUES('A','B','2020-01-01',1),
('A','C','2020-01-01',1),
('A','D','2020-01-01',1),
('B','E','2020-01-02',0),
('B','F','2020-01-02',0),
('D','G','2020-01-03',0),
('D','H','2020-01-03',0),
('F','i','2020-01-04',0),
('G','J','2020-01-05',0),
('i','A','2020-01-06',0),
('J','D','2020-01-07',0),
('X','Y','2020-01-08',1),
('X','Z','2020-01-08',1),
('Z','X','2020-01-09',0),
('Y','W','2020-01-09',0);
Then the following query can be amended as follows:
;WITH CTE AS
(
SELECT Person1, Person2, isRoot,
CONVERT(VARCHAR(MAX), (','+ Person1+ ','+ Person2+ ',')) AS nodes, 1 AS lev,
(CASE WHEN Person1 = Person2 THEN 1 ELSE 0 END) AS has_cycle
FROM #tblLoop e
UNION ALL
SELECT cte.Person1, e.Person2, cte.isRoot,
CONVERT(VARCHAR(MAX), (cte.nodes+ e.Person2+ ',')), lev + 1,
(CASE WHEN cte.nodes LIKE ('%,'+ e.Person2+ ',%') THEN 1 ELSE 0 END) AS has_cycle
FROM CTE
JOIN #tblLoop e ON e.Person1 = cte.Person2
WHERE cte.has_cycle = 0
)
SELECT *
FROM CTE
WHERE has_cycle = 1
AND isRoot = 1
Credit to Kevin for the idea, this is just a working implementation of it.
I have tried the following two steps:
Step 1: In this step, Find the start node's of the graph.
--Create table to store start nodes
IF OBJECT_ID('dbo.Temp_tblLoop', 'U') IS NOT NULL
BEGIN
DROP TABLE dbo.Temp_tblLoop;
END
--Query to find start nodes.
;WITH CTE AS
(
SELECT t.*
FROM tblLoop t
WHERE person1 IN (SELECT person2 FROM tblLoop t2 WHERE t.ColDate<= t2.ColDate) OR
person2 IN (SELECT person1 FROM tblLoop t3 WHERE t.ColDate<= t3.ColDate)
)
SELECT DISTINCT person1 INTO Temp_tblLoop
FROM CTE
WHERE person1 NOT IN (SELECT person2 FROM CTE);
Step 2: Find out the cycles in the graph (Excluding Similar Cycles)
;WITH CTE AS
(
SELECT Person1, Person2,
CONVERT(VARCHAR(MAX), (Person1+ '->'+ Person2)) AS nodes, 1 AS lev,
(CASE WHEN Person1 = Person2 THEN 1 ELSE 0 END) AS has_cycle
FROM tblLoop e WHERE person1 IN (SELECT person1 FROM Temp_tblLoop)
UNION ALL
SELECT cte.Person1, e.Person2,
CONVERT(VARCHAR(MAX), (cte.nodes+'->'+ e.Person2)), lev + 1,
(CASE WHEN CHARINDEX(e.Person2, cte.nodes) != 0 THEN 1 ELSE 0 END) AS has_cycle
FROM CTE
JOIN tblLoop e ON e.Person1 = cte.Person2
WHERE cte.has_cycle = 0
)
SELECT Person1 AS Start, Person2 AS [End],
nodes AS Links,
lev AS Levels
FROM CTE
WHERE has_cycle = 1;
Let me your reviews, if anything needs to be done for betterment.
Related
I have the below requirement
As can be figure out that for every Collector whenever the SubProduct is changing, we are performing it's "SubTotal".
Once the "SubTotal" is over, then we are performing the "Product Total" which is the summation of the "SubProducts" for that collector at the Product level (means until the Product is changed).
and finally, "Grand Total" of that Collector's Product
e.g.
Collector Amreet has 2 Products (NCB and NCT). For NCB he has 2 subProducts viz. Credit Card and Loan. His Product Total is computed at both NCB and NCT level and Grand Total for all the products.
Same is the case for the collector "Vijay"
I have written the below code which is very close to the requirement only thing is that, I'm not able to Add/append Collector Name at the Grand Total Level.
My attempt so far
declare #t table(Collector varchar(50),Product varchar(50),SubProduct varchar(50),Amount int)
Insert into #t
select 'Vijay','NCB','Credit Card',8000 union all
select 'Vijay','NCB','Credit Card',2000 union all
select 'Vijay','NCB','Credit Card',17000 union all
select 'Vijay','NCB','Credit Card',5000 union all
select 'Vijay','NCB','Loan',15000 union all
select 'Vijay','NCB','Loan',5000 union all
select 'Amreet','NCB','Credit Card',3000 union all
select 'Amreet','NCB','Credit Card',1000 union all
select 'Amreet','NCB','Loan',45000 union all
select 'Amreet','NCB','Loan',9000 union all
select 'Amreet','NCT','Loan',1000 union all
select 'Amreet','NCT','Loan',2000
Select
*
from
(select
case when grouping(Rn) = 1 then '' else Collector end as Collector,
case when grouping(Rn) = 1 then '' else Product end as Product,
case
when grouping(Collector) = 0 and grouping(Product) = 1 and grouping(SubProduct) = 1 and grouping(Rn) = 1 then 'Grand Total'
when grouping(Collector) = 0 and grouping(Product) = 0 and grouping(SubProduct) = 1 and grouping(Rn) = 1 then 'Total(Product Total)'
when grouping(Collector) = 0 and grouping(Product) = 0 and grouping(SubProduct) = 0 and grouping(Rn) = 1 then 'SubTotal(Sub Product Total)'
else SubProduct end as SubProduct,
sum(Amount) as Amount
from
(select
*,
Rn = row_number() over(partition by Collector,Product,SubProduct order by 1/0)
from #t) tData
group by
rollup(Collector,Product,SubProduct, Rn))x
where x.SubProduct is not null
Output
Please check this one. Here I've added in one things.
declare #t table(Collector varchar(50),Product varchar(50),SubProduct varchar(50),Amount int)
Insert into #t
select 'Vijay','NCB','Credit Card',8000 union all
select 'Vijay','NCB','Credit Card',2000 union all
select 'Vijay','NCB','Credit Card',17000 union all
select 'Vijay','NCB','Credit Card',5000 union all
select 'Vijay','NCB','Loan',15000 union all
select 'Vijay','NCB','Loan',5000 union all
select 'Amreet','NCB','Credit Card',3000 union all
select 'Amreet','NCB','Credit Card',1000 union all
select 'Amreet','NCB','Loan',45000 union all
select 'Amreet','NCB','Loan',9000 union all
select 'Amreet','NCT','Loan',1000 union all
select 'Amreet','NCT','Loan',2000
Select
*
from
(select
case when grouping(Rn) = 1 then '' else Collector end as Collector,
case when grouping(Rn) = 1 then '' else Product end as Product,
case
when grouping(Collector) = 0 and grouping(Product) = 1 and grouping(SubProduct) = 1 and grouping(Rn) = 1 then 'Grand Total (' + Collector + ')'
when grouping(Collector) = 0 and grouping(Product) = 0 and grouping(SubProduct) = 1 and grouping(Rn) = 1 then 'Total(Product Total)'
when grouping(Collector) = 0 and grouping(Product) = 0 and grouping(SubProduct) = 0 and grouping(Rn) = 1 then 'SubTotal(Sub Product Total)'
else SubProduct end as SubProduct,
sum(Amount) as Amount
from
(select
*,
Rn = row_number() over(partition by Collector,Product,SubProduct order by 1/0)
from #t) tData
group by
ROLLUP(Collector,Product,SubProduct, Rn))x
where x.SubProduct is not null;
I have a table (Meetings) which contains the following columns:
HomeTeam(varchar)
AwayTeam(varchar)
Home(int)
Away(int)
My problem is that I need to output all teams which have more than a half wins.
The code provided below is working perfectly for the output, but I need to filter out the teams with more than a half wins. For Example:
Liverpool has won 3 out of 5 games, so I want it to be outputed, PSG has 4 out of 5 wins, so I want them also to be outputed. But Man United has only 2 wins out of 5 games and I do not want them in the output.
I am using MSSQL Server 18.
SELECT T.*,
(SELECT COUNT(*)
FROM Meetings AS M
WHERE (M.HomeTeam = T.Team and M.Home > M.Away) OR
(M.AwayTeam = T.Team and M.Away > M.Home)
GROUP BY COUNT(*) > 2
) AS Wins
FROM Teams AS T
The expected output is:
From Juventus 2/5,
Man United 1/5,
PSG 4/5,
Liverpool 3/5,
Barcelona 3/5,
Bayern Munchen 0/5,
I want in the output to be only PSG, Liverpool and Barcelona as they have more than a half wins.
Here is your solution in "one" statement, logically split in WITH statement:
DECLARE #Meetings TABLE (
HomeTeam VARCHAR(100) NOT NULL,
AwayTeam VARCHAR(100) NOT NULL,
Home INT NOT NULL,
Away INT NOT NULL
)
INSERT INTO #Meetings
(
HomeTeam,
AwayTeam,
Home,
Away
)
VALUES
('A', 'B', 2, 5),
('C', 'D', 1, 5),
('A', 'D', 3, 2),
('C', 'A', 1, 5),
('C', 'B', 4, 2),
('B', 'D', 1, 4)
;WITH Teams AS (
SELECT DISTINCT HomeTeam AS TeamName FROM #Meetings
UNION
SELECT DISTINCT AwayTeam FROM #Meetings
)
, TeamWins AS (
SELECT T.TeamName, (HW.HomeWins + AW.AwayWins) Wins, GM.Games FROM Teams T
OUTER APPLY
(SELECT COUNT(*) HomeWins FROM #Meetings M WHERE M.HomeTeam = T.TeamName AND M.Home > M.Away) HW
OUTER APPLY
(SELECT COUNT(*) AwayWins FROM #Meetings M WHERE M.AwayTeam = T.TeamName AND M.Away > M.Home) AW
OUTER APPLY
(SELECT COUNT(*) Games FROM #Meetings M WHERE M.AwayTeam = T.TeamName) GM
)
SELECT * FROM TeamWins TW WHERE 2*TW.Wins > TW.Games
Output:
TeamName Wins Games
A 2 1
C 1 0
D 2 3
You can LEFT JOIN twice Teams with Meetings (once for home games and once for away games), and then analyze and filter the output:
SELECT
t.*,
COALESCE(h.cnt_wins, 0) + COALESCE(a.cnt_wins, 0) total_wins,
COALESCE(h.cnt_games, 0) + COALESCE(a.cnt_games, 0) total_games
FROM Teams t
LEFT JOIN (
SELECT
HomeTeam Team,
SUM(CASE WHEN Home > Away THEN 1 ELSE 0 END) cnt_wins,
COUNT(*) cnt_games,
FROM Meetings
GROUP BY HomeTeam
) h ON m.Team = t.Team
LEFT JOIN (
SELECT
AwayTeam Team,
SUM(CASE WHEN Home < Away THEN 1 ELSE 0 END) cnt_wins,
COUNT(*) cnt_games,
FROM Meetings
GROUP BY AwayTeam
) a ON a.Team = t.Team
WHERE
COALESCE(h.cnt_wins, 0) + COALESCE(a.cnt_wins, 0)
> ( COALESCE(h.cnt_games, 0) + COALESCE(a.cnt_games, 0) ) / 2
Adding an answer using a cte to organize the table in winners and losers
declare #t table (HomeTeam varchar(100),AwayTeam varchar(100),Home int, Away int)
-- determine winner and loser
;with cte as
(
select Outcome,Team
from #t
cross apply (values
('W'
, case when Home>Away
then HomeTeam
else AwayTeam end)
,('L', case when Home>Away
then AwayTeam
else HomeTeam end)) ca(Outcome,Team)
where Home<>Away
)
select Team
,wins = SUM(case when Outcome = 'W' then 1 else 0 end )
,Losses = SUM(case when Outcome = 'L' then 1 else 0 end )
,Rate = SUM(case when Outcome = 'W' then 1 else 0 end )/COUNT(*)
from cte
group by Team
having SUM(case when Outcome = 'W' then 1 else 0 end )/COUNT(*)>.5
Example table:
CREATE TABLE Fruit (
ID int identity(1,1) NOT NULL,
ParentID int NULL,
Name varchar(255)
);
I want to sort parent and child records from the same table in alphabetical order (more than one level deep):
Apples
--Green
----Just Sour
----Really Sour
--Red
----Big
----Small
Bananas
--etc.
I attempted this:
;WITH CTE(ID, ParentID, Name, Sort) AS
(
SELECT
ID
,ParentID
,Name
,cast('\' + Name as nvarchar(255)) AS Sort
FROM Fruit
WHERE ParentID IS NULL
UNION ALL
SELECT
a.ID
,a.ParentID
,a.Name
,cast(b.Sort + '\' + a.Name as nvarchar(255)) AS Sort
FROM Fruit a
INNER JOIN CTE b ON a.ParentID = b.ID
)
SELECT * FROM CTE Order by Sort
This produces results for the sort like:
\Apples
\Apples\Green
\Apples\Green\Just Sour
\etc.
Just when I thought things were good, it isn't reliable. For example, if an item has more than one word. Like:
\Apples
\Apples A <-- culprit
\Apples\Green
If I can expand my question while I'm at it, I'd like to show actual hyphens or something in the results:
Parent
- Child
--Grandchild
The cruddy way I quickly did this was by adding a prefix column in the table with the value of - for all records. Then I could do this:
;WITH CTE(ID, ParentID, Name, Sort, Prefix) AS
(
SELECT
ID
,ParentID
,Name
,cast('\' + Name as nvarchar(255)) AS Sort
,Prefix
FROM Fruit
WHERE ParentID IS NULL
UNION ALL
SELECT
a.ID
,a.ParentID
,a.Name
,cast(b.Sort + '\' + a.Name as nvarchar(255)) AS Sort
,cast(b.Prefix + a.Prefix as nvarchar(10)) AS Prefix
FROM Fruit a
INNER JOIN CTE b ON a.ParentID = b.ID
)
SELECT * FROM CTE Order by Sort
But that seems incorrect or not optimal.
These hierarchical queries still give me a headache, so perhaps I'm just not seeing the obvious.
I tend to use row_number() ordered by Name in this case
Example
Declare #YourTable table (id int,ParentId int,Name varchar(50))
Insert into #YourTable values
( 1, NULL,'Apples')
,( 2, 1 ,'Green')
,( 3, 2 ,'Just Sour')
,( 4, 2 ,'Really Sour')
,( 5, 1 ,'Red')
,( 6, 5 ,'Big')
,( 7, 5 ,'Small')
,( 8, NULL,'Bananas')
Declare #Top int = null --<< Sets top of Hier Try 5
Declare #Nest varchar(25) = '|-----' --<< Optional: Added for readability
;with cteP as (
Select Seq = cast(1000+Row_Number() over (Order by Name) as varchar(500))
,ID
,ParentId
,Lvl=1
,Name
From #YourTable
Where IsNull(#Top,-1) = case when #Top is null then isnull(ParentId ,-1) else ID end
Union All
Select Seq = cast(concat(p.Seq,'.',1000+Row_Number() over (Order by r.Name)) as varchar(500))
,r.ID
,r.ParentId
,p.Lvl+1
,r.Name
From #YourTable r
Join cteP p on r.ParentId = p.ID)
Select A.ID
,A.ParentId
,A.Lvl
,Name = Replicate(#Nest,A.Lvl-1) + A.Name
,Seq --<< Can be removed
From cteP A
Order By Seq
Returns
ID ParentId Lvl Name Seq
1 NULL 1 Apples 1001
2 1 2 |-----Green 1001.1001
3 2 3 |-----|-----Just Sour 1001.1001.1001
4 2 3 |-----|-----Really Sour 1001.1001.1002
5 1 2 |-----Red 1001.1002
6 5 3 |-----|-----Big 1001.1002.1001
7 5 3 |-----|-----Small 1001.1002.1002
8 NULL 1 Bananas 1002
I'm going to guess you want this result
\Apples
\Apples\Green
\Apples A
Maybe try something like this:
SELECT *
FROM CTE
ORDER BY replace(Sort, ' ', '~')
'~' is ascii 126, You can also check using excel sorting.
I have a following table:
State LAB GROUP DATE CODE ID
UP A I 1-Jan 1 345
UP R S 1-Feb 1 456
UP A S 1-Jan 2 567
DL R S 1-Feb 3 678
DL T S 1-Jan 1 789
DL A S 1-Jan 2 900
MN T S 1-Jan 3 1011
MN R I 1-Feb 1 1122
MN S I 1-Feb 2 1233
I need a pivot table of following type:
STATE A R T TOTAL
UP 2 1 0 3
DL 1 1 1 3
MN 0 1 1 2
DISTINCT COUNT OF ID FOR EACH LAB FOR EACH STATE.
I then need the pivot tables filtered for following columns:
GROUP
DATE
CODE
So 1st table will have the pivot table above counting only those records which have GROUP=S
2nd table will have the pivot table above counting only those records which have CODE=1
and so on, I wish to put multiple conditions. and generate several tables one by one and export them.
If this is possible in SQL please let me know! I ruled out excel vba due to the size of table (source table will have 800,000 records approx).
Try this :-
Select [State],[A],[R],[T],Total = [A] + [R]+ [T]
from
(
Select [State],
[A] = Sum(Case when LAB='A' then 1 else 0 END) ,
[R] = Sum(Case when LAB='R' then 1 else 0 END) ,
[T] = Sum(Case when LAB='T' then 1 else 0 END)
from YourTable
group by [State]
)a
SQL FIDDLE
CREATE TABLE #t(States VARCHAR(10),LAB VARCHAR(5),GROUPs VARCHAR(5),DATEs VARCHAR(10),CODE INT,ID INT)
INSERT INTO #t values('UP','A','I','1-Jan',1,345)
INSERT INTO #t values('UP','R','S','1-Feb',1,456)
INSERT INTO #t values('UP','A','S','1-Jan',2,567)
INSERT INTO #t values('DL','R','S','1-Feb',3,678)
INSERT INTO #t values('DL','T','S','1-Jan',1,789)
INSERT INTO #t values('DL','A','S','1-Jan',2,900)
INSERT INTO #t values('MN','T','S','1-Jan',3,1011)
INSERT INTO #t values('MN','R','I','1-Feb',1,1122)
INSERT INTO #t values('MN','S','I','1-Feb',2,1233)
SELECT States,ISNULL(A,0) A,ISNULL(R,0) R,ISNULL(T,0) T,ISNULL(A,0)+ISNULL(R,0)+ISNULL(T,0) total
FROM
(
SELECT States,LAB,Count(ID) AS cnt FROM #t GROUP BY States,LAB /*apply GROUP DATE CODE condition here*/
) AS PVT
PIVOT(MAX(cnt) FOR LAB IN (A,R,T)) pvt
Another solution using PIVOT :
WITH PivotInUse AS (
SELECT state,lab,COUNT(*) AS cnt
FROM YourTable
GROUP BY state,lab
)
SELECT STATE
,COALESCE([A], 0) AS A
,COALESCE([R], 0) AS R
,COALESCE([T], 0) AS T
,COALESCE([A], 0) + COALESCE([R], 0) + COALESCE([T], 0) AS TOTAL
FROM PivotInUse
PIVOT(SUM(cnt) FOR lab IN ([A],[R],[T])) AS p;
Your sample table
SELECT * INTO #TEMP FROM
(
SELECT 'UP' [State],'A' LAB,'I' [GROUP],'1-Jan' [DATE],1 CODE,345 ID
UNION ALL
SELECT 'UP','R','S','1-Feb',1,456
UNION ALL
SELECT 'UP','A','S','1-Jan',2,567
UNION ALL
SELECT 'DL','R','S','1-Feb',3,678
UNION ALL
SELECT 'DL','T','S','1-Jan',1,789
UNION ALL
SELECT 'DL','A','S','1-Jan',2,900
UNION ALL
SELECT 'MN','T','S','1-Jan',3,1011
UNION ALL
SELECT 'MN','R','I','1-Feb',1,1122
UNION ALL
SELECT 'MN','S','I','1-Feb',2,1233
)TAB
Now you need to get the distinct count of each state and get the sum as the result to show Total
in pivoted result.
SELECT DISTINCT [State],LAB,SUM(CNT) CNT
INTO #NEWTABLE
FROM
(
SELECT DISTINCT
[State],LAB,
CASE WHEN [State] IS NULL THEN NULL ELSE COUNT([State]) OVER(PARTITION BY [State],LAB) END CNT
FROM #TEMP
)TAB
GROUP BY [State],LAB
WITH ROLLUP
Now we need to get the distinct columns for pivot(#cols) and columns to identify and replace null with zero in pivot(#NullToZeroCols)
DECLARE #cols NVARCHAR (MAX)
DECLARE #NullToZeroCols NVARCHAR (MAX)
SET #cols = SUBSTRING((SELECT DISTINCT ',['+LAB+']' FROM #NEWTABLE GROUP BY LAB FOR XML PATH('')),2,8000)
SET #NullToZeroCols = SUBSTRING((SELECT DISTINCT ',ISNULL(['+LAB+'],0) AS ['+LAB+']'
FROM #NEWTABLE GROUP BY LAB FOR XML PATH('')),2,8000)
Join the pivotted query with the #NEWTABLE to get the Total for each State
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT P.State,' + #NullToZeroCols + ',T2.CNT TOTAL FROM
(
SELECT DISTINCT [State],LAB,CNT FROM #NEWTABLE
) x
PIVOT
(
SUM(CNT)
FOR [LAB] IN (' + #cols + ')
) p
JOIN #NEWTABLE T2 ON P.[STATE]=T2.[STATE]
WHERE P.State IS NOT NULL AND T2.LAB IS NULL AND T2.[STATE] IS NOT NULL;'
EXEC SP_EXECUTESQL #query
Here is your result
Here is the SQLFiddle http://sqlfiddle.com/#!3/c2588/1 (If it shows any error while loading the page, just click RUNSQL, it will work)
Now if you want to get the result as you said DISTINCT COUNT OF ID FOR EACH LAB FOR EACH STATE, just change
OVER(PARTITION BY [State],LAB)
to
OVER(PARTITION BY [State],LAB,Id)
which will show the following result after executing the pivot query
I'm struggling with this one. I have a table A which looks like this:
Employee_ID Dependant_FirstName DOB
1 John 12/12/1980
1 Lisa 11/11/1982
2 Mark 06/06/1985
2 Shane 07/07/1982
2 Mike 03/04/1990
3 NULL NULL
and would like to copy these data in Table B like this (knowing that there could only be a maximum of 6 dependants in Table A):
Employee_ID Dependant1_FirstName DOB Dependant2_FirstName DOB Dependant3_FirstName DOB
1 John 12/12/1980 Lisa 11/11/1982 NULL NULL
2 Mark 06/06/1985 Shane 07/07/1982 Mike 03/04/1990
3 NULL NULL NULL NULL NULL NULL
Thanks very much for the help.
Marc
This is a working example for just your example data, to give an idea of how I'd do it. I'm using a faked-up dependant counter based on date of birth and name. Bear in mind it will break if an employee has twins with the same name, but if they do that, then they deserve all the lifelong data-confusion that they've got in store :)
Also, please consider upgrading that SQL Server. Or moving this kind of pivoting to your reporting tool rather than the database.
CREATE TABLE #employees (employee_id INTEGER, Dependant_FirstName VARCHAR(20), DOB DATETIME)
INSERT INTO #employees VALUES (1, 'John', '12/12/1980')
INSERT INTO #employees VALUES (1, 'Lisa', '11/11/1982')
INSERT INTO #employees VALUES (2, 'Shane', '07/07/1982')
INSERT INTO #employees VALUES (2, 'Mark', '06/06/1985')
INSERT INTO #employees VALUES (2, 'Mike', '03/04/1990')
INSERT INTO #employees VALUES (3, NULL, NULL)
SELECT
employee_id,
MAX(CASE WHEN dep_count = 1 THEN Dependant_FirstName ELSE NULL END) 'Dependant1_FirstName',
MAX(CASE WHEN dep_count = 1 THEN DOB ELSE NULL END) 'Dependant1_DOB',
MAX(CASE WHEN dep_count = 2 THEN Dependant_FirstName ELSE NULL END) 'Dependant2_FirstName',
MAX(CASE WHEN dep_count = 2 THEN DOB ELSE NULL END) 'Dependant2_DOB',
MAX(CASE WHEN dep_count = 3 THEN Dependant_FirstName ELSE NULL END) 'Dependant3_FirstName',
MAX(CASE WHEN dep_count = 3 THEN DOB ELSE NULL END) 'Dependant3_DOB'
FROM
(
SELECT
employee_id,
Dependant_FirstName,
DOB,
(
SELECT
COUNT(*)
FROM
#employees deps
WHERE
#employees.employee_id = deps.employee_id AND
CONVERT(VARCHAR, #employees.DOB, 126) + #employees.Dependant_FirstName <=
CONVERT(VARCHAR, deps.DOB, 126) + deps.Dependant_FirstName
) 'dep_count'
FROM
#employees
) add_dep_count_query
GROUP BY
employee_id
You could
Create a view
Calculate a fictuous ranking
Group to find the maximum ranking for each employee_ID
return the results.
Note: I have ommitted the DOB column in the examples
Statement
CREATE VIEW dbo.VIEW_Employees_Ranking AS
SELECT Ranking = ISNULL(e6.Employee_ID, 0)
+ ISNULL(e5.Employee_ID, 0)
+ ISNULL(e4.Employee_ID, 0)
+ ISNULL(e3.Employee_ID, 0)
+ ISNULL(e2.Employee_ID, 0)
+ ISNULL(e1.Employee_ID, 0)
, e1.Employee_ID
, Name1 = e1.Dependant_FirstName
, Name2 = e2.Dependant_FirstName
, Name3 = e3.Dependant_FirstName
, Name4 = e4.Dependant_FirstName
, Name5 = e5.Dependant_FirstName
, Name6 = e6.Dependant_FirstName
FROM dbo.Employees e1
LEFT OUTER JOIN dbo.Employees e2 ON e2.Employee_ID = e1.Employee_ID AND e2.DOB > e1.DOB
LEFT OUTER JOIN dbo.Employees e3 ON e3.Employee_ID = e2.Employee_ID AND e3.DOB > e2.DOB
LEFT OUTER JOIN dbo.Employees e4 ON e4.Employee_ID = e3.Employee_ID AND e4.DOB > e3.DOB
LEFT OUTER JOIN dbo.Employees e5 ON e5.Employee_ID = e4.Employee_ID AND e5.DOB > e4.DOB
LEFT OUTER JOIN dbo.Employees e6 ON e6.Employee_ID = e5.Employee_ID AND e6.DOB > e5.DOB
GO
SELECT er.*
FROM dbo.VIEW_Employees_Ranking er
INNER JOIN (
SELECT Ranking = MAX(Ranking)
, Employee_ID
FROM dbo.VIEW_Employees_Ranking
GROUP BY
Employee_ID
) ermax ON ermax.Ranking = er.Ranking AND ermax.Employee_ID = er.Employee_ID
Check this code please, It might work for you.
declare #Emp_Id int
declare #Name int
declare #DOB int
declare #Count int
set #Count=1
DECLARE x_cursor CURSOR FOR
SELECT distinct Employee_ID from tableA
OPEN x_cursor
FETCH NEXT FROM x_cursor
INTO #Emp_Id
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE second_cursor CURSOR FOR
SELECT distinct Dependant_FirstName,DOB from tableA
where Employee_ID=#Emp_Id
OPEN second_cursor
FETCH NEXT FROM second_cursor
INTO #Name,#DOB
WHILE ##FETCH_STATUS = 0
BEGIN
if(#Count=1)
begin
insert into tableB (Employee_ID , Dependant1_FirstName,DOB)
values(#Emp_Id,#Name,#DOB)
set #Count=#Count+1
end
else
begin
exec('Update tableB set Dependant'+#count+'_FirstName='+#Name+' ,DOB'+#Count+'='+#DOB+' where Employee_ID='+#Emp_Id)
set #Count=#Count+1
end
FETCH NEXT FROM second_cursor
INTO #Name,#DOB
END
CLOSE second_cursor
DEALLOCATE second_cursor
set #Count=1
FETCH NEXT FROM x_cursor
INTO #Emp_Id
END
CLOSE x_cursor;
DEALLOCATE x_cursor
GO
Have a look at this example:
http://ryanfarley.com/blog/archive/2005/02/17/1712.aspx
here he is concatentating the child elements of a parent key into a string which should allow you to write out a flat record.