insert a row into table in pl/pgsql - sql

I am trying to update two tables by inserting some rows into the other. But when I try to do something like this :
BEGIN
FOR id IN (SELECT id FROM table1) LOOP
PERFORM (INSERT INTO anothertable VALUES(id));
END LOOP;
END;
It gives an error I don't know why. syntax error at or near "INTO".
Is it even possible to do such thing without cursors and such updating one by one ?

This is example of bad using PERFORM and bad plpgsql programming style. You must not use a PERFORM there. You should not use a parenthesis there (PL/pgSQL is based on ADA language - not C!). Some correct patterns are:
FOR _id IN
SELECT s.id
FROM sometab s
LOOP
INSERT INTO othertab(id) VALUES(_id);
END LOOP;
or faster (and shorter) pattern
INSERT INTO othertab(id)
SELECT id
FROM other tab
I used a qualified name and prefixes to reduce a risk of conflict between SQL identifiers and PL/pgSQL variables.
Note: PERFORM is implemented as SELECT statement without result processing. So your statement was SELECT (INSERT INTO tab ...) what is unsupported feature now.

why don't you insert it like this:
insert into anothertable
select id from table

Related

FOR loop in Oracle SQL or Apply SQL to multiple Oracle tables

My SQL is a bit rusty, so I don't know whether the following is even possible:
I have multiple tables t_a, t_b, t_c with the same column layout and I want to apply the same operation to them, namely output some aggregation into another table. For a table t_x this would look like this:
CREATE TABLE t_x_aggregate (
<here the col definitions which are the same for all new tables t_[abc]_aggregate>
);
INSERT INTO t_x_aggregate(id, ...)
SELECT id, SUM(factor*amount)
FROM t_x
WHERE some fixed condition
GROUP BY id;
I now want to execute something like a FOR loop around this:
for t_x in t_a, t_b, t_c
CREATE TABLE ...
INSERT INTO ...
end for
Is this possible in SQL? Or would I need to build a wrapper in another language for this?
So, the result of that operation would be 3 new tables? T_A_AGGREGATE, T_B_AGGREGATE and T_C_AGGREGATE?
I think that the fastest way is to write 3 separate CREATE TABLE statements, e.g.
create table t_a_aggregate as
select id, sum(factor * amount) suma
from t_a
where some_condition
group by id;
create table t_b_aggregate as
select id, sum(factor * amount) suma
from t_b
where some_condition
group by id;
create table t_c_aggregate as
select id, sum(factor * amount) suma
from t_c
where some_condition
group by id;
OK; I understand that queries aren't that simple, but nothing much changes - only table names in CREATE and FROM (perhaps somewhere else, but that's more or less "it"). Any decent text editor's search/replace capabilities should be able to do it quickly.
If you want to do it dynamically in a loop (read: PL/SQL), you can - but dynamic SQL doesn't scale, is difficult to maintain, is painful to debug. Therefore, if you're doing it only once, consider running 3 separate statements.
How to do it dynamically?
You'd have to create a string (we usually put them into a locally declared variable) which contains the whole DDL statement. Why? Because you can't execute DDL from a PL/SQL otherwise.
If there are multiple tables and/or columns involved, you'll have to combine "fixed" parts of the statement (like create table, select, from, order by) concatenated with "dynamic" parts - such as column names. Note that in between you have to concatenate commas as separators. Pay attention to usage of multiple single quotes as you have to escape them (or use the q-quoting mechanism).
Also, for multiple columns you'll probably have to do it in a loop, concatenating each new column to previously composed string.
It (the statement stored into the varirable) is the executed by EXECUTE IMMEDIATE. If it is correctly written, it'll succeed. Otherwise, it'll fail, but it won't tell you why it failed (that's why I said difficult debugging").
So, instead of executing it, we usually display that string (using dbms_output.put_line) so that we see how it looks like and - using copy/paste - try to execute it.
Basically, it can be quite complex and - as I said - difficult to maintain and debug.
For the FOR loop you need to use PL/SQL like this:(*)
declare
type array_t is table of varchar2(10);
array array_t := array_t('a', 'b', 'c');
lo_stmt varchar2(2000);
begin
lo_stmt :=
'CREATE TABLE t_'||array(i)||'_aggregate ('||
' <here the col definitions which are the same for all new tables t_[abc]_aggregate>'||
');'||
''||
'INSERT INTO t_'||array(i)||'_aggregate(id, ...)'||
'SELECT id, SUM(factor*amount)'||
'FROM t_'||array(i)||
'WHERE some fixed condition'||
'GROUP BY id;'||
execute immediate lo_stmt;
end loop;
end;
/
Look also at this SO question: How to use Oracle PL/SQL to create...
(*) #Littlefoot describes in the 2nd part of his answer valuable background to this program.

UPSERT for "INSERT INTO tab SELECT * FROM another_tab"

How would I do "UPSERT" (INSERT OR UPDATE) into SQLite table when inserting multiple rows from another table.
I've tried:
INSERT INTO tab_name
SELECT * FROM tmp
ON CONFLICT(id)
DO UPDATE SET
val = excluded.val;
But it gives me:
syntax error near "DO"
What would be the correct and the most efficient way to achieve that?
You might have hit a documented trap called parsing ambiguity :
When the INSERT statement to which the UPSERT is attached takes its values from a SELECT statement, there is a potential parsing ambiguity. The parser might not be able to tell if the "ON" keyword is introducing the UPSERT or if it is the ON clause of a join. To work around this, the SELECT statement should always include a WHERE clause, even if that WHERE clause is just "WHERE true".)
So, does this work better?
INSERT INTO tab_name
SELECT * FROM tmp WHERE true
ON CONFLICT(id) DO UPDATE SET val = excluded.val;

How to store the result of select statement into the temporary table in Oracle?

We can write select column1,column2 into #temp from tableName in SQL Server. But I am unable to write the same query in an Oracle database.
I want to store the result of select/insert/delete/update or any result set into a local temporary table in oracle database. How I can do this?
I am executing below query in my Oracle sql developer tool:
select * into #temp
from bmi;
but I am getting the error as follow please help to find this error.
when I execute the same query in Microsoft SQL Server it get executed & #temp table get created which is not present in the database but it can hold the data for that particular session. so i want same scenario in ORACLE database.
ORA-00911: invalid character
00911. 00000 - "invalid character"
*Cause: identifiers may not start with any ASCII character other than
letters and numbers. $#_ are also allowed after the first
character. Identifiers enclosed by doublequotes may contain
any character other than a doublequote. Alternative quotes
(q'#...#') cannot use spaces, tabs, or carriage returns as
delimiters. For all other contexts, consult the SQL Language
Reference Manual.
*Action:
Error at Line: 1 Column: 15
I want to store the result of select/insert/delete/update or any result set into a local temporary table in oracle database,How I can Do This?
You can't. Oracle doesn't have local temporary tables, it doesn't work like that. But it doesn't need to. Oracle has a very different internal model from SQL Server which means a lot of SQL Server practices are unnecessary in Oracle. (To be fair SQL Server has neat things which Oracle doesn't, like ANSI 92 Joins for DML.)
The key insight is: you don't want to store the result of select/insert/delete/update or any result set into a local temporary table. That is something you had to do in T-SQL to achieve the end goal of implementing some business logic. But what you actually wanted to do in SQL Server and what you want to do in Oracle is write some code which delivers value to your organisation.
So, with that mindset in place, what do you need to do?
If you want to loop round a result set then perhaps a Cursor Loop is what you're looking for?
for rec in ( select * from some_table
where the_date = date '2018-02-01' )
loop
...
If you want to work on some data prior to inserting it into a data then perhaps you should use a PL/SQL collection:
type l_recs is table of some_table%rowtype;
But maybe you just need to understand Oracle's Transaction Management model. A lot of things are possible in pure SQL without any need for procedural framework.
Create temporary table :
create global temporary table
results_temp (column1, column2)
on commit preserve rows;
and then insert to it from your table:
insert into results_temp (column1, column2 )
SELECT column1,column2
FROM source_table
create global temporary table temp_table_name
on commit preserve rows as select column1,column2,columnN from your_table;

sql ORA-00900: Invalid SQL statement

please help
IF EXISTS(SELECT * FROM MERC_ADM_VERSION )then UPDATE MERC_ADM_VERSION SET
VER_VALEUR = 20150409 WHERE VER_CLE = 'MEAD' ELSE INSERT INTO MERC_ADM_VERSION
('VER_VALEUR', 'VER_CLE') VALUES (20150409, 'MEAD');
ORA-00900: Invalid SQL statement
Remove the single quotes from the columns in the insert statement.
Instead of
('VER_VALEUR', 'VER_CLE')
It should be
(VER_VALEUR, VER_CLE)
Your question is quite unclear as to what you are trying to do. However, my best interpretation is you are looking for an oracle merge statement. Below is an example based on assumptions I made on the little information you provided. You are most likely looking for a MERGE statement. This allows you to perform a single operation that can either update or insert based on your criteria.
Also, you appear to be using a date, but in number format. I did nothing to address this due to lack of any table definition. You may still have problems with it.
MERGE
INTO merc_adm_version TARGET -- The table you want to update/insert into
USING (SELECT 20150409 as ver_valeur, 'MEAD' as ver_cle FROM dual) SOURCE -- The source of the data
ON (TARGET.ver_cle = SOURCE.ver_cle) -- How to try and match records between source and target
WHEN MATCHED THEN UPDATE SET ver_valeur = 20150409 -- When there is a match, how to update
WHEN NOT MATCHED THEN INSERT (ver_cle, ver_valeur) -- When there is not a match, how to insert
VALUES ('MEAD', 20150409);

How to reuse a large query without repeating it?

If I have two queries, which I will call horrible_query_1 and ugly_query_2, and I want to perform the following two minus operations on them:
(horrible_query_1) minus (ugly_query_2)
(ugly_query_2) minus (horrible_query_1)
Or maybe I have a terribly_large_and_useful_query, and the result set it produces I want to use as part of several future queries.
How can I avoid copying and pasting the same queries in multiple places? How can I "not repeat myself," and follow DRY principles. Is this possible in SQL?
I'm using Oracle SQL. Portable SQL solutions are preferable, but if I have to use an Oracle specific feature (including PL/SQL) that's OK.
create view horrible_query_1_VIEW as
select .. ...
from .. .. ..
create view ugly_query_2_VIEW as
select .. ...
from .. .. ..
Then
(horrible_query_1_VIEW) minus (ugly_query_2_VIEW)
(ugly_query_2_VIEW) minus (horrible_query_1_VIEW)
Or, maybe, with a with clause:
with horrible_query_1 as (
select .. .. ..
from .. .. ..
) ,
ugly_query_2 as (
select .. .. ..
.. .. ..
)
(select * from horrible_query_1 minus select * from ugly_query_2 ) union all
(select * from ugly_query_2 minus select * from horrible_query_1)
If you want to reuse the SQL text of the queries, then defining views is the best way, as described earlier.
If you want to reuse the result of the queries, then you should consider global temporary tables. These temporary tables store data for the duration of session or transaction (whichever you choose). These are really useful in case you need to reuse calculated data many times over, especially if your queries are indeed "ugly" and "horrible" (meaning long running). See Temporary tables for more information.
If you need to keep the data longer than a session, you can consider materialized views.
Since you're using Oracle, I'd create Pipelined TABLE functions.
The function takes parameters and returns an object (which you have to create)
and then you SELECT * or even specific columns from it using the TABLE() function and can use it with a WHERE clause or with JOINs. If you want a unit of reuse (a function) you're not restricted to just returning values (i.e a scalar function) you can write a function that returns rows or recordsets.
something like this:
FUNCTION RETURN_MY_ROWS(Param1 IN type...ParamX IN Type)
RETURN PARENT_OBJECT PIPELINED
IS
local_curs cursor_alias; --you need a cursor alias if this function is in a Package
out_rec ROW_RECORD_OF_CUSTOM_OBJECT:=ROW_RECORD_OF_CUSTOM_OBJECT(NULL, NULL,NULL) --one NULL for each field in the record sub-object
BEGIN
OPEN local_curs FOR
--the SELECT query that you're trying to encapsulate goes here
-- and it can be very detailed/complex and even have WITH () etc..
SELECT * FROM baseTable WHERE col1 = x;
-- now that you have captured the SELECT into a Cursor
-- here you put a LOOP to take what's in the cursor and put it in the
-- child object (that holds the individual records)
LOOP
FETCH local_curs --opening the ref-cursor
INTO out_rec.COL1,
out_rec.COL2,
out_rec.COL3;
EXIT WHEN local_curs%NOTFOUND;
PIPE ROW(out_rec); --piping out the Object
END LOOP;
CLOSE local_curs; -- always do this
RETURN; -- we're now done
END RETURN_MY_ROWS;
after you've done that, you can use it like so
SELECT * FROM TABLE(RETURN_MY_ROWS(val1, val2));
you can INSERT SELECT or even CREATE TABLE out of it , you can have it in joins.
two more things to mention:
--ROW_RECORD_OF_CUSTOM_OBJECT is something along these lines
CREATE or REPLACE TYPE ROW_RECORD_OF_CUSTOM_OBJECT AS OBJECT
(
col1 type;
col2 type;
...
colx type;
);
and PARENT_OBJECT is a table of the other object (with the field definitions) we just made
create or replace TYPE PARENT_OBJECT IS TABLE OF ROW_RECORD_OF_CUSTOM_OBJECT;
so this function needs two OBJECTs to support it, but one is a record, the other is a table of that record (you have to create the record first).
In a nutshell, the function is easy to write, you need a child object (with fields), and a parent object that will house that child object that is of type TABLE of the child object, and you open the original base-table fetching SQL into a SYS_REFCURSOR (which you may need to alias) if you're in a package and you read from that cursor from a loop into the individual records.
The function returns a type of PARENT_OBJECT but inside it packs the records sub-object with values from the cursor.
I hope this works for you (there may be permissioning issues with your DBA if you want to create OBJECTs and Table functions)*/
If you operate with values, you could write functions.
Here you find infos on how to do it. It basically works like writing a function in any language. You can define parameters and return values.
Which gives you the cool possibility to write code just once. Here is how you do it:
http://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_5009.htm
Have you tried using RESULT_CACHE hint in your queries? Also, you could
ALTER SESSION SET RESULT_CACHE_MODE=FORCE
and see if it helps.