简体   繁体   中英

How to extract values from grouped rows to specific columns in Oracle SQL?

Suppose I have a table like this in Oracle:

+----------+-----------+--------+
| PersonId | ValueType | Value  |
+----------+-----------+--------+
|        1 | FirstName | John   |
|        1 | LastName  | Smith  |
|        2 | FirstName | John   |
|        2 | LastName  | Doe    |
|        2 | City      | London |
+----------+-----------+--------+

How can I select the data like the following:

+----------+-----------+----------+--------+
| PersonId | FirstName | LastName |  City  |
+----------+-----------+----------+--------+
|        1 | John      | Smith    |        |
|        2 | John      | Doe      | London |
+----------+-----------+----------+--------+

?

Try with case expressions to pivot your data.

select
    PersonId,
    max(case when ValueType = 'FirstName' then Value end) as FirstName,
    max(case when ValueType = 'LastName' then Value end) as LastName,
    max(case when valueType = 'City' then value end) City
from yourTable
group by
    PersonId

You can pivot your dataset with conditional aggregation:

select
    personid,
    max(case when valuetype = 'FirstName' then value end) firstname,
    max(case when valuetype = 'LastName'  then value end) lastname,
    max(case when valuetype = 'City'      then value end) city
from mytable
group by personid

The old way of pivoting (and, importantly, the SQL Standard compliant way) uses conditional aggregation, as shown in zealous's and GMB's answers.

Oracle introduced the pivot (and related unpivot ) operator in version 11.1, that is, a very long time ago. I illustrate that approach below. These operators are particularly helpful when we need to pivot (or unpivot) multiple columns at a time; however, that's not the case in this thread.

Setting up the test table:

create table person (personid, valuetype, value) as
  select 1, 'FirstName', 'John'   from dual union all 
  select 1, 'LastName' , 'Smith'  from dual union all
  select 2, 'FirstName', 'John'   from dual union all
  select 2, 'LastName' , 'Doe'    from dual union all
  select 2, 'City'     , 'London' from dual
;

Query and output:

select personid, firstname, lastname, city
from   person
pivot  (min(value) for valuetype in ( 'FirstName' as firstname
                                    , 'LastName'  as lastname
                                    , 'City'      as city
                                    )
       )
;

PERSONID FIRSTNAME LASTNAME  CITY  
-------- --------- --------- ------
       1 John      Smith        
       2 John      Doe       London

And - perhaps more as a curiosity (with more serious applicability, maybe, to other, harder problems) - here is a solution using match_recognize , introduced in Oracle 12.1. Importantly, unlike any other solutions, this approach does not use aggregation of any kind.

select personid, firstname, lastname, city
from   person
match_recognize
  (
    partition by personid
    measures  f.value as firstname
           ,  l.value as lastname
           ,  c.value as city
    pattern   ( (f|l|c)* )
    define    f as valuetype = 'FirstName'
         ,    l as valuetype = 'LastName'
         ,    c as valuetype = 'City'
  )
;

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