Find Comebacks in Football Database With Sql Query - sql

I have 4 tables.
Matches:
| id | HomeTeamID | AwayTeamID |
--------|-------------|------------
| 1 | 1 | 2
| 2 | 1 | 3
| 3 | 3 | 1
Goals:
| id | MatchID | Minute | TeamID
--------|-------------|---------- |---------
| 1 | 1 | 3 | 2
| 2 | 1 | 5 | 1
| 3 | 1 | 15 | 1
| 4 | 2 | 43 | 3
| 5 | 2 | 75 | 1
| 6 | 2 | 85 | 1
| 7 | 3 | 11 | 1
| 8 | 3 | 13 | 3
| 9 | 3 | 77 | 3
Teams:
| id | Name |
--------|-------------|
| 1 | Chelsea |
| 2 | Arsenal |
| 3 | Tottenham |
Managers:
| id | Name | TeamID |
--------|-------------|-------------
| 1 | Conte | 1
| 2 | Wenger | 2
| 3 | Pochettino | 3
I want to get the number of comebacks matches for managers. For example, Conte's team conceded the first goal in match 1 and 2 but they won. So Conte has 2 comebacks. Pochettino has 1 comeback at match 3. I want to find that with SQL Query.
I found the first goal of matches for each team. But after some steps, I'm losing what I'm doing.
SELECT MatchID, MIN(minute), g.TeamID
FROM Goals g
JOIN Managers m ON m.TeamID = g.TeamID
GROUP BY MatchID, g.TeamID

with cte
(
MatchID,TeamID,TotalGoalTime,NoOfGoals,ManagerName,comeback)
as(SELECT MatchID, g.TeamID,sum(minutea) as'TotalGoalTime' ,count(*)as'NoOfGoals',m.name as'ManagerName'
,comeback =ROW_NUMBER() OVER(PARTITION BY MatchID order by sum(minutea) desc)
FROM Goals g
JOIN Managers m ON m.TeamID = g.TeamID
join [Teams] t on t.Id=g.TeamId
GROUP BY MatchID, g.TeamID,m.name )
Select MatchID,TeamID,NoOfGoals,ManagerName from cte where comeback =1
The above query for now gives us the overall comeback, Will update the no of come backs.

In case you want to count every comeback in football match, you can use the solution below. Then the definition of the comeback is every time when a team score one goal more the opponent, after loosing. For example, for the following scenario we have three comebacks:
Team A Team B
0 - 1 //team b scores
1 - 1 //team a scores
2 - 1 //team a scores (comeback for a)
2 - 2 //team b scores
2 - 3 //team b scores (comeback for b)
3 - 3 //team a scores
4 - 3 //team a scores (comeback for a)
From the above it seems that we have a comeback, when score is changed, and the previous score was even. I am using SUM with OVER and ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ordering by minute in order to calculated the score each time a goal is scored.
Here is full working example:
DECLARE #matches TABLE
(
[id] TINYINT
,[HomeTeamID] TINYINT
,[AwayTeamID] TINYINT
);
DECLARE #Goals TABLE
(
[id] TINYINT
,[MatchID] TINYINT
,[Minute] TINYINT
,[TeamID] TINYINT
);
DECLARE #Teams TABLE
(
[id] TINYINT
,[Name] VARCHAR(12)
);
DECLARE #Managers TABLE
(
[Id] TINYINT
,[Name] VARCHAR(12)
,[TeamID] TINYINT
);
INSERT INTO #matches ([id], [HomeTeamID], [AwayTeamID])
VALUES (1, 1, 2)
,(2, 1, 3)
,(3, 3, 1)
,(4, 1, 4);
INSERT INTO #Goals ([id], [MatchID], [Minute], [TeamID])
VALUES (1, 1, 3, 2)
,(2, 1, 5, 1)
,(3, 1, 15, 1)
,(4, 2, 43, 3)
,(5, 2, 75, 1)
,(6, 2, 85, 1)
,(7, 3, 11, 1)
,(8, 3, 13, 3)
,(9, 3, 77, 3)
,(10, 4, 3, 1)
,(11, 4, 5, 4)
,(12, 4, 10, 4)
,(13, 4, 12, 1)
,(14, 4, 25, 1)
,(15, 4, 46, 4)
,(16, 4, 60, 4)
,(17, 4, 72, 4)
,(18, 4, 84, 4);
INSERT INTO #Teams ([id], [Name])
VALUES (1, 'Chelsea')
,(2, 'Arsenal')
,(3, 'Tottenham')
,(4, 'Real Madrid');
INSERT INTO #Managers ([Id], [Name], [TeamID])
VALUES (1, 'Conte', 1)
,(2, 'Wenger', 2)
,(3, 'Pochettino', 3)
,(4, 'Zidane', 4);
WITH DataSource AS
(
SELECT m.[id]
,m.[HomeTeamID]
,m.[AwayTeamID]
,ROW_NUMBER() OVER (PARTITION BY m.[id] ORDER BY g.[minute]) AS [EventID]
,IIF
(
SUM(IIF(m.[HomeTeamID] = g.[teamID], 1, 0)) OVER (PARTITION BY m.[id] ORDER BY g.[minute] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) - 1
=
SUM(IIF(m.[AwayTeamID] = g.[teamID], 1, 0)) OVER (PARTITION BY m.[id] ORDER BY g.[minute] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
OR
SUM(IIF(m.[HomeTeamID] = g.[teamID], 1, 0)) OVER (PARTITION BY m.[id] ORDER BY g.[minute] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
=
SUM(IIF(m.[AwayTeamID] = g.[teamID], 1, 0)) OVER (PARTITION BY m.[id] ORDER BY g.[minute] ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) -1
,IIF(m.[HomeTeamID] = g.[teamID], 'H', 'A') -- (H)ome come back, (A)way come ba
,'N' -- no come back
) AS [ComeBack]
FROM #matches m
INNER JOIN #Goals g
ON m.[id] = g.[MatchID]
)
SELECT T.[Name]
FROM DataSource DS
INNER JOIN #Teams T
ON IIF([ComeBack] = 'H', [HomeTeamID], [AwayTeamID]) = T.[id]
WHERE DS.[EventID] <> 1
AND DS.[ComeBack] <> 'N';
The above will give us:
Chelsea
Chelsea
Chelsea
Tottenham
Real Madrid
Real Madrid
Note, I have added one more match to demonstrate this.

Here "Comeback" means that team conceded first goal, but team won this game.
I use 2 general subquery, 1) winners which contains MatchID and TeamID from each game which not ended as draw. And 2) first_goals, which contains those TeamID's, which scored first goal in match.
So, joining between these subqueries using:
on winners.MatchID = first_goals.MatchID and winners.TeamID <> first_goals.TeamID
gives us those matchs, where team won, but not scored first goal (i.e "Comeback").
Finally we use simple joins with Teams and Managers tables:
with Goals(id , MatchID , Minute ,TeamID) as (
select 1 , 1 , 3 , 2 union all
select 2 , 1 , 5 , 1 union all
select 3 , 1 , 15 , 1 union all
select 4 , 2 , 43 , 3 union all
select 5 , 2 , 75 , 1 union all
select 6 , 2 , 85 , 1 union all
select 7 , 3 , 11 , 1 union all
select 8 , 3 , 13 , 3 union all
select 9 , 3 , 77 , 3
),
Teams (id, Name) as(
select 1 ,'Chelsea' union all
select 2 ,'Arsenal' union all
select 3 ,'Tottenham'
),
Managers(id, Name, TeamID) as (
select 1 ,'Conte', 1 union all
select 2 ,'Wenger', 2 union all
select 3 ,'Pochettino', 3
)
select winners.TeamID, winners.MatchID, Teams.Name, Managers.Name from (
select t1.* from
(
select TeamID, MatchID, count(*) as goal_scored from Goals
group by TeamID, MatchID
)t1
inner join
(
select MatchID, max(goal_scored) as winner_goals_cnt from (
select TeamID, MatchID, count(*) as goal_scored from Goals
group by TeamID, MatchID
)t
group by MatchID
having min(goal_scored) <> max(goal_scored)
)t2
on t1.MatchID = t2.MatchID and t1.goal_scored = t2.winner_goals_cnt
) winners
inner join
(
select * from (
select Goals.*, row_number() over(partition by MatchID order by Minute, id) rn from Goals
) f
where rn = 1
) first_goals
on winners.MatchID = first_goals.MatchID and winners.TeamID <> first_goals.TeamID
inner join Teams
on winners.TeamID = Teams.id
inner join Managers
on winners.TeamID = Managers.TeamID

I don't follow much of football, but assuming that a comeback is when a team concedes the first goal but then wins the game, I would use the following logic to get the result.
First, we need the teams that had conceded the first goal in a match:
SELECT TeamID, MatchID FROM Goals WHERE GoalID in
(SELECT Min(GoalID) FROM Goals GROUP BY MatchID)
Second, for each match, we need the matches where the team making first goal went on to win.
SELECT MatchID FROM
(SELECT COUNT(GoalID) as TotalGoals, MatchID FROM Goals GROUP BY MatchID) AS MatchSummary
INNER JOIN
(SELECT COUNT(GoalID) as TeamGoals, MatchID FROM
(SELECT TeamID, MatchID FROM Goals WHERE GoalID in
(SELECT Min(GoalID) FROM Goals GROUP BY MatchID)
) as GoalsOfTheTeamThatConcededFirstGoal
GROUP BY MatchID) as SummaryOfTeamThatConcededFirstGoal
ON MatchSummary.MatchID = SummaryOfTeamThatConcededFirstGoal.MatchID
WHERE (TotalGoals - TeamGoals) < TeamGoals
Combining these two queries you will be able to get the TeamIDs for those teams that have made a comeback.
I think this task could be done much better with cursors or temporary tables, but I wanted to keep the answer as simple as possible. Hence I have avoided it. You should definitely explore those two methods.

Related

SQL Order By On two columns but same prority

I'm stuck on this simple select and don't know what to do.
I Have this:
ID | Group
===========
1 | NULL
2 | 100
3 | 100
4 | 100
5 | 200
6 | 200
7 | 100
8 | NULL
and want this:
ID | Group
===========
1 | NULL
2 | 100
3 | 100
4 | 100
7 | 100
5 | 200
6 | 200
8 | NULL
all group members keep together, but others order by ID.
I can not write this script because of that NULL records. NULL means that there is not any group for this record.
First you want to order your rows by the minimum ID of their group - or their own ID in case they belong to no group.Then you want to order by ID. That is:
order by min(id) over (partition by case when grp is null then id else grp end), id
If IDs and groups can overlap (i.e. the same number can be used for an ID and for a group, e.g. add a record for ID 9 / group 1 to your sample data) you should change the partition clause to something like
order by min(id) over (partition by case when grp is null
then 'ID' + cast(id as varchar)
else 'GRP' + cast(grp as varchar) end),
id;
Rextester demo: http://rextester.com/GPHBW5600
What about data after a null? In a comment you said don't sort the null.
declare #T table (ID int primary key, grp int);
insert into #T values
(1, NULL)
, (3, 100)
, (5, 200)
, (6, 200)
, (7, 100)
, (8, NULL)
, (9, 200)
, (10, 100)
, (11, NULL)
, (12, 150);
select ttt.*
from ( select tt.*
, sum(ff) over (order by tt.ID) as sGrp
from ( select t.*
, iif(grp is null or lag(grp) over (order by id) is null, 1, 0) as ff
from #T t
) tt
) ttt
order by ttt.sGrp, ttt.grp, ttt.id
ID grp ff sGrp
----------- ----------- ----------- -----------
1 NULL 1 1
3 100 1 2
7 100 0 2
5 200 0 2
6 200 0 2
8 NULL 1 3
10 100 0 4
9 200 1 4
11 NULL 1 5
12 150 1 6

Cumulative count over dates, discarding some values

I have an Answers table with the following schema:
CREATE TABLE Answers
([id] int, [analyst_id] int, [date] date);
I have to "accumulate-count" how many answers an analyst has per month, discarding any answers given before a period of 3 months after the last answer. Given the following:
INSERT INTO Answers
([id], [analyst_id], [date])
VALUES
(1, 1, '2017/01/01'),
(2, 1, '2017/02/01'), -- should be discarded
(3, 1, '2017/03/01'), -- should be discarded
(4, 1, '2017/05/01'),
(5, 1, '2017/06/01'), -- should be discarded
(6, 1, '2017/07/01'), -- should be discarded
(7, 1, '2017/08/01'),
(8, 2, '2017/01/01'),
(9, 2, '2017/04/01'),
(10, 1, '2018/02/01'),
(11, 2, '2018/03/01');
The expected result is:
analyst_id | month-year | count
-------------------------------
1 | 01/2017 | 1
1 | 02/2017 | 1
1 | 03/2017 | 1
1 | 04/2017 | 1
1 | 05/2017 | 2
1 | 06/2017 | 2
1 | 07/2017 | 2
1 | 08/2017 | 3
1 | 09/2017 | 3
1 | 10/2017 | 3
1 | 11/2017 | 3
1 | 12/2017 | 3
2 | 01/2017 | 1
2 | 02/2017 | 1
2 | 03/2017 | 1
2 | 04/2017 | 2
2 | 05/2017 | 2
2 | 06/2017 | 2
2 | 07/2017 | 2
2 | 08/2017 | 2
2 | 09/2017 | 2
2 | 10/2017 | 2
2 | 11/2017 | 2
2 | 12/2017 | 2
1 | 01/2018 | 0
1 | 02/2018 | 1
1 | 03/2018 | 1
2 | 01/2018 | 0
2 | 02/2018 | 0
2 | 03/2018 | 1
DBMS is a SQL Server 2012.
EDIT
I wrote this fiddle with my current half-solution: http://sqlfiddle.com/#!6/c2e82e/5
Each year, the count need to be reset.
EDIT:
OK, for the updated question, you essentially need to make a "dates" table (here a CTE called "D") that contains all the dates between the minimum and maximum date in your Answers table. Then you can essentially left join your results to that and use a DENSE_RANK() window function to determine the count.
DECLARE #Answers TABLE (ID INT, Analyst_ID INT, [Date] DATE);
INSERT #Answers (ID, Analyst_ID, [Date])
VALUES
(1, 1, '2017/01/01'),
(2, 1, '2017/02/01'),
(3, 1, '2017/03/01'),
(4, 1, '2017/05/01'),
(5, 1, '2017/06/01'),
(6, 1, '2017/07/01'),
(7, 1, '2017/08/01'),
(8, 2, '2017/01/01'),
(9, 2, '2017/04/01'),
(10, 1, '2018/02/01'),
(11, 2, '2018/03/01');
WITH CTE AS
(
SELECT A.Analyst_ID, [Date] = MIN(A.[Date])
FROM #Answers AS A
GROUP BY A.Analyst_ID
UNION ALL
SELECT A.Analyst_ID, A.[Date]
FROM
(
SELECT A.Analyst_ID, A.[Date], RN = ROW_NUMBER() OVER (PARTITION BY A.Analyst_ID ORDER BY A.ID)
FROM #Answers AS A
JOIN CTE
ON CTE.Analyst_ID = A.Analyst_ID
AND DATEADD(MONTH, 3, CTE.[Date]) <= A.[Date]
) AS A
WHERE A.RN = 1
),
D AS -- List of dates between minimum and maximum date in table for each analyst ID.
(
SELECT [Date] = DATEADD(MONTH, RN, (SELECT MIN([Date]) FROM #Answers)),
A.Analyst_ID
FROM (SELECT RN = ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) - 1 FROM sys.objects) AS O
CROSS JOIN (SELECT DISTINCT Analyst_ID FROM #Answers) AS A
WHERE RN <= (SELECT DATEDIFF(MONTH, MIN([Date]), MAX([Date])) FROM #Answers)
)
SELECT D.Analyst_ID,
[Month-Year] = FORMAT(D.[Date], 'MM/yyyy'),
[Count] = CASE WHEN A.[Date] IS NULL THEN 0 ELSE DENSE_RANK() OVER (PARTITION BY D.Analyst_ID, DATEPART(YEAR, A.[Date]) ORDER BY A.[Date]) END
FROM D
OUTER APPLY (SELECT TOP 1 * FROM CTE WHERE CTE.[Date] <= D.[Date] AND DATEDIFF(YEAR, CTE.[Date], D.[Date]) = 0 AND CTE.Analyst_ID = D.Analyst_ID ORDER BY CTE.[Date] DESC) AS A
ORDER BY D.Analyst_ID, D.[Date];

SQL Server grouping rows

I have this data:
Id | Name | count | Group_number
------+-------+-------+--------------
1 | cdd | 50 | 0
2 | cdd | 15 | 0
3 | cdd | 0 | 0
4 | cdd | 25 | 0
5 | cdd | 11 | 0
I want a script that makes three or four groups on condition: Sum(count) for each group < 50
I want this output:
1 | cdd | 50 | 1
2 | cdd | 15 | 2
3 | cdd | 0 | 2
4 | cdd | 25 | 2
5 | cdd | 11 | 3
Assuming this has to be done for each name, you can use a recursive cte.
with rownums as (select t.*,row_number() over(partition by name order by id) as rnum from t)
,cte(rnum,id,name,cnt,runningsum,grp) as
(select rnum,id,name,cnt,cnt,1 from rownums where rnum=1
union all
select t.rnum,t.id,t.name,t.cnt
,case when c.runningsum+t.cnt > 50 then t.cnt else c.runningsum+t.cnt end
,case when c.runningsum+t.cnt > 50 then t.id else c.grp end
from cte c
join rownums t on t.rnum=c.rnum+1 and t.name=c.name
)
select id,cnt,name,dense_rank() over(partition by name order by grp) as grp
from cte
Sample Demo
Keep track of the running sum and reset it when it goes over 50. Also remember the id when the sum goes over 50. This can be used to assign group numbers.
For records where the count is less than 50 we can simply generate a grouping id by calculating a running total on the count and then divide this running total by 50. However, since some records may already have a count that is greater than or equal to 50 might generate an incorrect id. To solve this problem, we need to somehow force the generation of a new grouping id on the next record. This can be done by simply adjusting the count the next record by 50 if the current records count is 50 or greater.
The following example demonstrates how this can be done:
CREATE TABLE #Items
(
[Id] INT NOT NULL PRIMARY KEY
,[Name] VARCHAR(50) NOT NULL
,[Count] INT NOT NULL
)
INSERT INTO #Items
VALUES
(1, 'cdd', 50 ),
(2, 'cdd', 15 ),
(3, 'cdd', 0 ),
(4, 'cdd', 25 ),
(5, 'cdd', 11 );
;WITH CTE_ItemCountsAdjusted
AS
(
SELECT [Id]
,[Name]
,[Count]
,LAG([Count], 1, 0) OVER (PARTITION BY [Name] ORDER BY [Id]) AS PrevCount
,(
CASE
WHEN LAG([Count], 1, 0) OVER (PARTITION BY [Name] ORDER BY [Id]) >= 50 THEN [Count] + 50
ELSE [Count]
END
) AdjustedCount
FROM #Items
)
SELECT [Id]
,[Name]
,[Count]
,SUM([AdjustedCount]) OVER (PARTITION BY [Name] ORDER BY [Id] ROWS UNBOUNDED PRECEDING) / 50 AS [Group_number]
FROM CTE_ItemCountsAdjusted
ORDER BY [Id]
This method eliminates the need for recursive calls. Note if you need the group id to be strictly sequential (no gaps between group numbers) then you can make use of the DENSE_RANK() windowing function to achieve this as per following example:
INSERT INTO #Items
VALUES
(1, 'cdd', 50 ),
(2, 'cdd', 15 ),
(3, 'cdd', 0 ),
(4, 'cdd', 25 ),
(5, 'cdd', 11 ),
(6, 'cdd', 200 ),
(7, 'cdd', 10 );
;WITH CTE_ItemCountsAdjusted
AS
(
SELECT [Id]
,[Name]
,[Count]
,LAG([Count], 1, 0) OVER (PARTITION BY [Name] ORDER BY [Id]) AS PrevCount
,(
CASE
WHEN LAG([Count], 1, 0) OVER (PARTITION BY [Name] ORDER BY [Id]) >= 50 THEN [Count] + 50
ELSE [Count]
END
) AdjustedCount
FROM #Items
),CTE_ItemCountsWithGroupID
AS
(
SELECT [Id]
,[Name]
,[Count]
,SUM([AdjustedCount]) OVER (PARTITION BY [Name] ORDER BY [Id] ROWS UNBOUNDED PRECEDING) / 50 AS [Group_number]
FROM CTE_ItemCountsAdjusted
)
SELECT [Id]
,[Name]
,[Count]
,[Group_number]

Soccer league table standings with SQL

Perhaps a familiar table for many people. A soccer league table.
But, in this list there is one mistake, rank 4 and 5, are totally equal, so these teams should not be ranked 4 and 5, but 4 and 4, and then the ranking should continue with 6.
Ranking | Team | Points | Goals difference | Goals scored | Goals against
1 A 3 4 4 0
2 B 3 3 3 0
3 C 3 1 2 1
4 D 3 1 1 0
5 E 3 1 1 0
6 F 1 0 2 2
7 G 1 0 0 0
I have been trying to improve the MS SQL query that produces this table, by using a Common Table Expression and SELECT ROW_Number, but that never gives me the right result. Does anyone have a better idea?
You can do this easy by using the RANK() function.
declare #table as table
(
Team varchar(1),
Points int,
GoalsScored int,
GoalsAgainst int
)
insert into #table values ('A', 3, 4, 0),
('B', 3, 3, 0),
('C', 3, 2, 1),
('D', 3, 1, 0),
('E', 3, 1, 0),
('F', 1, 2, 2),
('G', 1, 0, 0)
select RANK() OVER (ORDER BY points desc, GoalsScored - GoalsAgainst desc, GoalsScored desc) AS Rank
,team
,points
,GoalsScored - GoalsAgainst as GoalsDifference
,GoalsScored
,GoalsAgainst
from #table
order by rank
Here is a possible solution. I'm not sure specifically how you are ranking so I've ranked based on Points DESC, Goals Diff DESC, Goals Scored DESC and Goals Against ASC.
;WITH
src AS (
SELECT Team, Points, GoalsDiff, GoalsScor, GoalsAga
FROM dbo.[stats]
)
,src2 AS (
SELECT Points, GoalsDiff, GoalsScor, GoalsAga
FROM src
GROUP BY Points, GoalsDiff, GoalsScor, GoalsAga
)
,src3 AS (
SELECT ROW_NUMBER() OVER (ORDER BY Points DESC, GoalsDiff DESC, GoalsScor DESC, GoalsAga) AS Ranking
,Points, GoalsDiff, GoalsScor, GoalsAga
FROM src2
)
SELECT src3.Ranking, src.Team, src.Points, src.GoalsDiff, src.GoalsScor, src.GoalsAga
FROM src
INNER JOIN src3
ON src.Points = src3.Points
AND src.GoalsDiff = src3.GoalsDiff
AND src.GoalsScor = src3.GoalsScor
AND src.GoalsAga = src3.GoalsAga
The basic approach I used is to select just the stats themselves then group them all. Once grouped then you can rank them and then join the grouped stats with ranking back to the original data to get your rank numbers against the teams. One way to think of it is that you are ranking the stats not the teams.
Hope this helps.

Select and merge rows in a table in SQL Stored procedure

Have a temp table with schema: ID | SeqNo | Name
ID - Not unique
SeqNo - Int (can be 1,2 or 3). Sort of ID+SeqNo as Primary key
Name - Any text
And sample data in the table like this
1 | 1 | RecordA
2 | 1 | RecordB
3 | 1 | RecordC
1 | 2 | RecordD
4 | 1 | RecordE
5 | 1 | RecordF
3 | 1 | RecordG
Need to select from this table and output like
1 | RecordA/RecordD
2 | RecordB
3 | RecordC/RecordG
4 | RecordE
5 | RecordF
Need to do this without cursor.
If SeqNo is limited to 1,2,3:
select id, a.name + coalesce('/'+b.name, '') + coalesce('/'+c.name, '')
from myTable a
left outer join myTable b on a.id=b.id and b.seqno = 2
left outer join myTable c on a.id=c.id and c.seqno = 3
where a.seqno = 1;
If SeqNo is open ended you can deploy a recursive cte:
;with anchor as (
select id, name, seqno
from myTable
where seqno=1)
, recursive as (
select id, name, seqno
from anchor
union all
select t.id, r.name + '/' + t.name, t.seqno
from myTable t
join recursive r on t.id = r.id and r.seqno+1 = t.seqno)
select id, name from recursive;
If you know SeqNo will never be more than 3:
select Id, Names = stuff(
max(case when SeqNo = 1 then '/'+Name else '' end)
+ max(case when SeqNo = 2 then '/'+Name else '' end)
+ max(case when SeqNo = 3 then '/'+Name else '' end)
, 1, 1, '')
from table1
group by Id
Otherwise, something like this is the generic solution to an arbitrary number of items:
select Id, Names = stuff((
select '/'+Name from table1 b
where a.Id = b.Id order by SeqNo
for xml path (''))
, 1, 1, '')
from table1 a
group by Id
Or write a CLR UDA.
Edit: had the wrong alias on the correlated table!
Edit2: another version, based on Remus's recursion example. I couldn't think of any way to select only the last recursion per Id, without aggregation or sorting. Anybody know?
;with
myTable as (
select * from (
values
(1, 1, 'RecordA')
, (2, 1, 'RecordB')
, (3, 1, 'RecordC')
, (1, 2, 'RecordD')
, (4, 1, 'RecordE')
, (5, 1, 'RecordF')
, (3, 2, 'RecordG')
) a (Id, SeqNo, Name)
)
, anchor as (
select id, name = convert(varchar(max),name), seqno
from myTable where seqno=1
)
, recursive as (
select id, name, seqno
from anchor
union all
select t.id, r.name + '/' + t.name, t.seqno
from myTable t
join recursive r on t.id = r.id and r.seqno+1 = t.seqno
)
select id, name = max(name)
from recursive
group by id;
---- without aggregation, we get 7 rows:
--select id, name
--from recursive;
The solution is good. I have a similar issue, but here I am using 2 different tables. ex:
table1
1 | 1 |
2 | 3 |
3 | 1 |
4 | 2 |
5 | 1 |
1 | 2 |
1 | 3 |
4 | 1 |
5 | 2 |
2 | 2 |
4 | 3 |
table2
1 | RecordA
2 | RecordB
3 | RecordC
I want to get the data from two tables and display in the below format.
1 | RecordA,RecordB,RecordC|
2 | RecordB,RecordC|
3 | RecordA |
4 | RecordA,RecordB,RecordC|
5 | RecordA,RecordB |