Oracle SQL’s PIVOT clause allows you to transform row data into columns. It’s particularly useful when summarizing data across multiple categories, such as months, departments, or product types.
1. What Is Pivoting?
Pivoting is the process of converting rows into columns to better analyze or report on grouped data.
Example Scenario:
Imagine you have a table of sales data:
| EMPLOYEE | MONTH | SALES |
|---|---|---|
| Alice | Jan | 1000 |
| Alice | Feb | 2000 |
| Bob | Jan | 1500 |
| Bob | Feb | 2500 |
You want to transform this into:
| EMPLOYEE | JAN | FEB |
|---|---|---|
| Alice | 1000 | 2000 |
| Bob | 1500 | 2500 |
This is exactly what PIVOT does.
2. Oracle PIVOT Syntax
SELECT * FROM ( SELECT <columns> FROM <table> ) PIVOT ( <aggregate_function>(<column_to_aggregate>) FOR <column_to_pivot> IN (<list_of_values>) );
3. Sample Table for Example
Let’s create a simple table for demonstration:
CREATE TABLE sales (
employee VARCHAR2(50),
month VARCHAR2(10),
amount NUMBER
);
INSERT INTO sales VALUES ('Alice', 'JAN', 1000);
INSERT INTO sales VALUES ('Alice', 'FEB', 2000);
INSERT INTO sales VALUES ('Bob', 'JAN', 1500);
INSERT INTO sales VALUES ('Bob', 'FEB', 2500);
INSERT INTO sales VALUES ('Bob', 'MAR', 1000);
COMMIT;
4. Basic PIVOT Example
SELECT *
FROM (
SELECT employee, month, amount
FROM sales
)
PIVOT (
SUM(amount)
FOR month IN ('JAN' AS Jan, 'FEB' AS Feb, 'MAR' AS Mar)
);
Output:
| EMPLOYEE | JAN | FEB | MAR |
|---|---|---|---|
| Alice | 1000 | 2000 | NULL |
| Bob | 1500 | 2500 | 1000 |
Explanation:
SUM(amount)is the aggregation function.monthis the column whose values will become new columns.'JAN' AS Jan, 'FEB' AS Feb, ...specifies the values to pivot and how the new column should be named.
5. Important Notes
- The values in
IN(...)must match actual data values. - Column aliases (like
AS Jan) are optional but improve readability. - If a pivoted value is missing, Oracle returns
NULL.
6. Filtering with Pivot
If you want to filter by employee, you can add a WHERE clause outside the pivot:
SELECT *
FROM (
SELECT employee, month, amount
FROM sales
)
PIVOT (
SUM(amount)
FOR month IN ('JAN' AS Jan, 'FEB' AS Feb)
)
WHERE employee = 'Bob';
7. Dynamic Pivoting (Advanced)
Oracle SQL doesn’t support fully dynamic pivoting in plain SQL, but it can be achieved using PL/SQL or dynamic SQL.
Example using PL/SQL:
DECLARE
sql_stmt VARCHAR2(1000);
BEGIN
sql_stmt := '
SELECT * FROM (
SELECT employee, month, amount FROM sales
)
PIVOT (
SUM(amount)
FOR month IN (''JAN'' AS Jan, ''FEB'' AS Feb)
)';
EXECUTE IMMEDIATE sql_stmt;
END;
To build a dynamic list of months, you’d first query them from the data and construct the pivot SQL dynamically.
8. Multiple Aggregates with PIVOT
Oracle supports only one aggregation at a time using the PIVOT clause. If you need multiple aggregates (e.g., SUM and AVG), you can:
- Use multiple
PIVOTqueries and join them - Or use conditional aggregation instead
9. Alternatives to PIVOT
If PIVOT seems complex, or you need more flexibility, you can use CASE + GROUP BY manually.
SELECT employee, SUM(CASE WHEN month = 'JAN' THEN amount END) AS Jan, SUM(CASE WHEN month = 'FEB' THEN amount END) AS Feb, SUM(CASE WHEN month = 'MAR' THEN amount END) AS Mar FROM sales GROUP BY employee;
Same output, and easier to make dynamic if needed.
10. Real-World Use Case: Sales by Region and Month
Assume this data:
| REGION | MONTH | SALES |
|---|---|---|
| East | Jan | 1000 |
| East | Feb | 1500 |
| West | Jan | 1200 |
| West | Mar | 1300 |
Query:
SELECT *
FROM (
SELECT region, month, sales
FROM sales_by_region
)
PIVOT (
SUM(sales)
FOR month IN ('JAN' AS Jan, 'FEB' AS Feb, 'MAR' AS Mar)
);
11. Summary
| Feature | Description |
|---|---|
| Purpose | Convert row data into columns |
| Works With | Aggregation functions (SUM, AVG, COUNT, etc.) |
| Syntax | PIVOT (agg(column) FOR col IN (...)) |
| Required Oracle | Oracle 11g and above |
| Values Must Be | Known in advance or dynamically built |
12. Best Practices
- Use column aliases to make pivoted columns readable.
- Handle NULLs gracefully using
NVL()if needed. - Avoid too many pivoted columns – it can make the query hard to maintain.
- Use conditional aggregation (CASE + GROUP BY) for dynamic column needs.
