1

I am trying to transfer data from one table to another. But in the process I need to do something extra I am just wondering is it possible to do something like this in SQL or PL/SQL alone.

      source                    target
-------------------       ------------------------
| id | name | qty |       | id | source_id | qty |
-------------------       ------------------------
| 1  | test | 2   |       | 1  | 1         | 1   |
-------------------       ------------------------
| 2  | ago  | 1   |       | 2  | 1         | 1   |    
-------------------       ------------------------
                          | 3  | 2         | 1   |
                          -----------------------

Here based on the quantity in source table I will have to insert multiple records. Quantity could be of any number. ID in target table is auto incremented. I tried this

INSERT INTO target (SELECT id, qty FROM source);

But this does not take care of the qty loop.

3
  • I'd think you'd need PL/SQL to accomplish that, but I also wonder what your primary key will be on the new table. Commented Oct 28, 2016 at 19:52
  • PL/SQL is fine as well ID in target table is auto increment. I have updated by question. Commented Oct 28, 2016 at 19:56
  • So, if the id is auto generated, how do you plan to make sure the source_id's will be in order? Or is that irrelevant - you really just need to generate source_id and qty, and they can be assigned any (new) id in target? Commented Oct 28, 2016 at 20:17

3 Answers 3

1

Plain SQL:

with
     inputs ( id, qty ) as (
       select 1, 2 from dual union all
       select 2, 1 from dual union all
       select 3, 5 from dual
     )
-- end of test data; solution (SQL query) begins below this line
select row_number() over (order by id) as id, id as source_id, 1 as qty
from   inputs
connect by level <= qty
       and prior id = id
       and prior sys_guid() is not null
;

NOTE - if the id is generated automatically, just drop the row_number().... as id column; the rest is unchanged.

ID  SOURCE_ID  QTY
--  ---------  --
 1          1   1
 2          1   1
 3          2   1
 4          3   1
 5          3   1
 6          3   1
 7          3   1
 8          3   1
Sign up to request clarification or add additional context in comments.

3 Comments

Sorry, I overwrote it - will fix. It's at the end of the WITH clause. (I overwrote it when I added the comment about where the solution begins.)
By the way, if you need to use this in an INSERT, you must write it like so: INSERT INTO .... SELECT ID, 1 FROM SOURCE CONNECT BY... You don't need the WITH clause at all, because you already have your SOURCE table, and you don't need to use column aliases (you have the column names for the TARGET table), and you don't need the ROW_NUMBER() OVER ... if the new id is auto generated.
DB server is down I will try when it comes back up. Thank you very much for your reply though.
0

This is possible using SQL. Use CTE to generate amount of rows that matches your maximum qty from source table and use non-equi JOIN for generating rows. Use row_number analytic function to assign each row it's unique id (if you have it in your target table, check below on my Edit):

with gen_numbers(r) as (
select rownum r
from dual
connect by rownum <= (select max(qty) from src) -- our maximum limit of rows needed
)
select
  row_number() over (order by src.id) as id,
  src.id as source_id, 
  1 as qty
from src
join gen_numbers on src.qty <= gen_numbers.r; -- clone rows qty times

Note that you can safely put in constans value 1 in the output of qty.


Your test data:

create table src (id int, name varchar(255), qty int);
insert into src (id, name, qty) 
  select 1, 'test', 2 from dual union all
  select 2, 'ago',  1 from dual
  ;

Result:

ID  SOURCE_ID   QTY
1   2           1
2   2           1
3   1           1

Edit: Since your target id column is auto incremented, you don't need the row_number. Just specify it like that to perform an INSERT:

with gen_numbers(r) as (
select rownum r
from dual
connect by rownum <= (select max(qty) from src) -- our maximum limit of rows needed
)
insert into target_table(source_id, qty)
select
  src.id as source_id, 
  1 as qty
from src
join gen_numbers on src.qty <= gen_numbers.r; -- clone rows qty times
order by src.id

Notice that I've added an ORDER BY clause to ensure proper ordering of inserting values.

4 Comments

The new id's should be sequential numbers from 1 to 3.
Thanks, I've missed that. Corrected :-)
Well, in the meantime it was pointed out to me that in a Comment, the OP said the id is generated by the system!
Realised that as well. Edited my answer appropriately ;-)
0
INSERT INTO TARGET(source_id, qty)
    WITH
    output
    AS
    (
      SELECT id, qty FROM source
      UNION ALL
      SELECT id, qty - 1 FROM source WHERE qty > 1
    )    
    SELECT
    id, count(*) as qty 
    FROM output
    group by 
    id, quantity
    ORDER BY
    id

7 Comments

You can't use WITH clause with INSERT like that. Also, it is not clear where you created the new ID column - in fact you are generating just two columns.
The author has mentioned the ID field is auto increment column in comments and SQL server supports CTEs
Oh... I missed that.
The whole WITH clause should be moved right before SELECT - at least in Oracle. Oracle supports CTEs too, just not with that syntax for INSERT statements. And I didn't see the reference to SQL Server; the question is tagged Oracle.
SQL is fine. You mentioned SQL Server, a specific database product (from Microsoft). You realize they are distinct, right? There are three solutions right now, and they all use SQL - and specifically the Oracle version of SQL. In that regard, your current INSERT statement is syntactically incorrect - check the Oracle documentation (or test your own solution) and you will see. I already told you how to fix it to comply with Oracle syntax.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.