sqloracle-databaseoracle-apex

Group by A, B and order by A


tldr: Need to GROUP BY SUM(COST) OVER A,B and also ORDER BY SUM(COST) OVER A

I have a database with columns TYPE, NAME, and COST. My end goal is to create a grouped bar chart in an APEX Application that has TYPE as the X axis, COST as the Y axis, and groups bars by NAME. However, in order to isolate types with the greatest cost, I want to order by the sum of cost over just TYPE so I can select for the top types.

Example Data:

TYPE NAME COST
bad A 500
good A 300
bad A 1000
very bad B 200
very good B 2300
good C 400
bad A 100
very good C 700
good A 800
bad A 400
very good C 500
good B 300
bad C 350
bad C 600
good B 440
bad B 502
bad A 300
good A 456
bad B 340

Expected Tabular Result (Order-sensitive):

TYPE NAME SUM(COST) BY TYPE AND NAME TYPE RANKING
bad A 2300 1
bad B 842 1
bad C 950 1
very good B 2300 2
very good C 1200 2
good A 1556 3
good B 740 3
good C 400 3
very bad B 200 4

Here is a similar bar chart to the one I'd like to achieve in Oracle APEX.

Current Work:

SELECT
  TYPE,
  NAME,
  SUM(COST) OVER(PARTITION BY TYPE) AS SUM_COST_BY_TYPE,
  SUM(COST)
FROM
  mytable
GROUP BY
  NAME,
  TYPE
ORDER BY
  SUM_COST_BY_TYPE DESC

FAQ:

To my knowledge, having a grouped bar chart in Oracle APEX requires that you have a GROUP BY in the SQL query, so I believe using two PARTITION BYs will not work without some extra steps.

I am aware of stacked charts in APEX, they are not an option.


Solution

  • You can GROUP BY and SUM aggregation function to get the cost in each name/type group and then use the SUM analytic function to get the total cost per type and then use the DENSE_RANK analytic function to generate the rankings:

    SELECT type,
           name,
           DENSE_RANK() OVER (ORDER BY type_cost DESC, type) AS type_rank,
           total_cost
    FROM   (
      SELECT type,
             name,
             SUM(SUM(cost)) OVER (PARTITION BY type) AS type_cost,
             SUM(cost) AS total_cost
      FROM   mytable
      GROUP BY
            type,
            name
    )
    ORDER BY
          type_rank,
          name
    

    Which, for the sample data:

    CREATE TABLE mytable (type, name, cost) AS
    SELECT 'bad',       'A',  500 FROM DUAL UNION ALL
    SELECT 'good',      'A',  300 FROM DUAL UNION ALL
    SELECT 'bad',       'A', 1000 FROM DUAL UNION ALL
    SELECT 'very bad',  'B',  200 FROM DUAL UNION ALL
    SELECT 'very good', 'B', 2300 FROM DUAL UNION ALL
    SELECT 'good',      'C',  400 FROM DUAL UNION ALL
    SELECT 'bad',       'A',  100 FROM DUAL UNION ALL
    SELECT 'very good', 'C',  700 FROM DUAL UNION ALL
    SELECT 'good',      'A',  800 FROM DUAL UNION ALL
    SELECT 'bad',       'A',  400 FROM DUAL UNION ALL
    SELECT 'very good', 'C',  500 FROM DUAL UNION ALL
    SELECT 'good',      'B',  300 FROM DUAL UNION ALL
    SELECT 'bad',       'C',  350 FROM DUAL UNION ALL
    SELECT 'bad',       'C',  600 FROM DUAL UNION ALL
    SELECT 'good',      'B',  440 FROM DUAL UNION ALL
    SELECT 'bad',       'B',  502 FROM DUAL UNION ALL
    SELECT 'bad',       'A',  300 FROM DUAL UNION ALL
    SELECT 'good',      'A',  456 FROM DUAL UNION ALL
    SELECT 'bad',       'B',  340 FROM DUAL;
    

    Outputs:

    TYPE NAME TYPE_RANK TOTAL_COST
    bad A 1 2300
    bad B 1 842
    bad C 1 950
    very good B 2 2300
    very good C 2 1200
    good A 3 1556
    good B 3 740
    good C 3 400
    very bad B 4 200

    fiddle