Cross Tabs and Pivots, Part 1 – Converting Rows to Columns

  • Comments posted to this topic are about the item Cross Tabs and Pivots, Part 1 – Converting Rows to Columns

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • [p]I hope I'm not mentioning anything that you are covering in subsequent articles, but when you need row and column totals, averages, or other aggregations, then suddenly the CUBE and ROLLUP operators start to become very useful. To take your example....[/p]

    [font="Courier New"]

    SELECT

       COALESCE(CONVERT(CHAR(4),YEAR),'Sum'),

       STR(SUM(CASE WHEN Quarter = 1 THEN Amount ELSE 0 END),5,1) AS [1st Qtr],

       STR(SUM(CASE WHEN Quarter = 2 THEN Amount ELSE 0 END),5,1) AS [2nd Qtr],

       STR(SUM(CASE WHEN Quarter = 3 THEN Amount ELSE 0 END),5,1) AS [3rd Qtr],

       STR(SUM(CASE WHEN Quarter = 4 THEN Amount ELSE 0 END),5,1) AS [4th Qtr],

            STR(SUM(Amount),5,1) AS Total

    FROM #SomeTable1

    GROUP BY YEAR WITH ROLLUP ORDER BY GROUPING(YEAR)

    /*

         1st Qtr 2nd Qtr 3rd Qtr 4th Qtr Total

    ---- ------- ------- ------- ------- -----

    2006   1.1     1.2     1.3     1.4     5.0

    2007   2.1     2.2     2.3     2.4     9.0

    2008   1.5     0.0     2.3     1.9     5.7

    Sum    4.7     3.4     5.9     5.7    19.7

    */

    [/font]

    [p] I love crosstabs and pivot-tables. Soothing, they are, like knitting.[/p]

    Best wishes,
    Phil Factor

  • That's interesting. I had my doubts as to how useful pivot might be. It's good to see it backed up by examples and statistics.

    I'm looking forward to what you have to say about dynamic cross tabs. I recently had to do a cross tab for an electronic voting system using proportional representation where each election can have a different number of candidates. The only solution I could come up with in the time available works but is so complicated as to be ridiculous.

  • Jeff Moden (8/18/2008)


    Comments posted to this topic are about the item

    What a performance! Access SQL had the Pivot command from 1992, it's taken 13 years for SQL proper to catch up. About to convert an Access db to a SQL Express back-end. I imagine the X-tab queries will be a major nightmare.

    pj

  • Phil Factor (8/19/2008)


    [p]I hope I'm not mentioning anything that you are covering in subsequent articles, but when you need row and column totals, averages, or other aggregations, then suddenly the CUBE and ROLLUP operators start to become very useful. To take your example....[/p]

    Actually, you did, Phil... I'm devestated... 😉

    True enough... that will be in a subsequent article, but you've stolen no thunder. Thank you for the feedback ol' friend.

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • Peter Smith (8/19/2008)


    Jeff Moden (8/18/2008)


    Comments posted to this topic are about the item

    What a performance! Access SQL had the Pivot command from 1992, it's taken 13 years for SQL proper to catch up. About to convert an Access db to a SQL Express back-end. I imagine the X-tab queries will be a major nightmare.

    pj

    I agree... thanks for the feedback, Peter. You'd think that if MS wanted to make it easier, they'd have come up with something a lot easier than Pivot syntax. What would be interesting would be to do a million row test on manual cross-tab vs the automatic cross-tab functionality in Access. If it's done right, the automatic cross-tab should should win by a significant margin.

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • Jeff, I've copied your code. Maybe it will reduce the bad language as the upscaling to SQL takes place. Thanks for the article.

  • gr8 article Jeff. Keep them coming.

    "Keep Trying"

  • That was an amazing article. :w00t: Thanks so much, Jeff.

    Paul DB

  • Hey Jeff, good article. It's funny I have answered several forum questions recently using a Case embedded in an aggregate. They weren't crosstabs exactly, but similar. So, after having answered these questions I decided to post it on my blog, hoping someone might stumble upon it on a search. I was even considering submitting an article to SSC on it, but you beat me to the punch. At least the info is out there and with much more performance testing done on it than I would have done. Looking forward to the rest of the series.

  • Really nice to read, Jeff, and absolutely spot on.

    I've had to do pivots in the last couple of weeks, with an unknown number of output (transposed) columns with unknown names, as in the following. It will be interesting to see your solution, you have a knack of coming up with "why the heck didn't I think of that?" solutions!

    Cheers

    ChrisM

    SET NOCOUNT ON

    CREATE TABLE #EDIInvoices (HospitalName VARCHAR(20), Purchaser VARCHAR(20), Sales money)

    INSERT INTO #EDIInvoices (HospitalName, Purchaser, Sales)

    SELECT 'Guy''s', 'BUPA', 10001 UNION ALL

    SELECT 'Guy''s', 'Microsoft', 10002 UNION ALL

    SELECT 'Guy''s', 'NIH', 10003 UNION ALL

    SELECT 'Tommy''s', 'BUPA', 20001 UNION ALL

    SELECT 'Tommy''s', 'Microsoft', 20002 UNION ALL

    SELECT 'Tommy''s', 'NIH', 20003 UNION ALL

    SELECT 'Tommy''s', 'Cornhill', 20004 UNION ALL

    SELECT 'Raigmore', 'BUPA', 30001 UNION ALL

    SELECT 'Raigmore', 'Microsoft', 30002 UNION ALL

    SELECT 'Raigmore', 'NIH', 30003 UNION ALL

    SELECT 'Raigmore', 'Cornhill', 30004 UNION ALL

    SELECT 'Raigmore', 'HPA', 30005 UNION ALL

    SELECT 'Raigmore', 'RaigmoreOnly', 30006 UNION ALL

    SELECT 'Yorkhill', 'BUPA', 40001 UNION ALL

    SELECT 'Yorkhill', 'Microsoft', 40002 UNION ALL

    SELECT 'Yorkhill', 'Cornhill', 40004 UNION ALL

    SELECT 'Yorkhill', 'HPA', 40005 UNION ALL

    SELECT 'Yorkhill', 'YorkhillOnly', 40007

    --SELECT * FROM #EDIInvoices -- Sanity check

    -- PIVOT the results

    DECLARE @SQLstr VARCHAR(5000)

    SET @SQLstr = ''

    SELECT @SQLstr = @SQLstr + CHAR(10) + ' MAX(CASE Purchaser WHEN ''' + Purchaser + ''' THEN Sales ELSE 0 END) AS [' + Purchaser + '], '

    FROM (SELECT Purchaser FROM #EDIInvoices GROUP BY Purchaser) d

    SET @SQLstr = 'SELECT HospitalName, ' + REVERSE(STUFF(REVERSE(@SQLstr),1,2,'')) + ' ' + CHAR(10) + 'INTO ##EDIInvoicesPivot FROM #EDIInvoices GROUP BY HospitalName'

    PRINT @SQLstr -- sanity check

    EXECUTE (@SQLstr)

    SELECT * FROM ##EDIInvoicesPivot

    DROP TABLE #EDIInvoices

    DROP TABLE ##EDIInvoicesPivot

    “Write the query the simplest way. If through testing it becomes clear that the performance is inadequate, consider alternative query forms.” - Gail Shaw

    For fast, accurate and documented assistance in answering your questions, please read this article.
    Understanding and using APPLY, (I) and (II) Paul White
    Hidden RBAR: Triangular Joins / The "Numbers" or "Tally" Table: What it is and how it replaces a loop Jeff Moden

  • Check out this article on Dynamic Crosstabs by Jeff Smith:

    http://weblogs.sqlteam.com/jeffs/archive/2005/05/02/4842.aspx

    -smc

  • I am interested as to how or if we can pull a cross-tab or pivot dynamically. I have a query that can have an undetermined amount of comments associated to one job. In the query I need to show these comments in one line.

    SET NOCOUNT ON

    CREATE TABLE #BidComments (BidId int, Comment VARCHAR(varchar(max))

    INSERT INTO #BidComments (BidId , Comment)

    SELECT 1, 'This is comment 1 for bid 1' UNION ALL

    SELECT 1, 'This is comment 2 for bid 1' UNION ALL

    SELECT 1, 'This is comment 3 for bid 1' UNION ALL

    SELECT 2, 'This is comment 1 for bid 2' UNION ALL

    SELECT 2, 'This is the second comment for bid 2' UNION ALL

    SELECT 2, 'Here we have the third comment for bid2' UNION ALL

    SELECT 2, 'Comment 4' UNION ALL

    SELECT 3, '1 comment on 3' UNION ALL

    SELECT 3, 'comments for 3, the second' UNION ALL

    SELECT 3, 'comment 3' UNION ALL

    SELECT 3, 'Comment 4' UNION ALL

    SELECT 3, 'Comment 5' UNION ALL

    In the end I need to display it as

    BidID Comment1 Comment2 Comment3

    Any thoughts?

  • Congratulations on a well-written and informative article.

  • Great article Jeff. Keep 'em coming.

    ATBCharles Kincaid

Viewing 15 posts - 1 through 15 (of 243 total)

You must be logged in to reply to this topic. Login to reply