简体   繁体   中英

How to use group by with pivot in Oracle 11g

Table Structure

CREATE TABLE PLAN 
  ( 
     plan_id   NUMERIC, 
     startdate DATE NOT NULL, 
     enddate   DATE, 
     cost      NUMERIC (3) NOT NULL, 
     plan_type VARCHAR (5) NOT NULL, 
     CONSTRAINT plan_pk PRIMARY KEY (plan_id), 
     CONSTRAINT plan_ck CHECK (startdate <= enddate) 
  );

Requirement:

Create a pivot table to list the number of plans by plan type (voice, text, data) and also by cost range. For cost range, create 3 segments, “Economy Plans” defined as those with monthly fee less than $40, “Standard Plans” defined as plans with monthly fees between $40 and $50, and “Premium Plans” defined as those with monthly fees more than $50. The pivot table result should include 3 columns (Voice, Data, Text) and 3 rows (Economy Plans, Standard Plans, Premium Plans). The cells should contain the counts of each and should have 0 rather than NULL where there are no matching plans.

MY query SO FAR

SELECT * FROM 
(SELECT cost, plan_type
    FROM plan) 
PIVOT ( count(plan_type) FOR plan_type IN ('voice', 'data', 'text')
);

it return count of voice, data, text type plans for all cost. I am unable to get it in range as specified in the requirement.

MY other try

SELECT CASE 
         WHEN cost < 40 THEN '1-40' 
         WHEN cost<=50 THEN '40-50' 
         ELSE '50+' 
       END AS cost, count('voice'), count('data'), count('text') FROM 
(SELECT cost, plan_type
    FROM plan) 
PIVOT ( 
count(plan_type) FOR plan_type IN ('voice', 'data', 'text')
)
GROUP BY CASE 
         WHEN cost < 40 THEN '1-40' 
         WHEN cost<=50 THEN '40-50' 
         ELSE '50+' 
       END;

This query even count 1 for 0 value from pivot table thus returning a wrong answer. Please suggest how should I proceed about it?

You're almost there if you combine the two attempts:

SELECT * FROM (
  SELECT case
           when cost < 40 then 'Economy Plans'
           when cost <= 50 then 'Standard Plans'
           else 'Premium Plans'
    end as cost_range,
    plan_type
  FROM plan
) 
PIVOT (
  count(plan_type) FOR plan_type IN ('voice' as voice, 'data' as data, 'text' as text)
);

The subquery assigns a text label to the range, and the pivot is then done against that range rather than the actual plan costs.

With some made-up data that gives a result like:

COST_RANGE          VOICE       DATA       TEXT
-------------- ---------- ---------- ----------
Standard Plans          1          3          0
Economy Plans           2          1          1
Premium Plans           3          2          0

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM