Duplicate a row 100 times in SQL DEVELOPER - sql

I want to insert a row 100 times in a table based on count. For example, I have a table with table_id, table_name and want the rows
1,asd
2,asd
3,asd
4,asd
'
'
'
100,asd
I am looking for a solution using SQL Developer.

You can use the connect by level syntax to easily produce your result.
select level, 'asd'
from dual
connect by level <= 100
Or, as FSP notes an anonymous PL/SQL block with a loop, which isn't as good a solution as you should always use SQL over PL/SQL if possible...
begin
for i in 1 .. 100 loop
insert into my_table(table_id, table_name)
values(i, 'asd');
end loop;
end;
/

If you are using oracle it can be done with a single stamtement:
insert into your_table (table_id, table_name) select level, 'asd' from dual connect by level <= 100;

Related

How to create a Procedure with a specific WITH clause in Oracle?

I have a problem by creating a Procdure with a specific WITH clause. I use an Oracle database. I need a temp table for mapping specific values during time of execution.
In addition I have the following Oracle procedure. This Procedure will be created and executed by bringing into database:
How can I create this kind of Oracle SQL Procedure? Many thanks for helping me!
Use your WITH clause in a cursor FOR loop in your procedure:
DECLARE
PROCEDURE myProcedure(pinSchema IN VARCHAR2) AS
BEGIN
FOR aRow IN (WITH t AS (SELECT 'myFirstOldSchema' AS OLD_SCHEMA,
'myFirstNewSchema' AS NEW_SCHEMA
FROM DUAL UNION ALL
SELECT 'mySecondOldSchema' AS OLD_SCHEMA,
'mySecondNewSchema' AS NEW_SCHEMA
FROM DUAL UNION ALL
SELECT 'myThirdOldSchema' AS OLD_SCHEMA,
'myThirdNewSchema' AS NEW_SCHEMA
FROM DUAL)
SELECT t.*
FROM t
WHERE t.OLD_SCHEMA = pinSchema)
LOOP
-- Here a specific value from column "OLD_SCHEMA" must be inserted
EXECUTE IMMEDIATE 'INSERT INTO ' || aRow.OLD_SCHEMA ||
'.myTable(column_1, column_2, column_3)
(SELECT extern_column_1,
extern_column_2,
extern_column_3
FROM ' || aRow.NEW_SCHEMA || '.myExternTable)';
END LOOP;
END myProcedure;
BEGIN
FOR S IN (SELECT * FROM ROOT_SCHEMA.myTableWithSchema)
LOOP
-- First loop S.mySchemata represent the value 'myFirstOldSchema'
-- Second loop S.mySchemata represent the value 'mySecondOldSchema'
-- Third loop S.mySchemata represent the value 'myThirdOldSchema'
myProcedure(S.mySchemata);
END LOOP
COMMIT;
END;
Not tested on animals - you'll be first!

Create UNION ALL statements via a loop

Manually, I can select partitions in an inner query with the first code block below. Is there a way to do this in a more elegant way via a loop? I'm showing 3 partitions here, but I have about 200 and the partitions are based on a date column and therefore the partition names will need to change when I run this query again at a future date.
SELECT *
FROM (
SELECT * FROM RSS_ACQ.TRX_ARQ PARTITION("SYS_P211048") UNION ALL
SELECT * FROM RSS_ACQ.TRX_ARQ PARTITION("SYS_P210329") UNION ALL
SELECT * FROM RSS_ACQ.TRX_ARQ PARTITION("SYS_P176323")
) TRX_ARQ
;
With this statement, I've created a loop that outputs the UNION ALL statements.
BEGIN
FOR ALL_TAB_PARTITIONS IN
(
SELECT PARTITION_NAME
FROM ALL_TAB_PARTITIONS
where TABLE_OWNER = 'TABLEOWNER'
AND TABLE_NAME = 'TABLENAME'
AND PARTITION_POSITION > 123
ORDER BY partition_position DESC
)
LOOP
DBMS_OUTPUT.PUT_LINE( 'SELECT * FROM RSS_ACQ.TRX_ARQ PARTITION(\"'
|| ALL_TAB_PARTITIONS.PARTITION_NAME || '\") UNION ALL');
END LOOP;
END;
And in this block, I've attempted to use the loop inside the inner query. It's not yet formatted correctly and I'll need to avoid having UNION ALL for the very last partition.
SELECT *
FROM (
BEGIN
FOR ALL_TAB_PARTITIONS IN
(
SELECT PARTITION_NAME
FROM ALL_TAB_PARTITIONS
where TABLE_OWNER = 'TABLEOWNER'
AND TABLE_NAME = 'TABLENAME'
AND PARTITION_POSITION > 123
ORDER BY partition_position DESC
)
LOOP
DBMS_OUTPUT.PUT_LINE( 'SELECT * FROM RSS_ACQ.TRX_ARQ PARTITION(\"'
|| ALL_TAB_PARTITIONS.PARTITION_NAME || '\") UNION ALL');
END LOOP;
END;
) TRX_ARQ
;
Here are some of the errors, but there were also many more. They are syntax errors pointing to other parts of the query so I would expect that I have an issue with escaping the quotes.
Error starting at line : 99 in command -
END LOOP
Error report -
Unknown Command
Error starting at line : 100 in command -
END
Error report -
Unknown Command
Error starting at line : 101 in command -
)
Error report -
Unknown Command
Error starting at line : 102 in command -
) TABLENAME
Error report -
Unknown Command
This is a bit of a guess, but it's too long for a comment.
I am assuming your table is interval partitioned. In that case, getting all the data from partition positions > 123 is the same as getting all the rows with a higher date than the highest date in partition 123.
You can obtain that date from ALL_TAB_PARTITIONS and then use it to query the table. Like this:
WITH FUNCTION get_high_value RETURN DATE IS
l_high_val_expr ALL_TAB_PARTITIONS.HIGH_VALUE%TYPE;
l_high_value DATE;
BEGIN
SELECT high_value
INTO l_high_val_expr
FROM all_tab_partitions
WHERE table_owner = 'RSS_ACQ'
AND table_Name = 'TRX_ARQ'
and partition_position = 123;
EXECUTE IMMEDIATE 'SELECT ' || l_high_val_expr || ' FROM DUAL' INTO l_high_value;
RETURN l_high_value;
END;
SELECT * FROM rss_acq.trx_arq
-- Replace "partitioned_date_column" with the name of the column on which the
-- table is interval partitioned.
WHERE partitioned_date_column > get_high_value;
We can't execute an anonymous PL/SQL block in a SELECT statement.
What you need to do is spool the output of the ALL_TAB_PARTITIONS loop to a file (or a SQL worksheet if you're using an IDE like SQL Developer). This will give you a script you can run separately after editing it (you need to trim UNION ALL from the final generated SELECT.
Probably there are more elegant ways of achieving the same thing, but the task seems sufficiently wrong that it doesn't strike me as being worth the effort. You want to query 200 partitions in a single statement. That is a brute force operation and there isn't mush to be gained from querying named blocks. In fact, producing a union of 200 separate queries may be more expensive than a single query. So why not try something like this?
select * from RSS_ACQ.TRX_ARQ
where partition_key_col >= date '2018-08-01' -- or whatever
"I think you are overlooking the 12c feature of using PL/SQL in the WITH clause"
That 12c feature is for functions not procedures, so it won't help the OP run their code. It would be possible to use a WITH clause function but that would require:
creating a type with the same projection as the target table
and a nested table type based on that type
a WITH clause function which assembles and executes a dynamic SQL statement
we can't use REF CURSORs in SQL so ...
the function has to execute the dynamic select INTO a local collection variable ...
then loop over the collection and PIPE ROW to output those rows ...
so the main query can call the function with a table() call
Can a WITH clause function be pipelined? I can't find anything in the documentation to say we can't (don't have access to 12c right now to test).

How to store multiple rows in a variable in pl/sql function?

I'm writing a pl/sql function. I need to select multiple rows from select statement:
SELECT pel.ceid
FROM pa_exception_list pel
WHERE trunc(pel.creation_date) >= trunc(SYSDATE-7)
if i use:
SELECT pel.ceid
INTO v_ceid
it only stores one value, but i need to store all values that this select returns. Given that this is a function i can't just use simple select because i get error, "INTO - is expected."
You can use a record type to do that. The below example should work for you
DECLARE
TYPE v_array_type IS VARRAY (10) OF NUMBER;
var v_array_type;
BEGIN
SELECT x
BULK COLLECT INTO
var
FROM (
SELECT 1 x
FROM dual
UNION
SELECT 2 x
FROM dual
UNION
SELECT 3 x
FROM dual
);
FOR I IN 1..3 LOOP
dbms_output.put_line(var(I));
END LOOP;
END;
So in your case, it would be something like
select pel.ceid
BULK COLLECT INTO <variable which you create>
from pa_exception_list
where trunc(pel.creation_Date) >= trunc(sysdate-7);
If you really need to store multiple rows, check BULK COLLECT INTO statement and examples. But maybe FOR cursor LOOP and row-by-row processing would be better decision.
You may store all in a rowtype parameter and show whichever column you want to show( assuming ceid is your primary key column, col1 & 2 are some other columns of your table ) :
SQL> set serveroutput on;
SQL> declare
l_exp pa_exception_list%rowtype;
begin
for c in ( select *
from pa_exception_list pel
where trunc(pel.creation_date) >= trunc(SYSDATE-7)
) -- to select multiple rows
loop
select *
into l_exp
from pa_exception_list
where ceid = c.ceid; -- to render only one row( ceid is primary key )
dbms_output.put_line(l_exp.ceid||' - '||l_exp.col1||' - '||l_exp.col2); -- to show the results
end loop;
end;
/
SET SERVEROUTPUT ON
BEGIN
FOR rec IN (
--an implicit cursor is created here
SELECT pel.ceid AS ceid
FROM pa_exception_list pel
WHERE trunc(pel.creation_date) >= trunc(SYSDATE-7)
)
LOOP
dbms_output.put_line(rec.ceid);
END LOOP;
END;
/
Notes from here:
In this case, the cursor FOR LOOP declares, opens, fetches from, and
closes an implicit cursor. However, the implicit cursor is internal;
therefore, you cannot reference it.
Note that Oracle Database automatically optimizes a cursor FOR LOOP to
work similarly to a BULK COLLECT query. Although your code looks as if
it fetched one row at a time, Oracle Database fetches multiple rows at
a time and allows you to process each row individually.

How to generate SQL Code to update 9k rows with values for one column

I need to update one field for 9000 rows with random information on it.
Keep in mind that the table in question does not have Primary Key.
What's the best option to do it?
Thanks in advance.
Create table to hold 9000 random numbers for demonstration purposes:
CREATE TABLE brianl.random
(
VALUE NUMBER
, id INTEGER
);
Insert 9000 rows into the table
Since this is for demonstration purposes, I decided to use a PL/SQL loop as it is easy to understand even if you are not familiar with PL/SQL. I could just as easily used a Common Table Expression (CTE), but if you don't understand the PL/SQL, you won't understand the CTE
DECLARE
l_cnt INTEGER;
BEGIN
FOR i IN 1 .. 9000
LOOP
INSERT INTO random (id)
VALUES (i);
END LOOP;
COMMIT;
SELECT COUNT (*) c
INTO l_cnt
FROM random;
DBMS_OUTPUT.put_line ('-- '
|| TO_CHAR (SYSDATE, 'YYYY.MM.DD HH24:MI:SS')
|| ' count is: '
|| l_cnt);
END;
-- 2016.11.11 10:28:42 count is: 9000
Update 9000 records with random values with a single update statement
Now that we have a table of 9000 values, we can update 9000 values as requested`.
update random set value = dbms_random.value;
commit;
-- You can see those 9000 random values with a select from the table we
created for this purpose
select * from random;
Alternatives to PL/SQL for creating table records
And finally, some people have commented that the PL/SQL is bad practice for creating the values (I disagree, to me it is straightforward, and because it is procedural, easy to understand for the beginner), Here is a CTE that performs the same function. Don't forget to commit after it has finished executing.
INSERT INTO random (id)
WITH iset (num)
AS (SELECT 1
FROM DUAL
UNION ALL
SELECT num + 1
FROM iset
WHERE num < 9000)
SELECT *
FROM iset;
Another method people sometimes use is to select from all_objects. I personally don't care for this method as I believe that tables should only be used for their stated purpose. This is also defective if you need to go above the number of objects in all_objects, which on the system I am using is 82,009,
INSERT INTO random (id)
SELECT ROWNUM
FROM all_objects
WHERE ROWNUM <= 9000;
So, on the Oracle system I am creating these demonstrations on, the following will only insert 82009 rows into the table, not the requested 100000.
INSERT INTO random (id)
SELECT ROWNUM
FROM all_objects
WHERE ROWNUM <= 100000;

Manually forward a sequence - oracle sql

I need to forward a set of sequences with only DML access. Due to a bug in a piece of code several values were grabbed without a sequence but instead manually, so now the sequence is duplicating those values. So, I would like to push the sequence to the max value so that the next time nextval is called, it gives a value higher than the maximum. I've got about 50 sequences that each have to go a few thousand forward.
Is this possible with only DML access? If so, how should I go about it?
You should determine the difference between the next value of the sequence and the required value. The required value is typically the max value of a primary key column (let's name it ID).
DECLARE
maxid NUMBER;
maxseq NUMBER;
temp NUMBER; -- without this variable Oracle would skip to query the sequence
BEGIN
SELECT MAX(ID) INTO maxid FROM MYTABLE;
SELECT MYSEQ.NEXTVAL INTO maxseq FROM DUAL;
FOR i IN maxseq .. maxid LOOP
SELECT MYSEQ.NEXTVAL INTO temp FROM DUAL;
END LOOP;
END;
/
You can use dynamic SQL to do this. For example, this bit of code will select the next 10,000 values from each of a list of sequences.
DECLARE
l_num INTEGER;
BEGIN
FOR seq IN (select *
from all_sequences
where sequence_name in (<<list of 50 sequences>>)
and sequence_owner = <<owner of sequences>>)
LOOP
FOR i IN 1 .. 10000
LOOP
execute immediate
'select ' || seq.sequence_owner || '.' || seq.sequence_name || '.nextval from dual'
into l_num;
END LOOP;
END LOOP;
END;
If you had the ability to issue DDL against the sequence, you could use a similar approach to set the INCREMENT to 10,000, select one value from the sequence, and set the INCREMENT back down to 1 (or whatever it is now).
you can just
select seq.nextval from dual
until it is big enough...
If you have a table with at least as many rows as the amount you want to add to your sequences, the following will work. This increments each sequence by the same amount, which may not suit you, but it's quick and easy without requiring PL/SQL or the need to drop/re-create the sequence. I use it all the time when I want to get development server sequences ahead of production.
SELECT seq1.nextval, seq2.nextval, ..., seqN.nextval
FROM very_large_table
WHERE ROWNUM <= number_of_rows_to_add
To restart the sequence at a different value you need to drop and recreate it.
See the Oracle docs for ALTER SEQUENCE here.
And for CREATE SEQUENCE here
So, no I don't think it's possible with DML access, unless you just increment repeatedly like Randy suggests.
you can just;
declare
l_MaxVal pls_integer;
l_Currval pls_integer default - 1;
begin
select max(id)
into l_MaxVal
from people;
while l_Currval < l_Maxval
loop
select my_seq.nextval
into l_Currval
from dual;
end loop;
end;
There's a trick you can use to select a sequence of numbers, one per row. For example:
SELECT ROWNUM FROM DUAL CONNECT BY ROWNUM <= 100
Replace ROWNUM with <sequence>.NEXTVAL to fast-forward the sequence in large steps, for instance:
SELECT <sequence>.NEXTVAL FROM DUAL CONNECT BY ROWNUM <= 100
I wouldn't use this for making changes to a production database, but for a dev database, it may be sufficient.