Commit in PLSQL loop for update - java

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;

Related

creating a trigger on a dynamically added table inside a procedure

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.

Get Exception while executing a oracle procedure in java

I am trying to execute a procedure in oracle, but i get a exception while executing the procedure.Please help me where i am wrong.
Error :
java.sql.SQLException: ORA-06550: line 1, column 36:
PLS-00103: Encountered the symbol ";" when expecting one of the following:
. ( ) , * # % & = - + < / > at in is mod remainder not rem =>
<an exponent (**)> <> or != or ~= >= <= <> and or like like2
like4 likec between || indicator multiset member submultiset
The symbol ")" was substituted for ";" to continue.
Procedure :
CREATE OR REPLACE PROCEDURE "TEST_101"."AVG_UNLOADING"
(
P_CODE IN VARCHAR2,
P_DATE IN VARCHAR2,
P_VANID IN NUMBER,
P_CURSOR OUT SYS_REFCURSOR
)
IS
BEGIN
OPEN P_CURSOR
FOR
select CARGO_NAME,IMP_EXP_NAME,'TEMP_CARGO_Import',ROUND(SUM(QTY)/((((extract(day from max(END_TIME)-DISCHRG_CMNCD_ANCHRG_TM)*(24*60))+
(extract(day from max(END_TIME)- DISCHRG_CMNCD_ANCHRG_TM)*(60)) + (EXTRACT(DAY FROM max(END_TIME)- DISCHRG_CMNCD_ANCHRG_TM)))-DELAYS)/(24*60))) QTY
from(SELECT (SELECT C.CARGO_CATEGORY_NAME FROM IPT_CARGOMASTER C WHERE C.CARGO_CODE =LL.CARGO_CODE )CARGO_NAME,IL.IMP_EXP_NAME,CASE L.CARGO_TYPE_CODE WHEN 'VC001' THEN LL.DISCHARGE_QUANTITY WHEN 'VC002' THEN LL.QUANTITY_GMT WHEN 'VC004' THEN LL.QUANTITY_GMT
else LL.DISCHARGE_QUANTITY end QTY ,L.DISCHRG_CMNCD_ANCHRG_TM,LL.END_TIME ,(SELECT SUM(T3.TOTAL_TIME) FROM IPT_LOADUNLOADDELAYLINES T3 WHERE T3.ID = L.ID AND T3.MINUS_DELAY_HOURS = 'true') AS DELAYS
,LL.LINE_ID,L.VAN_ID FROM IPT_LOADINGUNLOADING L JOIN IPT_LOADUNLOADOPERATIONLINES LL ON L.ID=LL.ID LEFT JOIN IPT_IMPORTEXPORTFORM I ON I.VAN_ID=L.VAN_ID JOIN IPT_IGMEPCARGOLINES IL ON I.ID=IL.ID AND LL.CARGO_CODE=IL.CARGO_CODE
where L.PORTDETAIL_CODE= P_CODE and I.PORTDETAIL_CODE=L.PORTDETAIL_CODE and IL.IMP_EXP_NAME like '%KKR%' AND LL.END_TIME<=TO_TIMESTAMP(TO_CHAR(P_DATE || ' 06:59'),'dd/MM/yyyy HH:MI')
and L.VAN_ID in (P_VANID )
)t group by CARGO_NAME, IMP_EXP_NAME, 'TEMP_CARGO_Import',DISCHRG_CMNCD_ANCHRG_TM,DELAYS;
END AVG_UNLOADING;
Executing procedure
stkagentlist = "{call AVG_UNLOADING(?,?,?,?}";
callableStatement = conn.prepareCall(stkagentlist);
callableStatement.setString(1, portCode);
callableStatement.setString(2, dt );
callableStatement.setString(3, vanids );
callableStatement.registerOutParameter(4, OracleTypes.CURSOR);
callableStatement.executeUpdate();
stkagentlist = "{call AVG_UNLOADING(?,?,?,?}";
I think in this statement the closing round bracket is missing, it should be:
stkagentlist = "{call AVG_UNLOADING(?,?,?,?)}";

Java call procedure oracle plsql return no data but in plsql return data

I have a function plsql, run function in plsql return cursor have data But i call this function in java problem return resultset falase Help me handling problem this
enter code here
This function plsql
FUNCTION get_canh_bao(
p_toolid in varchar2)
RETURN sys_refcursor IS
s varchar2(4000);
ptype varchar2(1000);
pday varchar2(1000);
psql varchar2(1000);
pcreate varchar2(1000);
re sys_refcursor;
pwhere varchar2(1000);
BEGIN
--
pwhere := '';
s := 'select b.TYPE_REPORT, b.DAY_NHAPLIEU, a.sql_cmd, a.name_createdate'
||' from cpcdata.tbl_config_nhaplieu a'
||' left join cpcdata.tbl_mainmenu b'
||' on a.tool_id = b.ID '
||' where b.show_all = 0 and b.flag = 1 and b.ID = :toolid';
execute immediate s INTO ptype, pday, psql, pcreate using p_toolid;
-- Tinh ngay canh bao
if (INSTR(psql,'where') > 0 ) then
pwhere := ' and ';
else
pwhere := ' where ';
end if;
s := psql ||pwhere||pcreate||' between :aDate AND :bDate';
CASE
WHEN ptype = 'day' THEN
s := psql ||pwhere|| ' to_char('||pcreate||',''dd/mm/yyyy'') = to_char(sysdate - '||pday||',''dd/mm/yyyy'')';
open re for s;
WHEN ptype = 'week' THEN
open re for s USING TRUNC (sysdate, 'iw'), TRUNC(sysdate, 'iw') + 7 - 1/86400- pday;
WHEN ptype = 'month' THEN
open re for s USING TRUNC (sysdate, 'mm'), to_date(LAST_DAY(TRUNC (sysdate, 'mm')) + 1 - 1/86400 - pday,'dd/mm/yyyy');
WHEN ptype = 'quy' THEN
open re for s USING TRUNC (sysdate, 'Q'), to_date(add_months(trunc(sysdate,'Q'),3)- 1 - pday, 'dd/mm/yyyy');
WHEN ptype = 'year' THEN
open re for s USING TRUNC (sysdate , 'YEAR'), to_date(ADD_MONTHS(TRUNC (SYSDATE,'YEAR'),12) - 1 - pday, 'dd/mm/yyyy');
ELSE return null;
END CASE;
dbms_output.put_line(''||s);
RETURN re ;
exception when others then
declare
s_err varchar2(2000);
str varchar(2000);
c_err sys_refcursor;
begin
s_err := 'loi khi lay du lieu '|| sqlerrm;
str := 'select '||s_err||' from dual';
open c_err for str;
return c_err;
end;
END;
This is code java
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection conn =
DriverManager.getConnection("jdbc:oracle:thin:#***:**:**",
"***", "***");
System.out.println("Connect db success ! ");
ArrayList arr = new ArrayList();
String sql = "{? = call get_canh_bao(?)}";
CallableStatement cs = conn.prepareCall(sql);
cs.registerOutParameter(1, OracleTypes.CURSOR);
cs.setString(2, "502");
cs.executeQuery();
ResultSet rs = (ResultSet)cs.getObject(1);
System.out.println("------------ResultSet---------------"+rs.next());
while (rs.next()) {
System.out.println("a");
String[] str =
{"1" };
arr.add(str);
}
System.out.println("size="+arr.size());
Result when run code in java
Connect db success !
------------ResultSet---------------false
size=0
The problem is likely caused by one of the date conversions that uses an implicit date format.
Oracle does not have a "default" date format, the date format is always dependent on the client. Your Java client and database IDE likely have different defaults.
But don't fix the code by changing the client settings. Instead, clean up the date functions and remove all date to character conversions.
In general, when working with dates, TO_DATE and TO_CHAR should almost always be avoided. If a date needs to be manipulated, there is almost surely a date function
that does not require a conversion. And dates should always be stored as dates, so you should not need to convert back and forth from the table.
The biggest problem is probably this:
pcreate varchar2(1000);
Change that to a date. If for some reason a.name_createdate is a string, then at least make the conversion explicit.
You're populating pcreate (a string) from tbl_config_nhaplieu.name_createdate without specifying a format, so (assuming it is a date column) it is possible that the format could be different within the Java client environment, e.g. if it defaults to mm/dd/yyyy or dd-mon-yyyy instead of dd/mm/yyyy.
Later you compare it directly to dates:
s := psql || pwhere || pcreate || ' between :aDate and :bDate';
but without quoting, so you'll get a query like this:
where 01/02/2016 between :aDate and nDate
Elsewhere you convert it back to a date assuming dd/mm/yyyy format (which you didn't specify when you encoded it as a string earlier, so there's no reason it should match that format):
s := psql || pwhere || ' to_char(' || pcreate || ',''dd/mm/yyyy'') = to_char(sysdate - ' || pday || ',''dd/mm/yyyy'')';
but it's not quoted there either, so you'll get something like this:
where to_char(01/02/2016,'dd/mm/yyyy') = to_char(sysdate - 3,'dd/mm/yyyy')
which will fail.
Then there's this:
to_date(last_day(trunc(sysdate,'mm')) + 1 - 1 / 86400 - pday, 'dd/mm/yyyy')
which may not give the result you expect. For example:
select to_date(last_day(trunc(sysdate,'mm')) + 1 - 1 / 86400 - 0, 'dd/mm/yyyy') as test
from dual;
which gives me 31-JUL-2016 in SQL*Plus and SQL Developer but 31/07/0016 in PL/SQL Developer, due to different nls_date_parameter settings.
As Jon said, you need to clean up all of the date handling.

How to use SQL Case in Group by Clause?

I'm using SQL Case in my select and in group by clause and I'm working in JAVA. Whenever I execute my java program it says:
Column 'dbo.JOHN_Dashboard.Log_Date' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
My Query is:
SELECT EP.Site_Code AS [Site_Code], DB.[Site] AS [Site], DB.[Utility] AS [Utility],
CASE ? WHEN 'Raw' THEN dateadd(mi,datediff(mi,0,DB.[log_date]),0)
WHEN 'Hour' THEN dateadd(hh,datediff(hh,0,DB.[log_date]),0)
WHEN 'Day' THEN dateadd(dd,datediff(dd,0,DB.[log_date]),0)
WHEN 'Week' THEN dateadd(wk,datediff(wk,0,DB.[log_date]),0)
WHEN 'Month' THEN dateadd(mm,datediff(mm,0,DB.[log_date]),0)
WHEN 'Year' THEN dateadd(yy,datediff(yy,0,DB.[log_date]),0)
ELSE DB.[log_date]
END AS [log_date],
SUM(CASE WHEN DB.[value] >= 0 THEN DB.[value] ELSE 0 END) AS [value],
SUM(CASE WHEN DB.[Cost] >=0 THEN DB.[cost] ELSE 0 END) AS [Cost],
SUM(CASE WHEN DB.[CO2] >=0 THEN DB.[CO2] ELSE 0 END) AS [CO],
MT.[Meter_type_name] AS [Meter Type],
MN.[Meter_Name] AS [Meter Name],
U.[Unit_Name] AS [Units],
EP.EnergyPoint_ID AS [Meter_ID],
EP.Parent_ID AS [Parent],
EP.Meter_Description AS [Meter_Description]
FROM [dbo].[JOHN_Dashboard] DB
INNER JOIN [dbo].[EnergyPoints] EP ON DB.[EnergyPoint_ID] = EP.[EnergyPoint_ID]
INNER JOIN [dbo].[Meter_Types] MT ON MT.[Meter_Type_ID] = EP.[Meter_Type_ID]
INNER JOIN [dbo].[Meter_Names] MN ON MN.[Meter_Name_ID] = EP.[Meter_Name_ID]
INNER JOIN [dbo].[Units] U ON U.[Unit_ID] = EP.[Unit_id]
WHERE [log_date] >= ? AND [Log_Date] < DATEADD(DAY, 1, ?)
AND ( ? IS NULL OR EP.Energypoint_ID = ?)
GROUP BY EP.Site_Code, DB.[Site], DB.[Utility], MT.[Meter_type_name],
MN.[Meter_Name], U.[Unit_Name], EP.[EnergyPoint_ID],
EP.[Parent_ID], EP.[Meter_Description],
CASE ? WHEN 'Raw' THEN dateadd(mi,datediff(mi,0,DB.[log_date]),0)
WHEN 'Hour' THEN dateadd(hh,datediff(hh,0,DB.[log_date]),0)
WHEN 'Day' THEN dateadd(dd,datediff(dd,0,DB.[log_date]),0)
WHEN 'Week' THEN dateadd(wk,datediff(wk,0,DB.[log_date]),0)
WHEN 'Month' THEN dateadd(mm,datediff(mm,0,DB.[log_date]),0)
WHEN 'Year' THEN dateadd(yy,datediff(yy,0,DB.[log_date]),0)
ELSE DB.[log_date] END ;
The parameters i'm passing are:
'Week'
'2016-05-16'
'2016-05-22'
6044
6044
'Week'
Note: This query runs without error in SQL Management Studio.
As requested here is a reworked version of your code using a sub-query before grouping. Since I don't have your database I can't guarantee that I have everything exactly right but give this a try.
I recommend always using a sub-query when your group by has complicated logic that will be repeated in the select. Some people would probably drop the second criteria and just say whenever the group by has complicated logic.
SELECT sub.Site_Code, sub.[Site], sub.[Utility], sub.[Meter Type],
sub.[log_date],
SUM(sub.[value]) as [value],
SUM(sub.[Cost]) as [cost],
SUM(sub.[CO]) as [CO],
sub.[Meter Name], sub.[Units], sub.[Meter_ID],
sub.[Parent], sub.[Meter_Description]
FROM (
SELECT EP.Site_Code AS [Site_Code], DB.[Site] AS [Site], DB.[Utility] AS [Utility],
CASE ? WHEN 'Raw' THEN dateadd(mi,datediff(mi,0,DB.[log_date]),0)
WHEN 'Hour' THEN dateadd(hh,datediff(hh,0,DB.[log_date]),0)
WHEN 'Day' THEN dateadd(dd,datediff(dd,0,DB.[log_date]),0)
WHEN 'Week' THEN dateadd(wk,datediff(wk,0,DB.[log_date]),0)
WHEN 'Month' THEN dateadd(mm,datediff(mm,0,DB.[log_date]),0)
WHEN 'Year' THEN dateadd(yy,datediff(yy,0,DB.[log_date]),0)
ELSE DB.[log_date]
END AS [log_date],
CASE WHEN DB.[value] >= 0 THEN DB.[value] ELSE 0 END AS [value],
CASE WHEN DB.[Cost] >=0 THEN DB.[cost] ELSE 0 END AS [Cost],
CASE WHEN DB.[CO2] >=0 THEN DB.[CO2] ELSE 0 END AS [CO],
MT.[Meter_type_name] AS [Meter Type],
MN.[Meter_Name] AS [Meter Name],
U.[Unit_Name] AS [Units],
EP.EnergyPoint_ID AS [Meter_ID],
EP.Parent_ID AS [Parent],
EP.Meter_Description AS [Meter_Description]
FROM [dbo].[JOHN_Dashboard] DB
INNER JOIN [dbo].[EnergyPoints] EP ON DB.[EnergyPoint_ID] = EP.[EnergyPoint_ID]
INNER JOIN [dbo].[Meter_Types] MT ON MT.[Meter_Type_ID] = EP.[Meter_Type_ID]
INNER JOIN [dbo].[Meter_Names] MN ON MN.[Meter_Name_ID] = EP.[Meter_Name_ID]
INNER JOIN [dbo].[Units] U ON U.[Unit_ID] = EP.[Unit_id]
WHERE [log_date] >= ? AND [Log_Date] < DATEADD(DAY, 1, ?)
AND ( ? IS NULL OR EP.Energypoint_ID = ?)
) sub
GROUP BY sub.Site_Code, sub.[Site], sub.[Utility], sub.[Meter Type],
sub.[Meter Name], sub.[Units], sub.[Meter_ID],
sub.[Parent], sub.[Meter_Description], sub.[log_date];

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 || ''''
;

Categories