Stored procedure execute Immediate error with WHERE clause - stored-procedures

I am trying to copy over one row from my archive table to my original table.
Without my WHERE clause, the whole table of table2 gets copied to table1.
I don't want this of course. So based on the gridview's ID value listed, the table will copy over only the row whose ID is the same.
When I debug the lines I get the correct ID listed for DisplaySup.Rows(0).Cells(2).Text.
(
val_ID table2.V_ID%type
)
is
begin
execute immediate 'insert into table1 (select * from table2 where V_ID = val_ID)';
end;
Yet I get the error
ORA-00904: "VAL_ID": invalid identifier
Table2 and Table1 have identical columns; so they both have column titled V_ID. I am unsure why Val_ID is flagging an error.
VB.net line of coding:
SupArchive.Parameters.Add("val_ID", OleDbType.VarChar).Value = DisplaySup.Rows(0).Cells(2).Text
So I tried to reference: EXECUTE IMMEDIATE with USING clause giving errors
Like so to fix WHERE:
(
val_ID table2.V_ID%type
)
is
begin
execute immediate 'insert into table1 (select * from table2 where V_ID = '||val_ID||')';
end;
but I get error:
ORA-00904: "val_ID": invalid identifier
Any suggestions on how to fix my stored procedure?
UPDATE:
Tried to do the suggested:
(
val_ID table2.V_ID%type
)
AS
BEGIN
execute immediate 'insert into table1 (col1, col2, col3...)(select col1, col2, col3... from table2 where V_ID = :val_ID)' using val_ID;
end;
but get error:
ORA-00904: "col72": invalid identifier
for col72 after Select statement
EXAMPLE OF MY TABLES (both are identical) purpose of table2 is when a row is deleted in table1, table2 can re-create the user that was deleted
Table1
ID CompanyName FirstName LastName ....(72 cols)
Table2
ID CompanyName FirstName LastName... (72 cols)

You would do best to use a bind variable in your insert statement. Also, you need to list the columns you're inserting into as well as those you're inserting, to avoid the "too many values" error.
Eg:
declare
val_ID table2.V_ID%type := 1;
begin
execute immediate 'insert into table1 (col1, col2, ...) (select col1, col2, ... from table2 where V_ID = :val_ID)' using val_id;
end;
/
Although in this instance there is absolutely no need to use dynamic sql at all, so you could just do:
declare
val_id table2.v_id%type := 1;
begin
insert into table1 (col1, col2, ...)
select col1, col2, ...
from table2
where v_id = val_id;
end;
/
Don't forget to commit after you've run the procedure!

Related

Is there any way to append the variable in big query select in stored procedure

I am trying to call the variable in a select query like below:
BEGIN
DECLARE TGT_LOAD_STATUS_TBL STRING;
SET TGT_LOAD_STATUS_TBL=''||PROJ_ID||'.'||TGT_SCHEMA||'.'||LOAD_STATUS_TBL||'';
FOR FETCH_TEST IN (select col1 from TGT_LOAD_STATUS_TBL WHERE LOAD_STATUS='N')
DO
INSERT INTO project.datasetid.table VALUES(FETCH_TEST.col1);
END FOR;
END
Is there any way to append the variable in a select query?
You can use build your SQL statement by concatenating some strings together and then execute it using EXECUTE IMMEDIATE. This is exactly the use case that EXECUTE IMMEDIATE exists for
I don't think it's a good practice to update a table one by one within a loop.
Instead consider a bulk update using a dynamic sql like below
EXECUTE IMMEDIATE FORMAT("""
INSERT INTO table1
SELECT col1 FROM `%s` WHERE LOAD_STATUS='N'
""", TGT_LOAD_STATUS_TBL);
Test Query:
DECLARE TGT_LOAD_STATUS_TBL DEFAULT 'table2';
CREATE TEMP TABLE table1 (col1 STRING);
CREATE TEMP TABLE table2 AS
SELECT 'a' col1, 'N' LOAD_STATUS UNION ALL
SELECT 'b', 'Y' UNION ALL
SELECT 'c', 'N' UNION ALL
SELECT 'd', 'N' UNION ALL
SELECT 'e', 'N' ;
EXECUTE IMMEDIATE FORMAT("""
INSERT INTO table1
SELECT col1 FROM `%s` WHERE LOAD_STATUS='N'
""", TGT_LOAD_STATUS_TBL);
SELECT * FROM table1;

Setting a column on TZQuery.ParamByName

I need to run a SELECT statement where the column names are variable, as follows:
query.SQL.Text := 'SELECT A.:COLUMN '+
' FROM A ';
query.ParamByName('COLUMN').AsString := 'column_name';
query.Open();
But when I do this, there's an error in the SQL syntax because the component runs as:
SELECT A.'column_name'
FROM A
Is there a way to set these parameters without the quotes so I can dynamically choose columns?
You can change your query as follow (create subqueries with all columns). All columns that you select have to be the same type:
select myData
from (
select col1 as myData, 'col1' as colName from A
union all
select col2, 'col2' from A
union all
select colLast, 'colLast' from A
) as mTbl
where colName=:COLUMN

Insert Stored Procedure with WHERE clause

I have a stored procedure for Oracle 10g that needs to create a new row in the table and not create duplicates.
The table allows duplicates, so long as all columns are not the same. This is because the last two columns can differ in values.
With that being said, when I try to store my procedure I get the following flags:
Line # = 10 Column # = 1 Error Text = PL/SQL: SQL Statement ignored
Line # = 13 Column # = 3 Error Text = PL/SQL: ORA-00933: SQL command not properly ended
The procedure looks fine [given I haven't added a WHERE clause for an insert before like this].
So either my format isn't what it should be or my logic is off.
Whatever the case may be, I have tried finding examples online and on stackoverflow and have fallen short.
Any suggestions on how I should tweak this?
(val_ID tablename.column1%type,
val_cat tablename.column2%type,
val_sub tablename.column3%type
)
AS
BEGIN
INSERT INTO tablename (column1, column2, column3)
VALUES (val_ID, val_cat, val_sub)
WHERE ((column1 != val_ID) and (column2 != val_cat) and (column3 != val_sub));
COMMIT;
END;
I have even removed the "(" in WHERE clause and nothing changed.
UPDATE:
tried the suggestion and all errors are gone [however the record didn't create]
(val_ID tablename.column1%type,
val_cat tablename.column2%type,
val_sub tablename.column3%type
)
AS
BEGIN
INSERT INTO tablename (column1, column2, column3)
SELECT val_ID, val_cat, val_sub
FROM dual
MINUS
SELECT val_ID, val_cat, val_sub
FROM tablename;
The insert statement doesn't have a where clause. You could emulate it, though, by using an insert-select statement:
INSERT INTO tablename (column1, column2, column3)
SELECT val_ID, val_cat, val_sub
FROM dual
MINUS
SELECT column1, column2, column3
FROM tablename;
#Mureinik 's example did negate all my errors; however, when tested it didn't create the new row.
So my current work around will be a query in VB.net checking if the value exists and then implementing a simple insert stored procedure:
//Make select statement and look at table for whether more than 0 rows shows up. If 0 rows, then execute stored procedure
If DsAds1.Tables(0).Rows.Count = 0 Then
...do stored procedure
End If
Stored Procedure
(val_ID tablename.column1%type,
val_cat tablename.column2%type,
val_sub tablename.column3%type
)
AS
BEGIN
INSERT INTO tablename (column1, column2, column3)
VALUES( val_ID, val_cat, val_sub);
COMMIT;
END;

Generic procedure to delete duplicates - no PKs

I wrote a stored procedure with a table name as parameter, that checks if there are duplicate rows in this table. The statements are built dynamically of course:
INSERT INTO tmpTable
SELECT col1, col2,... FROM table GROUP BY col1, col2, ... HAVING COUNT(*) > 1;
DELETE FROM tablename FROM tablenname
INNER JOIN tmpTable ON ISNULL(tablename.col1, 0) = ISNULL(tmpTable.col1, 0)
AND ISNULL(tablename.col2, 0) = ISNULL(tmpTable.col2, 0)
AND ...;
INSERT INTO tablename SELECT * FROM tmpTable;
Should work so far, but problem is, that it fails when the table has blob columns, like text. Those can not be compared in the JOIN. I also tried
DELETE FROM tablename GROUP BY col1, col2, ... HAVING COUNT(*) > 1;
but GROUP BY is not supported in DELETE statement directly without self-joining.
Also it's not possible to query information_schema for primary key of this table, since none of these tables has one.
Any ideas? Thanks.
Since the statement is already built dynamically, add casting the relevant columns to varchar(max) for the purpose of join. It's not difficult to figure which columns that are:
select c.name, quotename(c.name, '[')
from
sys.columns c
inner join sys.types t on c.system_type_id = t.system_type_id
where
c.object_id = object_id(#TABLE_NAME)
and c.is_computed = 0
and t.name in ('text', 'image', 'timestamp', 'xml')

Local variables in an Informix script

I have to do a big update script - not an SPL (stored procedure).
It's to be written for an Informix db.
It involves inserting rows into multiple tables, each of which relies on the serial of the insert into the previous table.
I know I can get access to the serial by doing this:
SELECT DISTINCT dbinfo('sqlca.sqlerrd1') FROM systables
but I can't seem to define a local variable to store this before the insert into the next table.
I want to do this:
insert into table1 (serial, data1, data2) values (0, 'newdata1', 'newdata2');
define serial1 as int;
let serial1 = SELECT DISTINCT dbinfo('sqlca.sqlerrd1') FROM systables;
insert into table2 (serial, data1, data2) values (0, serial1, 'newdata3');
But of course Informix chokes on the define line.
Is there a way to do this without having to create this as a stored procedure, run it once and then delete the procedure?
If the number of columns in the tables involved is as few as your example, then you could make the SPL permanent, and use it to insert your data, ie:
EXECUTE PROCEDURE insert_related_tables('newdata1','newdata2','newdata3');
Obviously that doesn't scale terribly well, but is OK for your example.
Another thought that expands on Jonathan's example and solves any concurrency issues that might arise from the use of MAX() would be to include DBINFO('sessionid') in Table3:
DELETE FROM Table3 WHERE sessionid = DBINFO('sessionid');
INSERT INTO Table1 (...);
INSERT INTO Table3 (sessionid, value)
VALUES (DBINFO('sessionid'), DBINFO('sqlca.sqlerrd1'));
INSERT INTO Table2
VALUES (0, (SELECT value FROM Table3
WHERE sessionid = DBINFO('sessionid'), 'newdata3');
...
You could also make Table3 a TEMP table:
INSERT INTO Table1 (...);
SELECT DISTINCT DBINFO('sqlca.sqlerrd1') AS serial_value
FROM some_dummy_table_like_systables
INTO TEMP Table3 WITH NO LOG;
INSERT INTO Table2 (...);
Informix does not provide a mechanism outside of stored procedures for 'local variables' of the type you want. However, in the limited example you provide, this works:
CREATE TABLE Table1
(
serial SERIAL(123) NOT NULL,
data1 VARCHAR(32) NOT NULL,
data2 VARCHAR(32) NOT NULL
);
CREATE TABLE Table2
(
serial SERIAL NOT NULL,
data1 INTEGER NOT NULL,
data2 VARCHAR(32) NOT NULL
);
INSERT INTO Table1(Serial, Data1, Data2)
VALUES(0, 'newdata1', 'newdata2');
INSERT INTO Table2(Serial, Data1, Data2)
VALUES(0, DBINFO('sqlca.sqlerrd1'), 'newdata3');
SELECT * FROM Table1;
123 newdata1 newdata2
SELECT * FROM Table2;
1 123 newdata3
However, this works only because you need to insert one row into Table2. If you needed to insert more, the technique would not work well. You could, I suppose, use:
CREATE TEMP TABLE Table3
(
value INTEGER NOT NULL
);
INSERT INTO Table1(Serial, Data1, Data2)
VALUES(0, 'newdata1', 'newdata2');
INSERT INTO Table3(Value)
VALUES(DBINFO('sqlca.sqlerrd1'));
INSERT INTO Table2(Serial, Data1, Data2)
VALUES(0, (SELECT MAX(value) FROM Table3), 'newdata3');
INSERT INTO Table2(Serial, Data1, Data2)
VALUES(0, (SELECT MAX(value) FROM Table3), 'newdata4');
And so on...the temporary table for Table3 avoids problems with concurrency and MAX().

Resources