简体   繁体   中英

UNNEST Multiple Values?

I am trying to make a table off of another table. The original table has a row that looks like this:

------------------------
| col1 | col 2 | col 3 |
------------------------
| item | a,b,c | 1,2,3 |
------------------------

I'm trying to take that row, and put it into a table like this:

------------------------
| col1 | col 2 | col 3 |
------------------------
| item |   a   |   1   |
------------------------
| item |   b   |   2   |
------------------------
| item |   c   |   3   |
------------------------

So basically I am trying to UNNEST two comma separated rows at the same time. So far the best I have come up with is to UNNEST each column individually and then try to combine the two resulting tables (which I'm struggling with as well), but ideally I was hoping to do this in one step.

Here is my query to UNNEST one row at a time:

SELECT
  col1, col2, col3
FROM 
  tableName,
UNNEST(SPLIT(col2)) AS col2

Here is my attempt at doing an UNNEST as a subquery, but it gives a ton of results:

SELECT sub.*
FROM (
  SELECT
    col1, col2, col3 AS col3
  FROM 
    tableName,
  UNNEST(SPLIT(col2)) AS col2
  WHERE
    randomCol = 'something'
  ) sub,
UNNEST(SPLIT(sub.col3)) AS col3

The SQL standard allows to pass multiple values to the unnest() function.

So the following should work (and does in Postgres)

select d.col1, 
       t.*
from data d
  cross join unnest(string_to_array(d.col2, ','), string_to_array(d.col3, ',')) as t(col1, col2) 

That also deals correctly with different number of elements in the lists.

However, I don't know if your proprietary DBMS supports that.

Online example: http://rextester.com/XPN48947

by saying the original table has 'a row': do you mean exactly one? if yes, this does the trick:

with 
num_rows_ as (
  select length( regexp_replace((select b from t), '[^,]+')) + 1 value_ from dual),
a_ as (
  select a from t),
b_ as (
  select regexp_substr( (select b from t), '[^,]', 1, level ) b,rownum rownum_
  from dual
  connect by level <= (select value_ from num_rows_)),
c_ as (
  select regexp_substr( (select c from t), '[^,]', 1, level ) c,rownum rownum_
  from dual
  connect by level <= (select value_ from num_rows_))  
select a_.a,b_.b,c_.c 
  from a_ 
  full outer join b_ on 1=1
  inner join c_ on b_.rownum_ = c_.rownum_;

http://sqlfiddle.com/#!4/f795b9/29

or shorter, in one step:

with a_ as
(select a from t),
b_c_ as (
  select regexp_substr( (select b from t), '[^,]', 1, level ) b,regexp_substr( (select c from t), '[^,]', 1, level ) c
  from dual
  connect by level <= (length( regexp_replace((select b from t), '[^,]+')) + 1)
)
select * from a_ cross join b_c_;

http://sqlfiddle.com/#!4/f795b9/32/0

You can use the unnest(split(col)) strategy, but don't cross-join both columns. Your answer implies an implicit order to the comma-separated values, so you'll need to establish a field ( RowNumber below) to indicate this ordering.

with Expanded2 as (
  select
    tableName.col1,
    col2.col2,
    row_number() over (partition by col1 order by 1) RowNumber
  from
    tableName,
    unnest(split(col2)) col2
), Expanded3 as (
  select
    tableName.col1,
    col3.col3,
    row_number() over (partition by col1 order by 1) RowNumber
  from
    tableName,
    unnest(split(col3)) col3
)
select
  Expanded2.col1,
  Expanded2.col2,
  Expanded3.col3
from
  Expanded2
  full outer join Expanded3 on 
    Expanded2.col1 = Expanded3.col1
    and Expanded2.RowNumber = Expanded3.RowNumber

I'm not sure how your rdbms handles effectively-null window partitioning. The above works in PostgreSQL. SQL Server would require order by (select null) . Ymmv.

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