PL/SQL bulk INSERT into a table with an unknown structure - sql

Can you issue a FORALL bulk INSERT into a table with an unknown structure? That means, can you build dynamically the INSERT command in the FORALL construct without knowing the number of fields at compile time?
Number and name of fields is retrieved at runtime and stored in a collection:
TYPE RowType is TABLE OF VARCHAR2(50) INDEX BY VARCHAR2(50);
TYPE TableType is TABLE OF RowType;
my_table TableType;
So at runtime my_table could be filled this way for example:
my_table(1)('FIELD1') = 'VALUE1A';
my_table(1)('FIELD2') = 'VALUE2A';
my_table(1)('FIELD3') = 'VALUE3A';
my_table(2)('FIELD1') = 'VALUE1B';
my_table(2)('FIELD2') = 'VALUE2B';
my_table(2)('FIELD3') = 'VALUE3B';
my_table(3)('FIELD1') = 'VALUE1C';
my_table(3)('FIELD2') = 'VALUE2C';
my_table(3)('FIELD3') = 'VALUE3C';
The insert statements that should be bulk executed therefore are:
INSERT INTO TABLENAME (FIELD1,FIELD2,FIELD3) VALUES (VALUE1A,VALUE2A,VALUE3A);
INSERT INTO TABLENAME (FIELD1,FIELD2,FIELD3) VALUES (VALUE1B,VALUE2B,VALUE3B);
INSERT INTO TABLENAME (FIELD1,FIELD2,FIELD3) VALUES (VALUE1C,VALUE2C,VALUE3C);
EDIT: Do you even read the questions or you just read a couple of words in the title? The linked question asks how to bind a variable, this question asks how to bulk issue dynamic statements. Yes, there are the words 'insert' and 'table' in both questions.

No, you can't dynamically build and execute a FORALL...INSERT... statement dynamically. You can, however, build up an INSERT statement dynamically of the form:
INSERT ALL
INTO TABLENAME (FIELD1,FIELD2,FIELD3) VALUES (VALUE1A,VALUE2A,VALUE3A)
INTO TABLENAME (FIELD1,FIELD2,FIELD3) VALUES (VALUE1B,VALUE2B,VALUE3B)
INTO TABLENAME (FIELD1,FIELD2,FIELD3) VALUES (VALUE1C,VALUE2C,VALUE3C)
Or if the data you want to insert into your table resides in another table you might find an INSERT...(subquery) statement like
INSERT INTO TABLENAME
SELECT FIELD1, FIELD2, FIELD3
FROM OTHER_TABLE
WHERE something <> something_else
or you might be able to use a MERGE statement similar to
MERGE INTO TABLENAME t
USING (SELECT FIELD1, FIELD2, FIELD3 FROM OTHER_TABLE) o
ON (t.FIELD1 = o.FIELD1)
WHEN NOT FOUND THEN
INSERT (FIELD1, FIELD2, FIELD3) VALUES (o.FIELD1, o.FIELD2, o.FIELD3)
which will do a mass insert based on the data specified in the USING clause and the match criteria in the ON predicate.
So there may be ways to do what you want but without knowing the specifics of the source of your data and how you're manipulating that data prior to inserting it into your database it's tough to say whether or not any of them would apply.
Best of luck.

Related

How can I use a postgres variable in my SELECT statement?

I'm storing a value in a variable featureId and then trying to use that value in my SELECT statement, but postgres seems to be taking the name literally and looking for a column called "featureid". I'm getting an error "ERROR: column "featureid" does not exist
LINE 4: featureId,"
My code is below. How can I use the value of the variable in my SELECT statement?
SELECT id INTO featureId FROM tableA WHERE NAME = 'some value';
INSERT INTO tableB (client_id, feature_id, does_have)
SELECT
id,
featureId,
TRUE
FROM tableA
Without a declared variable your SELECT INTO is the version of SELECT INTO that creates a table. To see it for yourself try:
SELECT id
INTO featureid
FROM tablea
WHERE name = 'some value';
SELECT *
FROM featureid;
For assigning the value to a variable the variable must be declared. You can use an anonymous DO block.
DO
$$
DECLARE
featureid tablea.id%TYPE;
BEGIN
SELECT id
INTO featureid
FROM tablea
WHERE name = 'some value';
INSERT INTO tableb
(client_id,
feature_id,
does_have)
SELECT id,
featureid,
true
FROM tablea;
END;
$$
LANGUAGE plpgsql;
There are few errors on what you're tryng to do:
sql is declarative language so you're asking what to do not how to do and this is for this reason that you cannot store variables and some statements like declare and begin-end should be used in trigger and not in a simple query.
you are executing two statements: select and insert into and they are executed one after the other, so once again you cannot store a variable.
insert into, insert a single record but potentially you're tryng to retrieve more data with your select statement (if NAME is not unique)
if 'some-value' is a known constant and NAME is unique just insert that value in the where clause of the insert into. If you're tryng to insert more data take a look on bulk insert syntax of postgres: bulk insert

ORACLE TRIGGER INSERT INTO ... (SELECT * ...)

Trigger with Insert into (select * ...)
I'm trying it.
INSERT INTO T_ USERS SELECT * FROM USERS WHERE ID = :new.ID;
not working...
this work.
INSERT INTO T_USERS(ID) VALUES(:new.ID);
Trigger
create or replace trigger "TRI_USER"
AFTER
insert on "USER"
for each row
begin
INSERT INTO T_USER SELECT * FROM USER WHERE ID = :new.ID;
end;​
this work.
INSERT INTO T_USERS(ID) VALUES(:new.ID);
So if it fits to you then try this:
INSERT INTO T_USER(ID) SELECT ID FROM USER WHERE ID = :new.ID;
If you want to select one or more rows from another table, you have to use this syntax:
insert into <table>(<col1>,<col2>,...,<coln>)
select <col1>,<col2>,...,<coln>
from ...;
Perhaps you could post the actual error you are experiencing?
Also, I suggest that you rethink your approach. Triggers that contain DML introduce all sorts of issues. Keep in mind that Oracle Database may need to restart a trigger, and could therefore execute your DML multiple times for a particular row.
Instead, put all your related DML statements together in a PL/SQL procedure and invoke that.
Its not about your trigger but because of INSERT statement
here insert statement works as below
INSERT INTO <TABLE>(COL1,COL2,COL3) VALUES (VAL1,VAL2,VAL3); --> If trying to populate value 1 by one.
INSERT INTO <TABLE>(COL1,COL2,COL3) --> If trying to insert mult vales at a time
SELECT VAL1,VAL2,VAL3 FROM <TABLE2>;
The number of values should match with number of columsn mentioned.
Hope this helps you to understand

Bulk insert in SQL Server database from one table to another

I have 200k records in one table and I want to insert these records into another table. I read about the bulk insert but the query I found on msdn website is just not making any sense.
This is the query
BULK INSERT AdventureWorks2012.Sales.SalesOrderDetail
FROM 'f:\orders\lineitem.tbl'
WITH
(
FIELDTERMINATOR =' |',
ROWTERMINATOR =' |\n'
);
What is f:\orders\lineitem.tbl and the whole this is just not making any sense.
I have a table with four columns: id, frm, to1 and country
Same this in destination table
Any easy syntax will be helpful
I am using SQL Server 2008/12
BULK INSERT imports from an external data file. If you already have the data in a SQL Server table, then you should do something like:
INSERT INTO NewTable (field1, field2, field3)
SELECT field1, field2, field3 FROM OldTable
DO NOT point BULK INSERT at your SQL Server database file. The .tbl file referenced in your example code is to a text file with delimited fields.
Bulk insert is for import external data from file to sql table like
BULK INSERT 'tableName' From 'File Path',
If You have Copy data from one table to other table in sql, use:
select into instate insert into like ' select * into table1 From table2
insert into destinationsever.destinationdatabase.dbo.destinationtable
select * from sourcesever.sourcedatabase.dbo.sourcetable

Setting multiple scalar variables from a single row in SQL Server 2008?

In a trigger, I have code like:
SET #var1 = (SELECT col1 FROM Inserted);
SET #var2 = (SELECT col2 FROM Inserted);
Is it possible to write the above in a single line? Something conceptually like:
SET (#var1,#var2) = (SELECT col1,col2 FROM Inserted);
Obviously I tried the above, without success; am I just stuck with the first method?
Even if possible, is that a good idea?
Thanks!
yes, use first method.
Or...
SELECT
#var1 = col1
,#var2 = col2
FROM
Inserted;
However, it is a major red flag if you are expecting to set variable values like that in a trigger. It generally means the trigger is poorly designed and needs revision. This code expects there will be only one record in inserted and this is something that is not going to be true in all cases. A multiple record insert or update will have multiple records in inserted and the trigger must account for that (please without using a trigger!!!). Triggers should under no circumstances be written to handle only one-record inserts/updates or deletes. They must be written to handle sets of data.
Example to insert the values from inserted to another table where the trigger is on table1:
CREATE TRIGGER mytrigger on table1
AFTER INSERT
AS
INSERT table2 (field1, field2, field3)
SELECT field1, 'test', CASE WHEN field3 >10 THEN field3 ELSE 0 END
FROM inserted
No, it is not possible. SET accepts a single target and value. AFAIK.

Creating a conditional SQL trigger in SQLite

I’m trying to write a trigger for sqlite and just running into all kinds of problems. In truth I think my real problem is with my poor background in the sql language. Anyway here goes…
I have two tables Table1 and Table2. Table1 has a column named time (which is a 64bit integer time). I need a trigger that watches for a new row being inserted in Table1. If there are 3 or more rows in Table1 that have time greater than X (a hard coded value in the below example 120 seconds) I need to insert a new row into Table2.
Here is what I have so far (note this does not work)
CREATE TRIGGER testtrigger AFTER
INSERT ON Table1 WHEN
(
SELECT COUNT() AS tCount FROM
(
SELECT * FROM Table1 WHERE
time > (NEW.time - 120)
) WHERE tCount > 3
)
BEGIN
INSERT INTO Table2 (time, data) VALUES
(NEW.time, 'data1');
END
Any kind souls out there who are better in SQL than I?
This works because the WHEN clause needs an expression:
sqlite> .schema Table1
CREATE TABLE Table1 (time int);
CREATE TRIGGER testtrigger AFTER INSERT ON Table1
WHEN 3<(SELECT Count() FROM Table1 WHERE time>(NEW.time-120))
BEGIN
INSERT INTO Table2 (time, data) VALUES (NEW.time,'data1');
END;
Have you looked at this reference page? From what I can tell this is a "misuse of aggregate" which probably stems from statement in the When section. You had this:
sqlite> .tables
Table1 Table2
sqlite> .schema Table1
CREATE TABLE Table1 (time int);
CREATE TRIGGER testtrigger AFTER
INSERT ON Table1 WHEN
(
SELECT COUNT() AS tCount FROM
(
SELECT * FROM Table1 WHERE
time > (NEW.time - 120)
) WHERE tCount > 3
)
BEGIN
INSERT INTO Table2 (time, data) VALUES
(NEW.time, 'data1');
END;
sqlite> .schema Table2
CREATE TABLE Table2 (time int,data string);
sqlite> insert into Table1 VALUES (5);
SQL error: misuse of aggregate:
sqlite>
I tried deleting "WHERE tCount" to make it into an expression, but then I got a syntax error at the operator.
So instead I switched things about for the solution above.
Your WHEN clause in the trigger should be a comparison expression which returns true or false, instead of returning a number. Try dlamblin's idea.
Maybe a different syntactical approach?
CREATE TRIGGER testtrigger ON Table1
FOR INSERT
AS
BEGIN
DECLARE #timeNum int
SET #timeNum = SELECT count(*) FROM Table1 WHERE time > (New.time - 120)
IF #timeNum > 3
BEGIN
INSERT INTO Table2 (time, data) VALUES
(NEW.time, 'data1');
END
END
But also, try some debugging statements. When I was debugging my last trigger for a webservice I put some INSERT statements into a debugging table that I setup. So then you could output the #timeNum every time the trigger gets called, and then put another debug INSERT inside the loop to make see if you actually get into your Table2 INSERT logic.
UPDATE:
Sorry! Looks like SqlLite kinda sucks, I did not know that it lacked some of this syntax. Nonetheless, if you are not getting any answers, consider some debugging statements to make sure that your code paths are being called under the right conditions.