creating a trigger on a dynamically added table inside a procedure - java

I'am creating dynamic tables from java. What I want is that I want to create a trigger on every new table added which will just use the sequence to populate the primary key(serial_no) which is common in every table.How to achieve this ???
EDIT:
I have tried this code but I'am getting a message "procedure created with compilation errors"
create or replace procedure "TRIGGER_CALL" (trigger_name IN VARCHAR2, table_name IN VARCHAR2, sno IN NUMBER) as begin CREATE SEQUENCE abc MINVALUE 1 MAXVALUE 10000 INCREMENT BY 1 START WITH 141 CACHE 20 NOORDER NOCYCLE; CREATE OR REPLACE TRIGGER trigger_name before insert on table_name for each row begin select s_no.nextval into :new.sno from dual; end; end;
EDIT2:
My code
CREATE OR REPLACE
PROCEDURE "TRIGGER_CALL" (p_table_name IN VARCHAR2)
AUTHID CURRENT_USER
AS
l_sql VARCHAR2(4000);
l_dummy NUMBER;
l_trigger_name VARCHAR2(30);
l_seq_name VARCHAR2(30);
BEGIN
--SELECT '1'
--INTO l_dummy
--FROM all_tables
-- WHERE table_name = UPPER(p_table_name);
l_trigger_name := p_table_name || '_trg';
l_seq_name := p_table_name || 's_no';
EXECUTE IMMEDIATE 'CREATE SEQUENCE l_seq_name start with 1 increment by 1 ';
l_sql :=
'CREATE OR replace TRIGGER ' || l_trigger_name ||
' BEFORE INSERT ON ' || p_table_name ||
' FOR EACH ROW
BEGIN
SELECT l_seq_name.NEXTVAL
INTO :new.sno
FROM dual;
END;';
EXECUTE IMMEDIATE l_sql;
END;
/

Please check following code:
CREATE SEQUENCE my_sequence;
/
CREATE OR REPLACE
PROCEDURE "TRIGGER_CALL" (p_table_name IN VARCHAR2)
AUTHID CURRENT_USER
AS
l_sql VARCHAR2(4000);
l_dummy NUMBER;
l_trigger_name VARCHAR2(30);
BEGIN
-- Validate if a p_table_name is a valid object name
-- If you have access you can also use DBMS_ASSERT.SQL_OBJECT_NAME procedure
SELECT '1'
INTO l_dummy
FROM all_tables
WHERE table_name = UPPER(p_table_name);
l_trigger_name := p_table_name || '_trg';
l_sql :=
'CREATE OR replace TRIGGER ' || l_trigger_name ||
' BEFORE INSERT ON ' || p_table_name ||
' FOR EACH ROW
BEGIN
SELECT my_sequence.NEXTVAL
INTO :new.sno
FROM dual;
END;';
EXECUTE IMMEDIATE l_sql;
END;
/
CREATE TABLE my_test(sno NUMBER);
/
BEGIN
trigger_call('my_test');
END;
/
Important notes:
Usage of AUTHID CURRENT_USER eliminates the "insufficient privileges" problem. For reference see: Execute Immediate within a stored procedure keeps giving insufficient priviliges error
Because the dynamic sql simply concatenates the input parameter it needs to be validated to protect against SQL Injection. See also DBMS_ASSERT.
Because of point 2. I used table name to build trigger name.

Related

Cannot perform fetch on a PLSQL statement

this is my table
CREATE TABLE accounts (
accountid VARCHAR(255),
name VARCHAR2(255),
phone_number INTEGER,
email_addresses VARCHAR2(255),
account_number VARCHAR(255),
password VARCHAR2(255)
);
and now i created one procedure
create or replace PROCEDURE getaccount (
param1 IN VARCHAR2
) AS
BEGIN
DBMS_OUTPUT.PUT_LINE(' SELECT
accountid,
password
FROM
accounts
WHERE
account_number = '||param1) ;
END getaccount;
and also why we used || or operator
second i run this code in SQLDEVELOPER
I GET THIS OUT
call getaccount('123456');
Call completed.
GET THIS MESSAGE
NOW I AM CONNECTING TO JAVA
HERE IS MY CODE IN JAVA
CallableStatement pst = Get.connection().prepareCall("{call getAccount(?)}");
pst.setString(1, Number);
ResultSet rs = pst.executeQuery();
if (rs.next()) {
System.out.println(rs.getString(1));
here Get.connection return Connection .
after i run this i get this error
Enter Account Number : 123456
java.sql.SQLException: Cannot perform fetch on a PLSQL statement: next
at ojdbc10/oracle.jdbc.driver.InsensitiveScrollableResultSet.next(InsensitiveScrollableResultSet.java:409)
at bank.services.SignValidator.getBank(SignValidator.java:20)
at bank.services.KingobankMain.getBank(KingobankMain.java:49)
at bank.services.KingobankMain.main(KingobankMain.java:14)
Your procedure does not return values (output variable ) and you are trying to get data.
This results in an error.
Stored Procedure OUT Parameters:
reate or replace procedure getaccount (
p_account_number in varchar2,
p_account_id out varchar2,
p_password out varchar2
) as
begin
select
accountid,
password
into
p_account_id,
p_password
from
accounts
where
account_number = p_account_number ;
end getaccount;
/
CallableStatement pst = Get.connection().prepareCall("{call getAccount(?,?,?)}");
pst.setString(1, "....");//set account number
pst.registerOutParameter(2, OracleTypes.VARCHAR);
pst.registerOutParameter(3, OracleTypes.VARCHAR);
pst.execute();
//read the OUT parameter now
System.out.println("AccountID="+pst.getString(2)+",password="+pst.getString(3));
..........your of code....
Stored Procedure Oracle SYS_REFCURSOR:
create or replace procedure getaccount (
p_account_number in varchar2,
p_cur out sys_refcursor
) as
begin
open p_cur for
select
accountid,
password
from
accounts
where
account_number = p_account_number ;
end getaccount;
/
CallableStatement pst = Get.connection().prepareCall("{call getAccount(?,?)}");
pst.setString(1, ".....");// set account number
pst.registerOutParameter(2, OracleTypes.CURSOR);
pst.execute();
//read the OUT parameter now
rs = (ResultSet) pst.getObject(2);
while(rs.next()){
System.out.println("AccountID="+rs.getString("accountid")+",password="+rs.getString("password"));
..........your of code....
}
Addition links:
JDBC Basics
Calling PL/SQL from Java
I don't know Java, but: procedure you wrote results in output displayed on the screen, but only if tool you used to call the procedure supports it - e.g. SQL*Plus, SQL Developer, TOAD and similar. For the rest, there will be no result at all.
It looks as if you wanted to run
select accountid, password from accoutns where account_number = 123456
in Java. You should then use a function instead of a procedure. It should return a VARCHAR2 (that select statement) and then you'd execute it in Java. How? Can't tell; as I said, I don't know Java. In Oracle, you'd use dynamic SQL.
Or, if it has to be a procedure, then it should have an OUT parameter so that it returns that select statement to the caller. But, caller then has to have "something" (a variable?) to accept that output into.
As you requested, a procedure with an OUT parameter:
CREATE OR REPLACE PROCEDURE getaccount (param1 IN VARCHAR2,
par_query OUT VARCHAR2)
AS
BEGIN
par_query :=
'SELECT accountid, password FROM accounts WHERE '
|| ' account_number = '
|| DBMS_ASSERT.enquote_literal (param1);
END getaccount;
Note dbms_assert, which is used to prevent SQL injection.
In order to run it in Oracle, you'd have to use a PL/SQL block with a variable that accepts the result returned by that procedure.
SQL> set serveroutput on
SQL>
SQL> create or replace procedure getaccount (param1 in varchar2,
2 par_query out varchar2)
3 as
4 begin
5 par_query :=
6 'SELECT accountid, password FROM accounts WHERE '
7 || ' account_number = '
8 || dbms_assert.enquote_literal (param1);
9 end getaccount;
10 /
Procedure created.
SQL> declare
2 l_out varchar2(200);
3 begin
4 getaccount('12345', l_out);
5 dbms_output.put_line('Result is: ' || l_out);
6 end;
7 /
Result is: SELECT accountid, password FROM accounts WHERE account_number = '12345'
PL/SQL procedure successfully completed.
SQL>
Apparently, you don't want to return a command represented as a string, but result that is produced by that select statement. As commented, consider returning a refcursor. Moreover, use a function instead of a procedure; they are designed to "return" a value. If it is a procedure, you'll have to create OUT parameter(s) and accept their values into something (local variables, probably).
Suppose this is table's contents:
SQL> select * From accounts;
ACCOUNTID PASSWORD ACCOUNT_NUMBER
---------- ---------- --------------------
1 my_pwd 12345
SQL>
Function that returns refcursor:
SQL> create or replace function getaccount (param1 in varchar2)
2 return sys_refcursor
3 is
4 l_rc sys_refcursor;
5 begin
6 open l_rc for
7 'SELECT accountid, password FROM accounts WHERE '
8 || ' account_number = '
9 || dbms_assert.enquote_literal (param1);
10 return l_rc;
11 end getaccount;
12 /
Function created.
SQL> select getaccount('12345') from dual;
GETACCOUNT('12345')
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
ACCOUNTID PASSWO
---------- ------
1 my_pwd
SQL>
Or, a procedure with 2 OUT parameters:
SQL> create or replace procedure getaccount
2 (param1 in varchar2,
3 p_accid out varchar2,
4 p_pwd out varchar2) is
5 begin
6 select accountid, password
7 into p_accid, p_pwd
8 from accounts
9 where account_number = param1;
10 end;
11 /
Procedure created.
SQL> set serveroutput on
SQL> declare
2 l_id varchar2(20);
3 l_pwd varchar2(20);
4 begin
5 getaccount('12345', l_id, l_pwd);
6 dbms_output.put_line('ID = ' || l_id ||', PWD = ' || l_pwd);
7 end;
8 /
ID = 1, PWD = my_pwd
PL/SQL procedure successfully completed.
SQL>

Select Count stored procedure with dynamic query not working correctly

-I'm trying to execute a stored procedure that should returns me the result of a Count, but it gives me the following error(it's in portuguese):
2019-04-01 10:13:49.215 WARN 7068 --- [io-8080-exec-10] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 214, SQLState: S0002
2019-04-01 10:13:49.216 ERROR 7068 --- [io-8080-exec-10] o.h.engine.jdbc.spi.SqlExceptionHelper : O procedimento espera o parĂ¢metro '#statement' do tipo 'ntext/nchar/nvarchar'.
-I dont quite understand, because the only parameter that i setted is the table name, which i'm passing on my repository execution code.
-This is my stored procedure:
ALTER PROCEDURE [dbo].[usp_verificarTabelaCadMov] (
#NomeTabela VARCHAR(20)
)
AS
BEGIN
SET NOCOUNT ON;
Declare #Comando Varchar(1000)
Declare #Resultado INT;
Set #Comando = 'SELECT #Resultado = COUNT(*) FROM sysobjects WHERE nome = ' + QUOTENAME(#NomeTabela)
exec sp_executesql #Comando , N'#Resultado INT OUTPUT', #Resultado = #Resultado OUTPUT
SELECT #Resultado
END;
GO
-This is my execution in my Repository:
#Query(value = "EXECUTE usp_verificarTabelaCadMov :tabela", nativeQuery = true)
public Integer verificarTabela(#Param("tabela") String tabela);
-In my controller:
String tabela = ("M00"+(String.valueOf(Math.round(funcionario.getEmpresa().getCodigo())))+anoInicio+mesAtual);
if (eventoEspelhoPontoRepository.verificarTabela(tabela) > 1) {
registros.addAll(eventoEspelhoPontoRepository.findAllRegistrosByFuncionarioTableUnica(
tabela, dataInicioString, dataFimString, funcionario.getCracha()));
}
EDIT1 - what i'm trying to do here, is select a row of tables, but sometimes some of the tables doesn't exist, so i'm trying to do a verification if the table exists, and if it does, it should execute the Select * from that table, otherwise, should ignore. This is the only way i found to do it, if anyone has a better way please help me.
Btw, this is my select mentioned above, I tried to verify the existance of the table with the clause IF EXISTS but it doest work, I keep getting the error COULD NOT EXTRACT RESULT SET on my main program.
ALTER PROCEDURE [dbo].[usp_listarRegistrosMov]
--PARĂ‚METROS
#NomeTabela VARCHAR(20),
#DataInicial VARCHAR(20),
#DataFinal VARCHAR(20),
#Cracha FLOAT
AS
IF EXISTS(SELECT name FROM sysobjects WHERE name = #NomeTabela AND xtype = 'U')
BEGIN
Declare #Comando Varchar(1000)
Set #Comando = 'SELECT * FROM ' + #NomeTabela + ' WHERE mov_data BETWEEN ''' + #DataInicial + ''' AND ''' + #DataFinal + ''' AND mov_cracha = ' + CAST(#Cracha AS VARCHAR(50))
Exec(#Comando)
END
GO
EDIT 2: I tried using OBJECT_ID in the same sp also:
IF OBJECT_ID(#NomeTabela) IS NOT NULL
But always get the error:
2019-04-01 11:18:03.147 WARN 7068 --- [nio-8080-exec-8] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: null
2019-04-01 11:18:03.148 ERROR 7068 --- [nio-8080-exec-8] o.h.engine.jdbc.spi.SqlExceptionHelper : The statement did not return a result set.
I would do something like this:
ALTER PROCEDURE dbo.usp_verificarTabelaCadMov
(
#NomeTabela VARCHAR(20)
)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Comando NVARCHAR(1000);
DECLARE #Resultado INT;
SET #Resultado = -1
-- Or set to NULL depending on how you would like to handle table existence in the application code;
--SET #Resultado = NULL
IF OBJECT_ID(QUOTENAME(#NomeTabela), 'U') IS NOT NULL
BEGIN
SET #Comando = N'SELECT #Resultado = COUNT(*) FROM ' + QUOTENAME(#NomeTabela);
EXEC sys.sp_executesql
#Comando
, N'#Resultado INT OUTPUT'
, #Resultado OUTPUT;
END;
SELECT #Resultado AS resultado;
END;
So depending on your choice how to handle table existence, calling
EXEC dbo.usp_verificarTabelaCadMov #NomeTabela = 'thistabledoesNOTexist';
would return -1 or NULL (see stored proc comment) and
EXEC dbo.usp_verificarTabelaCadMov #NomeTabela = 'thistableDOESexist';
would return a value > -1.
Fixed it, I had to:
1 - Change my stored procedure to this:
ALTER PROCEDURE dbo.usp_verificarTabelaCadMov
#NomeTabela VARCHAR(20)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #Exists INT
IF EXISTS(SELECT name
FROM sysobjects
WHERE name = #NomeTabela)
BEGIN
SET #Exists = 1
END
ELSE
BEGIN
SET #Exists = 0
END
RETURN #Exists
END
2 - Change my call on Repository to this:
#Query(value = "Declare #Exists int "
+ "Exec #Exists = usp_verificarTabelaCadMov :tabela"
+ " Select #Exists", nativeQuery = true)
RESULT: it's checking whether the table exists or not.

SQL statement that uses a #variable fails when executed in java

I have the following statement which requires an SQL variable:
SELECT #i := 53; UPDATE my_table SET index = (SELECT #i := #i + 1), status = 0 WHERE id = '12345' ORDER BY seqindex ASC;
For some reason it works fine when I execute it on MySQL's workbench but it fails whenever I execute it within Java. I get org.springframework.jdbc.BadSqlGrammarException.
Any help will be appreciated.
Use Single quotes for table name or column name they have underscore like this 'my_table'
SELECT #i := 53; UPDATE 'my_table' SET index = (SELECT #i := #i + 1),
status = 0 WHERE id = '12345' ORDER BY seqindex ASC;

Method to pass arguments to a procedure in oracle database

code in jsp page
cs=conn.prepareCall("{call held('"+session.getAttribute("roll")+"')}");
cs.executeUpdate();
in oracle database procedure is as
create or replace procedure "Held"
(s in Varchar2)
l_col_name varchar2(30);
begin
SELECT SUBJECTCODE
into l_col_name
FROM table02
WHERE SERIALNUMBER = '1';
execute immediate
'UPDATE TABLE01 SET '|| l_col_name || ' = '
|| l_col_name || ' + 1 WHERE Rollno = s'
;
end;
the following is the error
java.sql.SQLException: ORA-00904: "S": invalid identifier ORA-06512: at "ROHIT.HELD", line 12 ORA-06512: at line 1
please rectify it
i am trying to take the value of session ie from code in jsp and trying to use it in oracle database ....
i have used s as variable to store that value and used it in where clause
suggest a solution
I don't know if the jsp side is correct, but you clearly have a bug in your procedure.
When ORA-00904 occurs, you must enter a valid column name as it is
either missing or the one entered is invalid.
Here, your statement WHERE Rollno = s is understand as 'where column rollno is equal to column s', but the column s doesn't exist. I suppose you just have to move s out of the string.
execute immediate
'UPDATE TABLE01 SET '|| l_col_name || ' = '
|| l_col_name || ' + 1 WHERE Rollno = ''' || s || ''''
;

Commit in PLSQL loop for update

I am using the below function in my java program to copy a column from temporary table to main table.
FUNCTION TEST(TBL_NAME VARCHAR2, TMP_TBL_NAME VARCHAR2, ID_COL VARCHAR2, REQ_COL VARCHAR2, BATCH_SIZE NUMBER) RETURN NUMBER AS
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(1) FROM ' || TMP_TBL_NAME INTO TOTAL_RECORDS;
OFFSET := 0;
WHILE OFFSET < TOTAL_RECORDS
LOOP
MAX_RESULTS := OFFSET + BATCH_SIZE;
EXECUTE IMMEDIATE 'SELECT ' || ID_COL || ', ' || REQ_COL || ' FROM ' || TMP_TBL_NAME || ' WHERE SEQ_NBR BETWEEN :OFFSET AND :MAX_RESULTS' BULK COLLECT INTO SEQ_IDS, REQ_COL_VALUESS USING OFFSET, MAX_RESULTS;
FORALL IND IN SEQ_IDS.FIRST .. SEQ_IDS.LAST
EXECUTE IMMEDIATE 'UPDATE ' || TBL_NAME || ' SET ' || REQ_COL || ' = :REQ_COL_VAL WHERE ' || ID_COL || ' = :ID_COL_VAL' USING REQ_COL_VALUESS(IND), SEQ_IDS(IND);
OFFSET := MAX_RESULTS;
COMMIT;
END LOOP;
RETURN 0;
EXCEPTION
WHEN OTHERS THEN
RAISE CUST_EXCEPTION;
END;
Expected result is when I am running with a batch_size of 100000, for each commit 100000 records have to be updated as id_col I used above is primary key. After running the java program, in middle when I am checking for updates in main table I am able to see that records of batches 6469 or 80148 like that are being updated.
There are about 10 million records in temp table. If I remove the forall update statement I am iterating over the data in proper batch size.
Can any clarify me on why this is happening
1) How many records do you have in your temporary tables?
2) Why you don't use sys_refcursor and bulk collect with limit ?
declare
v_cursor sys_refcursor;
res1 sys.DBMS_DEBUG_VC2COLL; -- - predefined collection in oracle
res2 sys.DBMS_DEBUG_VC2COLL;
v_batch_size pls_integer := 50;
begin
open v_cursor for 'Select 1,2 from dual connect by level < 10000';
loop
fetch v_cursor bulk collect into res1,res2 limit v_batch_size;
exit when res1.count =0;
dbms_output.put_line(res1.count);
-- forall ind in res1.first .. res1.last loop e.g
-- commit;
end loop;
close v_cursor;
end;
-- first iteration offset= 0 , max_result = 10
-- v_batch_size = 10
-- offset := 0
-- MAX_RESULTS := 10
select seg from ( Select level seg from dual connect by level < 1000) where seg between 0 and 10;
-- secound iteration
-- v_batch_size = 10
-- offset := 10
-- MAX_RESULTS := 20
select seg from ( Select level seg from dual connect by level < 1000) where seg between 10 and 20;

Categories