By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
460,043 Members | 1,126 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 460,043 IT Pros & Developers. It's quick & easy.

Combine multiple records into one row with multiple columns [SOLVED]

P: 63
I need to combine multiple records into one row with multiple columns for those records. Very similar to a crosstab query, but one that shows all the records, not a summary of them.

For example, if the table is like this:

Expand|Select|Wrap|Line Numbers
  1. Name     Month     Score
  2. John     Jan       92
  3. John     Jan       90
  4. John     Feb       97
  5. John     Feb       91
I want a query that gives these results like this:

Expand|Select|Wrap|Line Numbers
  1. Month     Name     Score1     Score2
  2. Jan       John     92         90
  3. Feb       John     97         91
Crosstab queries are set for summarizing data, but I don't want that--I just want ALL the scores laid out in a row for each person for each month.

I'm using Access 2007 and in this case, VBA is not an option.

Any help would be much appreciated.

EDIT: See complete solution down below.
Feb 18 '15 #1

✓ answered by Seth Schrock

So ignore the fix from the previous post. Try adding the following to the end of line 9 of your last code:
Expand|Select|Wrap|Line Numbers
  1.  AND T1.qID <= T2.qID

Share this Question
Share on Google+
27 Replies


Seth Schrock
Expert 2.5K+
P: 2,951
Can there be more than two scores per person per month? There is a SQL solution for that, but the more scores, the harder it is to code.
Feb 18 '15 #2

P: 63
It would be a maximum of 4 scores.
Feb 18 '15 #3

Seth Schrock
Expert 2.5K+
P: 2,951
Just an FYI, it is not a good idea to use Name or Month as field names as those are reserved words in Access. Access does permit you to use them, but it can sometimes cause problems.

Anyway, we first need to have an id field in your table. An autonumber field would be perfect for this. Then we need to create a querydef that puts a number beside each record to tell us if it is score 1, 2, 3 or 4. I'll assume a table name of tblScores.
Expand|Select|Wrap|Line Numbers
  1. SELECT ID
  2. , [Name]
  3. , [Month]
  4. , Score
  5. , DCount("*"
  6.     , "tblScores"
  7.     , "[Name] = '" & [Name] & 
  8.         "' And [Month] = '" & [Month] & 
  9.         "' And ID <= " & ID) 
  10.     As ScoreNum
  11. FROM tblScores
This should produce the results
Expand|Select|Wrap|Line Numbers
  1. ID    Name    Month    Score    ScoreNum
  2. 1     John    Jan      92       1
  3. 2     John    Jan      90       2
  4. 3     John    Feb      97       1
  5. 4     John    Feb      91       2
Save this query as qryScores. Now the query that gets the results that you want will have several subqueries to get the score numbers based on the ScoreNum field.
Expand|Select|Wrap|Line Numbers
  1. SELECT MQ.ID
  2. , MQ.[Name]
  3. , MQ.[Month]
  4. , S1.Score As Score1
  5. , S2.Score As Score2
  6. , S3.Score As Score3
  7. , S4.Score As Score4
  8. FROM qryScores As MQ 
  9. LEFT JOIN (SELECT ID, Score FROM qryScores WHERE ScoreNum = 1) As S1 ON MQ.ID = S1.ID
  10. LEFT JOIN (SELECT ID, Score FROM qryScores WHERE ScoreNum = 2) As S2 ON MQ.ID = S2.ID
  11. LEFT JOIN (SELECT ID, Score FROM qryScores WHERE ScoreNum = 3) As S3 ON MQ.ID = S3.ID
  12. LEFT JOIN (SELECT ID, Score FROM qryScores WHERE ScoreNum = 4) As S4 ON MQ.ID = S4.ID
Feb 18 '15 #4

jforbes
Expert 100+
P: 1,107
Nice Work! That is probably preferable to a Crosstab as you will have a predefined amount of columns.

But if you wanted to, since you already did the hard work of creating a RowNumber type column, you could use a Crosstab ;)
Expand|Select|Wrap|Line Numbers
  1. TRANSFORM First(qryScores.[Month]) AS Scores
  2. SELECT qryScores.[Name], First(qryScores.[Month]) AS ReportMonth
  3. FROM qryScores
  4. GROUP BY qryScores.[Name], qryScores.[Month]
  5. PIVOT qryScores.[ScoreNum]
Feb 18 '15 #5

P: 63
Thanks, Seth.

I know about reserved names and ID fields. It was just a simple example to illustrate what I was after.

I actually tried this method with the DCount function before, but it ran extremely slow due to the aggregate function. But I see a few differences in your function's criteria that I'll try out.

I'll test and post results.

Thanks!
Feb 18 '15 #6

Seth Schrock
Expert 2.5K+
P: 2,951
The only way that you can speed up the query is to reduce the number of records that it has to run the aggregate function on.
Feb 18 '15 #7

NeoPa
Expert Mod 15k+
P: 31,769
Actually Seth, you could link in the data as a separate data source rather than use a Domain Aggregate function call. The latter are notoriously inefficient and should be avoided where possible within SQL.

As J Forbes illustrates this can still be done using a cross-tab. I would expect that to work more efficiently as you're letting the SQL engine to the hard work - which is what it's designed for.
Feb 18 '15 #8

P: 63
NeoPa, could you please clarify or give an example of linking in the data as a separate data source?

Thanks.
Feb 19 '15 #9

P: 63
Seth, the performance is much better on the DCount function using the method in your example, thank you.

But I keep getting a "syntax error (missing operator)" on the second query example you gave with the subqueries. The error is in the section with the LEFT JOINS.

Any ideas?
Feb 19 '15 #10

Seth Schrock
Expert 2.5K+
P: 2,951
Try this
Expand|Select|Wrap|Line Numbers
  1. SELECT MQ.ID
  2. , MQ.[Name]
  3. , MQ.[Month]
  4. , S1.Score As Score1
  5. , S2.Score As Score2
  6. , S3.Score As Score3
  7. , S4.Score As Score4
  8. FROM ((((qryScores As MQ 
  9. LEFT JOIN (SELECT ID, Score FROM qryScores WHERE ScoreNum = 1) As S1 ON MQ.ID = S1.ID)
  10. LEFT JOIN (SELECT ID, Score FROM qryScores WHERE ScoreNum = 2) As S2 ON MQ.ID = S2.ID)
  11. LEFT JOIN (SELECT ID, Score FROM qryScores WHERE ScoreNum = 3) As S3 ON MQ.ID = S3.ID)
  12. LEFT JOIN (SELECT ID, Score FROM qryScores WHERE ScoreNum = 4) As S4 ON MQ.ID = S4.ID)
Access sometimes doesn't like multiple joins without the parenthesis.
Feb 19 '15 #11

P: 63
Thanks.
That eliminated that error, but now it's prompting for a Score parameter when I run the query.
Feb 19 '15 #12

Seth Schrock
Expert 2.5K+
P: 2,951
Can you copy and paste into this thread what you have just in case there is an error somewhere?
Feb 19 '15 #13

P: 63
Expand|Select|Wrap|Line Numbers
  1. SELECT MQ.qID
  2. , MQ.[sMonth]
  3. , MQ.[rName]
  4. , MQ.MgrName
  5. , S1.Score AS [Score 1]
  6. , S2.Score AS [Score 2]
  7. , S3.Score AS [Score 3]
  8. , S4.Score AS [Score 4]
  9. FROM (((([ScoreCount] AS MQ 
  10. LEFT JOIN (SELECT qID, Score FROM [ScoreCount] WHERE ScoreNum = 1) As S1 ON MQ.qID = S1.qID) 
  11. LEFT JOIN (SELECT qID, Score FROM [ScoreCount] WHERE ScoreNum = 2) As S2 ON MQ.qID = S2.qID) 
  12. LEFT JOIN (SELECT qID, Score FROM [ScoreCount] WHERE ScoreNum = 3) As S3 ON MQ.qID = S3.qID) 
  13. LEFT JOIN (SELECT qID, Score FROM [ScoreCount] WHERE ScoreNum = 4) As S4 ON MQ.qID = S4.qID);
  14.  
Feb 19 '15 #14

Seth Schrock
Expert 2.5K+
P: 2,951
Well, nothing is obviously wrong. Try removing lines 6 through 8 and see if you get an error. Keep adding lines one at a time until you do receive the error and then let us know which one errors out.
Feb 19 '15 #15

P: 63
I tried that and I still get the prompt to enter a Parameter Value for Score.

I also removed lines 10-12 but still get the prompt.

Any other ideas?
Feb 20 '15 #16

Seth Schrock
Expert 2.5K+
P: 2,951
It sounds like the field name Score isn't accurate.
Feb 20 '15 #17

P: 63
Okay, it was my fault. Had the Score field name wrong. Fixed that and I don't get the prompt, but it's not giving the expected results.

It's just listing the first score and showing a different row for each record still.

And the final query runs extremely slow.

Any ideas? I really appreciate your help on this.
Feb 20 '15 #18

Seth Schrock
Expert 2.5K+
P: 2,951
Did you start back with the query from post #14 and fix the score field name? As NeoPa and JForbes said, a cross tab might be better since you have the ScoreNum field.
Feb 20 '15 #19

P: 63
Yes fixed the Score field name.

Trying the crosstab again. I'll post results.

Do you know what NeoPa meant by "link in the data as a separate data source rather than use a Domain Aggregate function call" ?

Sounds like it might help the performance on the ScoreCount query that is getting the ScoreNum numbering.
Feb 20 '15 #20

Seth Schrock
Expert 2.5K+
P: 2,951
You would create another subquery and use the Count() function. Try the following for your ScoreCount query:
Expand|Select|Wrap|Line Numbers
  1. SELECT T1.qID
  2. , T1.sMonth
  3. , T1.rName
  4. , T1.MgrName
  5. , T1.Score
  6. , Count(T2.*) As ScoreNum
  7. FROM tblScores As T1 LEFT JOIN
  8.     (SELECT * FROM tblScores) As T2 
  9.     ON T1.rName = T2.rName AND T1.sMonth = T2.sMonth
Feb 20 '15 #21

P: 63
I get this error when saving the query: "Syntax error in query expression 'Count(T2.*)'"

So I tried putting a field name: 'Count(T2.qID)' and that got past the error.

But when I ran the query, I got an error saying it did not include the specified expression 'qID' as part of an aggregate function--in reference to the qID in the SELECT statement at the beginning.

I really appreciate the help on this because I really want this to work. Using the other way with the DCount takes 45 seconds just to open that query, so I'm hoping the performance with this method can cut that down.
Feb 20 '15 #22

P: 63
I added GROUP BY to the end and that solved the error. But it's just showing a total count for each person for each month on each row--not the incremental count.

Here's the query now:
Expand|Select|Wrap|Line Numbers
  1. SELECT T1.qID
  2. , T1.sMonth
  3. , T1.rName
  4. , T1.MgrName
  5. , T1.Score
  6. , Count(T2.qID) As ScoreNum
  7. FROM tblScores As T1 LEFT JOIN
  8.     (SELECT * FROM tblScores) As T2 
  9.     ON T1.rName = T2.rName AND T1.sMonth = T2.sMonth
  10. GROUP BY T1.qID, T1.sMonth, T1.rName, T1.MgrName, T1.qScore
Produces results like this:
Expand|Select|Wrap|Line Numbers
  1. qID  sMonth  rName    MgrName    Score  ScoreNum
  2. 1    Jan     John     George     92     3
  3. 2    Jan     John     George     94     3
  4. 3    Jan     John     George     98     3
  5. 4    Feb     John     George     97     2
  6. 5    Feb     John     George     91     2
Feb 20 '15 #23

Seth Schrock
Expert 2.5K+
P: 2,951
Try removing the qID field from both the SELECT and GROUP BY clauses.

***Edit*** The above won't work. The Count function needs modified to only get records with lesser qID values that have matching sMonth and rName fields. I'm looking into this.
Feb 20 '15 #24

Seth Schrock
Expert 2.5K+
P: 2,951
So ignore the fix from the previous post. Try adding the following to the end of line 9 of your last code:
Expand|Select|Wrap|Line Numbers
  1.  AND T1.qID <= T2.qID
Feb 20 '15 #25

P: 63
That did it! And the performance is WAY better.

Thank you, Seth!

Here is the complete solution:

Step 1: Created a SourceCount query to provide numbered results for each person in each time period:
Expand|Select|Wrap|Line Numbers
  1. SELECT T1.qID
  2. , T1.sMonth
  3. , T1.rName
  4. , T1.MgrName
  5. , T1.Score
  6. , Count(T2.qID) As ScoreNum
  7. FROM tblScores As T1 LEFT JOIN
  8.     (SELECT * FROM tblScores) As T2 
  9.     ON T1.rName = T2.rName AND T1.sMonth = T2.sMonth AND T1.qID <= T2.qID 
  10. GROUP BY T1.qID, T1.sMonth, T1.rName, T1.MgrName, T1.qScore

Which gives results like this:
Expand|Select|Wrap|Line Numbers
  1. qID  sMonth  rName    MgrName    Score  ScoreNum
  2. 1    Jan     John     George     92     2
  3. 2    Jan     John     George     94     3
  4. 3    Jan     John     George     98     1
  5. 4    Feb     John     George     97     1
  6. 5    Feb     John     George     91     2

Step 2: Create a crosstab query:
Expand|Select|Wrap|Line Numbers
  1. TRANSFORM First([ScoreCount].qScore) AS Score
  2. SELECT First([ScoreCount].[sMonth]), [ScoreCount].[rName], [ScoreCount].MgrName
  3. FROM ScoreCount
  4. GROUP BY [ScoreCount].[sMonth], [ScoreCount].[rName], [ScoreCount].MgrName
  5. PIVOT [ScoreCount].[ScoreNum];

Which gives the desired final result:
Expand|Select|Wrap|Line Numbers
  1. sMonth  rName    MgrName    Score1  Score2  Score3  
  2. Jan     John     George     98      92      94
  3. Feb     John     George     97      91
  4.  
Thanks again for everyone's help!
Feb 20 '15 #26

Seth Schrock
Expert 2.5K+
P: 2,951
Glad you got it to work. Good luck on your project.
Feb 20 '15 #27

NeoPa
Expert Mod 15k+
P: 31,769
Congratulations Seth. Good work.

@Postman.
Please accept my apologies for leaving you without my response for so long. Work is keeping me very busy ATM and I don't get to visit as often as I'd like to. This is one I would have enjoyed too.

Luckily our Seth is getting more and more experienced and was able to resolve it for you.
Mar 2 '15 #28

Post your reply

Sign in to post your reply or Sign up for a free account.