Pivot the first ten values of a table in SQL Server
up vote
2
down vote
favorite
Consider two tables, one containing the details of work (cases
) to be carried out, and one describing what work has been performed on each cases (activities
).
The cases
table is roughly 20million rows.
CREATE TABLE #cases
(CASEID int, DETAILS varchar(1))
INSERT INTO #cases
(CASEID, DETAILS)
VALUES
(1, 'A'),
(2, 'B'),
(3, 'C')
;
The activities
table is roughly 180million rows.
CREATE TABLE #activities
(ACTIVITYID int, CASEID int, CODE varchar(3), STARTDATE date)
INSERT INTO #activities
(ACTIVITYID, CASEID, CODE, STARTDATE)
VALUES
(1, 1, '00', '2018-01-01'),
(2, 1, '110', '2018-02-01'),
(3, 1, '900', '2018-03-01'),
(4, 1, '910', '2018-05-01'),
(5, 1, '920', '2018-04-01'),
(6, 2, '900', '2018-01-01'),
(7, 2, '110', '2018-02-01'),
(8, 2, '900', '2018-03-01'),
(9, 3, '00', '2018-01-01'),
(10, 3, '123', '2018-02-01')
;
It is not ideal - but I need to find a way to create a wide table containing case details, and then details of the first 10 activities with a code in the range 900-999.
Some cases will have more than 10 activities in that range - some cases will have none.
The output I am looking for is something along the lines of:
CASEID DETAILS CODE1st900 STARTDATE1st900 CODE2nd900 STARTDATE2nd900 CODE3rd900 STARTDATE3rd900
1 A 900 01/01/2018 00:00:00 920 01/04/2018 00:00:00 910 01/05/2018 00:00:00
2 B 900 01/01/2018 00:00:00 900 01/03/2018 00:00:00
3 C
Ultimately I am not sure whether some kind of clever pivot is the best approach here, joining each set of values with a subquery, or a cursor which is typically how my organisation has created this sort of data previously.
DBFiddle to play with here:
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=5eef2de402726218a8472880ef0bab85
sql sql-server tsql
add a comment |
up vote
2
down vote
favorite
Consider two tables, one containing the details of work (cases
) to be carried out, and one describing what work has been performed on each cases (activities
).
The cases
table is roughly 20million rows.
CREATE TABLE #cases
(CASEID int, DETAILS varchar(1))
INSERT INTO #cases
(CASEID, DETAILS)
VALUES
(1, 'A'),
(2, 'B'),
(3, 'C')
;
The activities
table is roughly 180million rows.
CREATE TABLE #activities
(ACTIVITYID int, CASEID int, CODE varchar(3), STARTDATE date)
INSERT INTO #activities
(ACTIVITYID, CASEID, CODE, STARTDATE)
VALUES
(1, 1, '00', '2018-01-01'),
(2, 1, '110', '2018-02-01'),
(3, 1, '900', '2018-03-01'),
(4, 1, '910', '2018-05-01'),
(5, 1, '920', '2018-04-01'),
(6, 2, '900', '2018-01-01'),
(7, 2, '110', '2018-02-01'),
(8, 2, '900', '2018-03-01'),
(9, 3, '00', '2018-01-01'),
(10, 3, '123', '2018-02-01')
;
It is not ideal - but I need to find a way to create a wide table containing case details, and then details of the first 10 activities with a code in the range 900-999.
Some cases will have more than 10 activities in that range - some cases will have none.
The output I am looking for is something along the lines of:
CASEID DETAILS CODE1st900 STARTDATE1st900 CODE2nd900 STARTDATE2nd900 CODE3rd900 STARTDATE3rd900
1 A 900 01/01/2018 00:00:00 920 01/04/2018 00:00:00 910 01/05/2018 00:00:00
2 B 900 01/01/2018 00:00:00 900 01/03/2018 00:00:00
3 C
Ultimately I am not sure whether some kind of clever pivot is the best approach here, joining each set of values with a subquery, or a cursor which is typically how my organisation has created this sort of data previously.
DBFiddle to play with here:
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=5eef2de402726218a8472880ef0bab85
sql sql-server tsql
add a comment |
up vote
2
down vote
favorite
up vote
2
down vote
favorite
Consider two tables, one containing the details of work (cases
) to be carried out, and one describing what work has been performed on each cases (activities
).
The cases
table is roughly 20million rows.
CREATE TABLE #cases
(CASEID int, DETAILS varchar(1))
INSERT INTO #cases
(CASEID, DETAILS)
VALUES
(1, 'A'),
(2, 'B'),
(3, 'C')
;
The activities
table is roughly 180million rows.
CREATE TABLE #activities
(ACTIVITYID int, CASEID int, CODE varchar(3), STARTDATE date)
INSERT INTO #activities
(ACTIVITYID, CASEID, CODE, STARTDATE)
VALUES
(1, 1, '00', '2018-01-01'),
(2, 1, '110', '2018-02-01'),
(3, 1, '900', '2018-03-01'),
(4, 1, '910', '2018-05-01'),
(5, 1, '920', '2018-04-01'),
(6, 2, '900', '2018-01-01'),
(7, 2, '110', '2018-02-01'),
(8, 2, '900', '2018-03-01'),
(9, 3, '00', '2018-01-01'),
(10, 3, '123', '2018-02-01')
;
It is not ideal - but I need to find a way to create a wide table containing case details, and then details of the first 10 activities with a code in the range 900-999.
Some cases will have more than 10 activities in that range - some cases will have none.
The output I am looking for is something along the lines of:
CASEID DETAILS CODE1st900 STARTDATE1st900 CODE2nd900 STARTDATE2nd900 CODE3rd900 STARTDATE3rd900
1 A 900 01/01/2018 00:00:00 920 01/04/2018 00:00:00 910 01/05/2018 00:00:00
2 B 900 01/01/2018 00:00:00 900 01/03/2018 00:00:00
3 C
Ultimately I am not sure whether some kind of clever pivot is the best approach here, joining each set of values with a subquery, or a cursor which is typically how my organisation has created this sort of data previously.
DBFiddle to play with here:
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=5eef2de402726218a8472880ef0bab85
sql sql-server tsql
Consider two tables, one containing the details of work (cases
) to be carried out, and one describing what work has been performed on each cases (activities
).
The cases
table is roughly 20million rows.
CREATE TABLE #cases
(CASEID int, DETAILS varchar(1))
INSERT INTO #cases
(CASEID, DETAILS)
VALUES
(1, 'A'),
(2, 'B'),
(3, 'C')
;
The activities
table is roughly 180million rows.
CREATE TABLE #activities
(ACTIVITYID int, CASEID int, CODE varchar(3), STARTDATE date)
INSERT INTO #activities
(ACTIVITYID, CASEID, CODE, STARTDATE)
VALUES
(1, 1, '00', '2018-01-01'),
(2, 1, '110', '2018-02-01'),
(3, 1, '900', '2018-03-01'),
(4, 1, '910', '2018-05-01'),
(5, 1, '920', '2018-04-01'),
(6, 2, '900', '2018-01-01'),
(7, 2, '110', '2018-02-01'),
(8, 2, '900', '2018-03-01'),
(9, 3, '00', '2018-01-01'),
(10, 3, '123', '2018-02-01')
;
It is not ideal - but I need to find a way to create a wide table containing case details, and then details of the first 10 activities with a code in the range 900-999.
Some cases will have more than 10 activities in that range - some cases will have none.
The output I am looking for is something along the lines of:
CASEID DETAILS CODE1st900 STARTDATE1st900 CODE2nd900 STARTDATE2nd900 CODE3rd900 STARTDATE3rd900
1 A 900 01/01/2018 00:00:00 920 01/04/2018 00:00:00 910 01/05/2018 00:00:00
2 B 900 01/01/2018 00:00:00 900 01/03/2018 00:00:00
3 C
Ultimately I am not sure whether some kind of clever pivot is the best approach here, joining each set of values with a subquery, or a cursor which is typically how my organisation has created this sort of data previously.
DBFiddle to play with here:
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=5eef2de402726218a8472880ef0bab85
sql sql-server tsql
sql sql-server tsql
edited Nov 19 at 8:46
asked Nov 19 at 8:38
Ross
19011
19011
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
up vote
1
down vote
accepted
Given the volume of data, I would do this using apply
:
select c.*, a.*
from cases c outer apply
(select max(case when seqnum = 1 then code end) as code_1,
max(case when seqnum = 1 then startdate end) as startdate_1,
max(case when seqnum = 2 then code end) as code_2,
max(case when seqnum = 2 then startdate end) as startdate_2,
. . .
from (select top (10) a.*,
row_number() over (partition by a.caseid order by a.startdate) as seqnum
from activities a
where a.caseid = c.caseid and
a.code between 900 and 999
) a
) a;
This should have much better performance than solutions using pivot
or group by
, because the data from cases
does not need to be aggregated. The aggregations are taking place ten rows at a time as needed.
add a comment |
up vote
3
down vote
Usually we'd prefer PIVOT
but there's no syntax currently for pivoting multiple columns simultaneously. So we'll use conditional aggregation instead:
declare @cases table (CASEID int, DETAILS varchar(1))
INSERT INTO @cases (CASEID, DETAILS) VALUES
(1, 'A'),
(2, 'B'),
(3, 'C');
declare @activities table (ACTIVITYID int, CASEID int, CODE varchar(3), STARTDATE date)
INSERT INTO @activities (ACTIVITYID, CASEID, CODE, STARTDATE) VALUES
(1, 1, '00', '2018-01-01'),
(2, 1, '110', '2018-02-01'),
(3, 1, '900', '2018-03-01'),
(4, 1, '910', '2018-05-01'),
(5, 1, '920', '2018-04-01'),
(6, 2, '900', '2018-01-01'),
(7, 2, '110', '2018-02-01'),
(8, 2, '900', '2018-03-01'),
(9, 3, '00', '2018-01-01'),
(10, 3, '123', '2018-02-01');
select
c.CASEID,
c.DETAILS,
MAX(CASE WHEN rn=1 THEN CODE END) as Code1st,
MAX(CASE WHEN rn=1 THEN STARTDATE END) as Start1st,
MAX(CASE WHEN rn=2 THEN CODE END) as Code2nd,
MAX(CASE WHEN rn=2 THEN STARTDATE END) as Start2nd
from
@cases c
left join
(select *,ROW_NUMBER() OVER (PARTITION BY CASEID ORDER BY STARTDATE) rn
from @activities
where CODE BETWEEN 900 and 999) a
on
c.CASEID = a.CASEID and
a.rn <= 10
group by c.CASEID,c.DETAILS
I've shown pivoting the first pair of pairs above. Hopefully you can see how it extends for the remaining 8.
add a comment |
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
accepted
Given the volume of data, I would do this using apply
:
select c.*, a.*
from cases c outer apply
(select max(case when seqnum = 1 then code end) as code_1,
max(case when seqnum = 1 then startdate end) as startdate_1,
max(case when seqnum = 2 then code end) as code_2,
max(case when seqnum = 2 then startdate end) as startdate_2,
. . .
from (select top (10) a.*,
row_number() over (partition by a.caseid order by a.startdate) as seqnum
from activities a
where a.caseid = c.caseid and
a.code between 900 and 999
) a
) a;
This should have much better performance than solutions using pivot
or group by
, because the data from cases
does not need to be aggregated. The aggregations are taking place ten rows at a time as needed.
add a comment |
up vote
1
down vote
accepted
Given the volume of data, I would do this using apply
:
select c.*, a.*
from cases c outer apply
(select max(case when seqnum = 1 then code end) as code_1,
max(case when seqnum = 1 then startdate end) as startdate_1,
max(case when seqnum = 2 then code end) as code_2,
max(case when seqnum = 2 then startdate end) as startdate_2,
. . .
from (select top (10) a.*,
row_number() over (partition by a.caseid order by a.startdate) as seqnum
from activities a
where a.caseid = c.caseid and
a.code between 900 and 999
) a
) a;
This should have much better performance than solutions using pivot
or group by
, because the data from cases
does not need to be aggregated. The aggregations are taking place ten rows at a time as needed.
add a comment |
up vote
1
down vote
accepted
up vote
1
down vote
accepted
Given the volume of data, I would do this using apply
:
select c.*, a.*
from cases c outer apply
(select max(case when seqnum = 1 then code end) as code_1,
max(case when seqnum = 1 then startdate end) as startdate_1,
max(case when seqnum = 2 then code end) as code_2,
max(case when seqnum = 2 then startdate end) as startdate_2,
. . .
from (select top (10) a.*,
row_number() over (partition by a.caseid order by a.startdate) as seqnum
from activities a
where a.caseid = c.caseid and
a.code between 900 and 999
) a
) a;
This should have much better performance than solutions using pivot
or group by
, because the data from cases
does not need to be aggregated. The aggregations are taking place ten rows at a time as needed.
Given the volume of data, I would do this using apply
:
select c.*, a.*
from cases c outer apply
(select max(case when seqnum = 1 then code end) as code_1,
max(case when seqnum = 1 then startdate end) as startdate_1,
max(case when seqnum = 2 then code end) as code_2,
max(case when seqnum = 2 then startdate end) as startdate_2,
. . .
from (select top (10) a.*,
row_number() over (partition by a.caseid order by a.startdate) as seqnum
from activities a
where a.caseid = c.caseid and
a.code between 900 and 999
) a
) a;
This should have much better performance than solutions using pivot
or group by
, because the data from cases
does not need to be aggregated. The aggregations are taking place ten rows at a time as needed.
edited Nov 20 at 0:24
Ross
19011
19011
answered Nov 19 at 12:20
Gordon Linoff
748k34285391
748k34285391
add a comment |
add a comment |
up vote
3
down vote
Usually we'd prefer PIVOT
but there's no syntax currently for pivoting multiple columns simultaneously. So we'll use conditional aggregation instead:
declare @cases table (CASEID int, DETAILS varchar(1))
INSERT INTO @cases (CASEID, DETAILS) VALUES
(1, 'A'),
(2, 'B'),
(3, 'C');
declare @activities table (ACTIVITYID int, CASEID int, CODE varchar(3), STARTDATE date)
INSERT INTO @activities (ACTIVITYID, CASEID, CODE, STARTDATE) VALUES
(1, 1, '00', '2018-01-01'),
(2, 1, '110', '2018-02-01'),
(3, 1, '900', '2018-03-01'),
(4, 1, '910', '2018-05-01'),
(5, 1, '920', '2018-04-01'),
(6, 2, '900', '2018-01-01'),
(7, 2, '110', '2018-02-01'),
(8, 2, '900', '2018-03-01'),
(9, 3, '00', '2018-01-01'),
(10, 3, '123', '2018-02-01');
select
c.CASEID,
c.DETAILS,
MAX(CASE WHEN rn=1 THEN CODE END) as Code1st,
MAX(CASE WHEN rn=1 THEN STARTDATE END) as Start1st,
MAX(CASE WHEN rn=2 THEN CODE END) as Code2nd,
MAX(CASE WHEN rn=2 THEN STARTDATE END) as Start2nd
from
@cases c
left join
(select *,ROW_NUMBER() OVER (PARTITION BY CASEID ORDER BY STARTDATE) rn
from @activities
where CODE BETWEEN 900 and 999) a
on
c.CASEID = a.CASEID and
a.rn <= 10
group by c.CASEID,c.DETAILS
I've shown pivoting the first pair of pairs above. Hopefully you can see how it extends for the remaining 8.
add a comment |
up vote
3
down vote
Usually we'd prefer PIVOT
but there's no syntax currently for pivoting multiple columns simultaneously. So we'll use conditional aggregation instead:
declare @cases table (CASEID int, DETAILS varchar(1))
INSERT INTO @cases (CASEID, DETAILS) VALUES
(1, 'A'),
(2, 'B'),
(3, 'C');
declare @activities table (ACTIVITYID int, CASEID int, CODE varchar(3), STARTDATE date)
INSERT INTO @activities (ACTIVITYID, CASEID, CODE, STARTDATE) VALUES
(1, 1, '00', '2018-01-01'),
(2, 1, '110', '2018-02-01'),
(3, 1, '900', '2018-03-01'),
(4, 1, '910', '2018-05-01'),
(5, 1, '920', '2018-04-01'),
(6, 2, '900', '2018-01-01'),
(7, 2, '110', '2018-02-01'),
(8, 2, '900', '2018-03-01'),
(9, 3, '00', '2018-01-01'),
(10, 3, '123', '2018-02-01');
select
c.CASEID,
c.DETAILS,
MAX(CASE WHEN rn=1 THEN CODE END) as Code1st,
MAX(CASE WHEN rn=1 THEN STARTDATE END) as Start1st,
MAX(CASE WHEN rn=2 THEN CODE END) as Code2nd,
MAX(CASE WHEN rn=2 THEN STARTDATE END) as Start2nd
from
@cases c
left join
(select *,ROW_NUMBER() OVER (PARTITION BY CASEID ORDER BY STARTDATE) rn
from @activities
where CODE BETWEEN 900 and 999) a
on
c.CASEID = a.CASEID and
a.rn <= 10
group by c.CASEID,c.DETAILS
I've shown pivoting the first pair of pairs above. Hopefully you can see how it extends for the remaining 8.
add a comment |
up vote
3
down vote
up vote
3
down vote
Usually we'd prefer PIVOT
but there's no syntax currently for pivoting multiple columns simultaneously. So we'll use conditional aggregation instead:
declare @cases table (CASEID int, DETAILS varchar(1))
INSERT INTO @cases (CASEID, DETAILS) VALUES
(1, 'A'),
(2, 'B'),
(3, 'C');
declare @activities table (ACTIVITYID int, CASEID int, CODE varchar(3), STARTDATE date)
INSERT INTO @activities (ACTIVITYID, CASEID, CODE, STARTDATE) VALUES
(1, 1, '00', '2018-01-01'),
(2, 1, '110', '2018-02-01'),
(3, 1, '900', '2018-03-01'),
(4, 1, '910', '2018-05-01'),
(5, 1, '920', '2018-04-01'),
(6, 2, '900', '2018-01-01'),
(7, 2, '110', '2018-02-01'),
(8, 2, '900', '2018-03-01'),
(9, 3, '00', '2018-01-01'),
(10, 3, '123', '2018-02-01');
select
c.CASEID,
c.DETAILS,
MAX(CASE WHEN rn=1 THEN CODE END) as Code1st,
MAX(CASE WHEN rn=1 THEN STARTDATE END) as Start1st,
MAX(CASE WHEN rn=2 THEN CODE END) as Code2nd,
MAX(CASE WHEN rn=2 THEN STARTDATE END) as Start2nd
from
@cases c
left join
(select *,ROW_NUMBER() OVER (PARTITION BY CASEID ORDER BY STARTDATE) rn
from @activities
where CODE BETWEEN 900 and 999) a
on
c.CASEID = a.CASEID and
a.rn <= 10
group by c.CASEID,c.DETAILS
I've shown pivoting the first pair of pairs above. Hopefully you can see how it extends for the remaining 8.
Usually we'd prefer PIVOT
but there's no syntax currently for pivoting multiple columns simultaneously. So we'll use conditional aggregation instead:
declare @cases table (CASEID int, DETAILS varchar(1))
INSERT INTO @cases (CASEID, DETAILS) VALUES
(1, 'A'),
(2, 'B'),
(3, 'C');
declare @activities table (ACTIVITYID int, CASEID int, CODE varchar(3), STARTDATE date)
INSERT INTO @activities (ACTIVITYID, CASEID, CODE, STARTDATE) VALUES
(1, 1, '00', '2018-01-01'),
(2, 1, '110', '2018-02-01'),
(3, 1, '900', '2018-03-01'),
(4, 1, '910', '2018-05-01'),
(5, 1, '920', '2018-04-01'),
(6, 2, '900', '2018-01-01'),
(7, 2, '110', '2018-02-01'),
(8, 2, '900', '2018-03-01'),
(9, 3, '00', '2018-01-01'),
(10, 3, '123', '2018-02-01');
select
c.CASEID,
c.DETAILS,
MAX(CASE WHEN rn=1 THEN CODE END) as Code1st,
MAX(CASE WHEN rn=1 THEN STARTDATE END) as Start1st,
MAX(CASE WHEN rn=2 THEN CODE END) as Code2nd,
MAX(CASE WHEN rn=2 THEN STARTDATE END) as Start2nd
from
@cases c
left join
(select *,ROW_NUMBER() OVER (PARTITION BY CASEID ORDER BY STARTDATE) rn
from @activities
where CODE BETWEEN 900 and 999) a
on
c.CASEID = a.CASEID and
a.rn <= 10
group by c.CASEID,c.DETAILS
I've shown pivoting the first pair of pairs above. Hopefully you can see how it extends for the remaining 8.
answered Nov 19 at 8:46
Damien_The_Unbeliever
191k17243329
191k17243329
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53370963%2fpivot-the-first-ten-values-of-a-table-in-sql-server%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown