Discussions
Categories
- 385.5K All Categories
- 5.1K Data
- 2.5K Big Data Appliance
- 2.5K Data Science
- 453.4K Databases
- 223.2K General Database Discussions
- 3.8K Java and JavaScript in the Database
- 47 Multilingual Engine
- 606 MySQL Community Space
- 486 NoSQL Database
- 7.9K Oracle Database Express Edition (XE)
- 3.2K ORDS, SODA & JSON in the Database
- 585 SQLcl
- 4K SQL Developer Data Modeler
- 188K SQL & PL/SQL
- 21.5K SQL Developer
- 46 Data Integration
- 46 GoldenGate
- 298.4K Development
- 4 Application Development
- 20 Developer Projects
- 166 Programming Languages
- 295K Development Tools
- 150 DevOps
- 3.1K QA/Testing
- 646.7K Java
- 37 Java Learning Subscription
- 37.1K Database Connectivity
- 201 Java Community Process
- 108 Java 25
- 22.2K Java APIs
- 138.3K Java Development Tools
- 165.4K Java EE (Java Enterprise Edition)
- 22 Java Essentials
- 176 Java 8 Questions
- 86K Java Programming
- 82 Java Puzzle Ball
- 65.1K New To Java
- 1.7K Training / Learning / Certification
- 13.8K Java HotSpot Virtual Machine
- 94.3K Java SE
- 13.8K Java Security
- 208 Java User Groups
- 25 JavaScript - Nashorn
- Programs
- 667 LiveLabs
- 41 Workshops
- 10.3K Software
- 6.7K Berkeley DB Family
- 3.6K JHeadstart
- 6K Other Languages
- 2.3K Chinese
- 207 Deutsche Oracle Community
- 1.1K Español
- 1.9K Japanese
- 474 Portuguese
PIVOT SQL

Hello Team,
I was trying to query the table for dynamic columns from rows.
I am good with the following example below,
create table a_table(id_number number, dept_id number, dept_name varchar2(40));
insert into a_table(id_number,dept_id,dept_name) values(1,10,'Eng');
insert into a_table(id_number,dept_id,dept_name) values(2,20,'Bio');
insert into a_table(id_number,dept_id,dept_name) values(3,10,'Eng');
insert into a_table(id_number,dept_id,dept_name) values(4,30,'Mat');
select * from a_table;
ID_NUMBER | DEPT_ID | DEPT_NAME |
1 | 10 | Eng |
2 | 20 | Bio |
3 | 10 | Eng |
4 | 30 | Mat |
SELECT * FROM
(
SELECT id_number, dept_id,dept_name
FROM a_table
)
PIVOT
(
max(dept_name)
FOR dept_id IN (10, 20, 30)
);
ID_NUMBER | 10 | 20 | 30 |
1 | Eng | ||
2 | Bio | ||
4 | Mat | ||
3 | Eng |
Question: Pivot works fine if I send static data into FOR statement of the query.
But I need to pass the dynamic data so that the column names should be dynamic.
For example if I have 100 departments I want to extract the data with heading as 100 departments as column names.
Any help for this question would be greatly appreciated. Thank You.
Regards,
Anil Kumar.
Answers
-
If I had a dollar for every time someone asks about dynamic columns and pivoting, I'd be able to retire.
and ask yourself, if you have a dynamic number of columns with dynamic names.... how are you then going to use the results of that query? You don't know what column names you have available to reference in your code, or how many columns there are.
My answer is simple... use the right tool... a reporting tool. The only place you need to dynamically have your data represented is when you are generating reports. Before that, for any data processing, you typically don't need to pivot your data in such a dynamic way.
-
Hi,
Blushadow is right (as usual); this is a job for a reporting tool.
If you must do it in pure SQL, then you'll need dynamic SQL if you need a separate column for each distinct value of dept_id that is found in the table.
An alternative is to produce a huge VARCHAR2 column (called txt in the query below) that is formatted to look like separate columns.
For example:
<p>WITH all_dept_ids AS</p><p>(</p><p> SELECT dept_id</p><p> , ROW_NUMBER () OVER (ORDER BY dept_id) AS col_number</p><p> FROM a_table</p><p> GROUP BY dept_id</p><p>)</p><p>, union_results AS</p><p>(</p><p> SELECT 3 AS part_number</p><p> , t.id_number</p><p> , TO_CHAR (t.id_number, '99999999') AS id_number_str</p><p> , LISTAGG ( LPAD ( NVL (t.dept_name, ' ')</p><p> , 10</p><p> )</p><p> , ' '</p><p> ) WITHIN GROUP (ORDER BY d.col_number) AS txt</p><p> FROM all_dept_ids d</p><p> LEFT OUTER JOIN a_table t PARTITION BY (t.id_number)</p><p> ON t.dept_id = d.dept_id</p><p> GROUP BY t.id_number</p><p>UNION ALL</p><p> SELECT 1</p><p> , NULL</p><p> , 'ID_NUMBER'</p><p> , LISTAGG ( LPAD ( TO_CHAR (dept_id)</p><p> , 10</p><p> )</p><p> , ' '</p><p> ) WITHIN GROUP (ORDER BY col_number)</p><p> FROM all_dept_ids</p><p>UNION ALL</p><p> SELECT 2</p><p> , NULL</p><p> , '---------'</p><p> , LISTAGG ( LPAD ('-', 10, '-')</p><p> , ' '</p><p> ) WITHIN GROUP (ORDER BY col_number)</p><p> FROM all_dept_ids</p><p>)</p><p>SELECT id_number_str</p><p>, txt</p><p>FROM union_results</p><p>ORDER BY part_number</p><p>;</p>
Output:
ID_NUMBER TXT
--------- --------------------------------------------------
ID_NUMBER 10 20 30
--------- ---------- ---------- ----------
1 Eng
2 Bio
3 Eng
4 Mat
6 rows selected.Notice that the result set contains 2 columns and 6 rows: 2 "header" rows, and 4 data rows, shown in black above. The output shown in red above is added when I run this in SQL*Plus. Use your front end to avoid displaying lines like those. (In SQL*Plus, that would be SET HEADING OFF and SET FEEDBACK OFF .)
Here, I used 10 as the maximum dept_name length. You can change that to 40, to match the actual column length, or any other value. LISTAGG returns a VARCHAR2, so with 10 characters per name, plus 1 space to separate the virtual columns, you can have up to TRUNC (4000 / (10 + 1) ) =
363 virtual columns in a 4000-character VARCHAR2. With 40 characters per name, you could have 97 virtual columns.
-
When this site was changed in 2020, the reply above got garbled. Here's a formatted version of the same query:
WITH all_dept_ids AS ( SELECT dept_id , ROW_NUMBER () OVER (ORDER BY dept_id) AS col_number FROM a_table GROUP BY dept_id ) , union_results AS ( SELECT 3 AS part_number , t.id_number , TO_CHAR (t.id_number, '99999999') AS id_number_str , LISTAGG ( LPAD ( NVL (t.dept_name, ' ') , 10 ) , ' ' ) WITHIN GROUP (ORDER BY d.col_number) AS txt FROM all_dept_ids d LEFT OUTER JOIN a_table t PARTITION BY (t.id_number) ON t.dept_id = d.dept_id GROUP BY t.id_number UNION ALL SELECT 1 , NULL , 'ID_NUMBER' , LISTAGG ( LPAD ( TO_CHAR (dept_id) , 10 ) , ' ' ) WITHIN GROUP (ORDER BY col_number) FROM all_dept_ids UNION ALL SELECT 2 , NULL , '---------' , LISTAGG ( LPAD ('-', 10, '-') , ' ' ) WITHIN GROUP (ORDER BY col_number) FROM all_dept_ids ) SELECT id_number_str , txt FROM union_results ORDER BY part_number ;