Oracle10G SQL : Turning Columns into rows

前端 未结 1 707
遇见更好的自我
遇见更好的自我 2021-01-24 04:49

I need to turn columns into rows and get its average.

For example I have this table:

Name   Math    Science     Computer
----   ----    -------     -----         


        
1条回答
  •  生来不讨喜
    2021-01-24 05:06

    If you were on 11G you could use unpivot:

    SELECT subject, AVG(percentage) AS percentage
    FROM (
        SELECT * FROM tablea
        UNPIVOT (percentage FOR subject IN (math, science, computer))
    )
    GROUP BY subject
    ORDER BY subject;
    
    SUBJECT  PERCENTAGE
    -------- ----------
    COMPUTER      94.33
    MATH          91.33
    SCIENCE       87.33
    

    But since you are not, you can fake it. Adapting from this site:

    SELECT subject, AVG(percentage) AS percentage
    FROM (
        SELECT DECODE(unpivot_row, 1, 'Math',
                                   2, 'Science',
                                   3, 'Computer') AS subject,
               DECODE(unpivot_row, 1, math,
                                   2, science,
                                   3, computer) AS percentage
        FROM tablea
        CROSS JOIN (SELECT level AS unpivot_row FROM dual CONNECT BY level <= 3)
    )
    GROUP BY subject
    ORDER BY subject;
    
    SUBJECT  PERCENTAGE
    -------- ----------
    Computer      94.33
    Math          91.33
    Science       87.33
    

    In both cases, the inner select is transforming rows into columns; in 10g you just have to do it yourself. The SELECT ... CONNECT BY ... just generates a list of dummy values, and this has to have enough to cover the number of columns you are converting to rows (and if you really have 1000, you should really revisit the data model). The two decode statements use that generated number to match up a column name and value - run the inner select on its own to se what that looks like.

    Without resorting to dynamic SQL, you can't get away from having to list the columns - only once with the real unpivot, but twice with the fake 10g version, and you have to make sure they match up properly, and that the row number generator is producing enough values. (Too many and you might get odd results, but as any extra values will be null here and you're using avg, it doesn't matter too much in this case; just as a sanity check you should probably make it match exactly anyway).


    Or another version, based on you always wanting all the columns except name, which means you only need to list the columns you do want once and it's easier to match them up visually - just keep adding when clauses; and you don't need the row count:

    SELECT subject, AVG(percentage) AS percentage
    FROM (
        SELECT column_name AS subject,
            CASE
                WHEN column_name = 'MATH' then math
                WHEN column_name = 'SCIENCE' then science
                WHEN column_name = 'COMPUTER' then computer
            END AS percentage
        FROM tablea
        CROSS JOIN (
            SELECT column_name
            FROM user_tab_columns
            WHERE table_name = 'TABLEA'
            AND column_name != 'NAME'
        )
    )
    GROUP BY subject
    ORDER BY subject;
    
    SUBJECT                        PERCENTAGE
    ------------------------------ ----------
    COMPUTER                            94.33
    MATH                                91.33
    SCIENCE                             87.33
    

    0 讨论(0)
提交回复
热议问题