I'm trying to create a table within PL/SQL
how I can achieve that?
keep getting
Error report:
ORA-00933: "SQL command not properly ended"
here is the code that I have error with
DECLARE
station_id_ms1 NUMBER :=10347;
realtime_start DATE :=to_date('2012-01-01 00:00:00','YYYY-DD-MM HH24:MI:SS');
realtime_end DATE :=to_date('2012-07-01 00:00:00','YYYY-DD-MM HH24:MI:SS');
BEGIN
EXECUTE IMMEDIATE ('
CREATE TABLE new_table_name
AS
SELECT
((realtime - to_date(''01-JAN-1970'',''DD-MON-YYYY'')) * (86400)) AS realtime_ms1,
magnetic_ms_id,
ADC_value_pp_2_mgntc_fld_amp(ch2_value,ch2_gain_value,magnetic_ms_id,2) AS B_x_ms1,
ADC_value_pp_2_mgntc_fld_amp(ch1_value,ch1_gain_value,magnetic_ms_id,1) AS B_y_ms1,
real_nanosecs2*4/3*360/20e6 AS phase_x_ms1,
real_nanosecs1*4/3*360/20e6 AS phase_y_ms1
FROM
raw_mag
WHERE
magnetic_ms_id = '||station_id_ms1||'
AND realtime > '||realtime_start||'
AND realtime < '||realtime_end||'
AND ch1_tune_value = 0
AND realtime < pkg_timezone.change_timezone(gettime,''CET'',''UTC'')
');
END;
You should do the char-to-date conversion within the plsql-string that you excecute immediate.
The date you declared will be "back-cast" to a varchar2 in the concatenation and "re-cast" into a date again for the execution of the create table statement. And "all sorts of things" can happen in these two casts, so you want to make sure you're in control how the character-string is interpreted when cast to a date.
DECLARE
station_id_ms1 NUMBER :=10347;
realtime_start VARCHAR2(100) :='2012-01-01 00:00:00';
realtime_end VARCHAR2(100) :='2012-07-01 00:00:00';
BEGIN
EXECUTE IMMEDIATE ('
CREATE TABLE new_table_name
AS
SELECT
((realtime - to_date(''01-JAN-1970'',''DD-MON-YYYY'')) * (86400)) AS realtime_ms1,
magnetic_ms_id,
ADC_value_pp_2_mgntc_fld_amp(ch2_value,ch2_gain_value,magnetic_ms_id,2) AS B_x_ms1,
ADC_value_pp_2_mgntc_fld_amp(ch1_value,ch1_gain_value,magnetic_ms_id,1) AS B_y_ms1,
real_nanosecs2*4/3*360/20e6 AS phase_x_ms1,
real_nanosecs1*4/3*360/20e6 AS phase_y_ms1
FROM
raw_mag
WHERE
magnetic_ms_id = '||station_id_ms1||'
AND realtime > to_date(''' || realtime_start || ''', ''YYYY-DD-MM HH24:MI:SS'')
AND realtime < to_date(''' || realtime_end || ''', ''YYYY-DD-MM HH24:MI:SS'')
AND ch1_tune_value = 0
AND realtime < pkg_timezone.change_timezone(gettime,''CET'',''UTC'')
');
END;
I would use binds for station_id_ms1, realtime_start, realtime_end:
EXECUTE IMMEDIATE '
...
WHERE
magnetic_ms_id = :station_id_ms1
AND realtime > :realtime_start
AND realtime < :realtime_end
...
' USING IN station_id_ms1, realtime_start, realtime_end
Related
CREATE TABLE COPPER_TAN_META
(
ID decimal(22,0) PRIMARY KEY,
NOTES clob,
ERROR varchar2(2000)
);
CREATE UNIQUE INDEX SYS_C0070016 ON COPPER_TAN_META(ID);
Before update
ID ERROR NOTES
20 <null> 27-APR-21 08.48.18 AM - XML is not a full-text article;
Update
update COPPER_TAN_META set error = 'trigger warning' where id = 20;
With trigger, I'd like to see this:
ID ERROR NOTES
20 trigger warning 27-APR-21 08.48.18 AM - XML is not a full-text article; <timestamp> - trigger warning;
My trigger doesn't work:
CREATE OR REPLACE TRIGGER copper_error_appends_to_note
AFTER UPDATE of error on COPPER_TAN_META
for each row
begin
IF :new.error is not null THEN
:new.notes := :old.notes || localtimestamp(0) || ' - ' || :new.error || '; ';
END IF;
end;
/
Error: ORA-04098: trigger 'B026.COPPER_ERROR_APPENDS_TO_NOTE' is invalid and failed re-validation
This works fine using a normal update statement, like
update copper_tan_meta
set notes = notes || localtimestamp(0) || ' - ' || :fileError || '; '
where id = :tanMetaId
I'm running this on SQuirreL SQL Client Version 3.5.3
new values cannot be changed for AFTER trigger type, but possible for BEFORE
localtimestamp without argument might be used within the direct concatenation, but localtimestamp with precision argument such as localtimestamp(0) could be used within a SQL statement.
So, rewrite the code block as
CREATE OR REPLACE TRIGGER copper_error_appends_to_note
BEFORE UPDATE OF ERROR ON copper_tan_meta
FOR EACH ROW
DECLARE
ts TIMESTAMP;
BEGIN
SELECT localtimestamp(0) INTO ts FROM dual;
IF :new.error IS NOT NULL THEN
:new.notes := :old.notes || ts || ' - ' || :new.error || '; ';
END IF;
END;
/
For the below procedure I am getting the ora error as mentioned in the title while running in Oracle SQL developer
DECLARE
sqlstr VARCHAR2(1000);
CURSOR TabSubPartitions IS
SELECT TABLE_NAME, PARTITION_NAME
FROM USER_TAB_PARTITIONS
WHERE TABLE_NAME = 'PART_TABLE'
ORDER BY PARTITION_NAME;
BEGIN
FOR aSubPart IN TabSubPartitions LOOP
IF TRUNC(LAST_DAY(SYSDATE)) = '31-07-2020' THEN
sqlstr := 'ALTER TABLE '||aSubPart.TABLE_NAME||' MODIFY PARTITION '||aSubPart.PARTITION_NAME|| ' ADD SUBPARTITION ' ||aSubPart.PARTITION_NAME||'_'||TO_CHAR(TRUNC(LAST_DAY(SYSDATE))+1, 'MON_YYYY')||' VALUES LESS THAN ( '||TO_DATE(TRUNC(LAST_DAY(SYSDATE))+2, 'SYYYY-MM-DD HH24:MI:SS' , 'NLS_CALENDER=GREGORIAN')||')' ;
EXECUTE IMMEDIATE sqlstr;
END IF;
END LOOP;
END;
Could anyone please help me. Many thanks in advance.
The main issue in the block itself is is here:
'NLS_CALENDER=GREGORIAN'
where it must be
'NLS_CALENDAR=GREGORIAN'
Moreover, you should not rely on implicite date conversions.
But there is also an issue with your resulting ALTER TABLE statement, which looks something like
ALTER TABLE MODIFY PARTITION ... VALUES LESS THAN ( 02.08.2020 00:00:00)
depending on session date settings (again because of implict date conversion). I doubt that Oracle accepts a timestamp like this. Make sure you produce a proper date literal (like DATE '2020-08-02') instead.
The whole corrected code:
DECLARE
sqlstr VARCHAR2(1000);
CURSOR TabSubPartitions IS
SELECT TABLE_NAME, PARTITION_NAME
FROM USER_TAB_PARTITIONS
WHERE TABLE_NAME = 'PART_TABLE'
ORDER BY PARTITION_NAME;
BEGIN
FOR aSubPart IN TabSubPartitions LOOP
IF TRUNC(LAST_DAY(SYSDATE)) = DATE '2020-07-31' THEN
sqlstr :=
'ALTER TABLE ' || aSubPart.TABLE_NAME || ' MODIFY PARTITION ' || aSubPart.PARTITION_NAME ||
' ADD SUBPARTITION ' || aSubPart.PARTITION_NAME || '_' || TO_CHAR(TRUNC(LAST_DAY(SYSDATE))+1, 'MON_YYYY') ||
' VALUES LESS THAN (DATE ''' || TRIM(TO_CHAR(TRUNC(LAST_DAY(SYSDATE))+2, 'SYYYY-MM-DD', 'NLS_CALENDAR=GREGORIAN')) || ''')' ;
EXECUTE IMMEDIATE sqlstr;
END IF;
END LOOP;
END;
I have several pieces of advice.
First, modify your code to output the value of sqlstr, so you can see exactly what the SQL statement is that you are trying to execute. This should make it easier to understand where the syntax error is.
Second, change TRUNC(LAST_DAY(SYSDATE)) = '31-07-2020' to use an explicit date format when converting from date to string. Something like TO_CHAR(TRUNC(LAST_DAY(SYSDATE)), 'DD-MM-YYYY') .... I don't think this is related to your error, but it is better practice than relying on the implicit conversion format.
Third, look carefully at the TO_DATE call. As it is you appear to be calling TO_DATE with date parameter, then implicitly converting that back to a string. At best that's not necessary, and at worst it will cause unexpected behavior. I suspect you may simply mean to use TO_CHAR where you currently have TO_DATE.
I have cursor which is selecting data and it is running quite fine
CURSOR Crs_c1 IS
SELECT a.GLOBAL_ACCOUNT_CODE,
a.LOCAL_ACCOUNT_CODE,
a.LOCAL_ACCOUNT_NAME,
c.GLOBAL_ACCOUNT_TYPE t_conto,
ltrim(rtrim(a.GLOBAL_ACCOUNT_CODE))||ltrim(rtrim(a.LOCAL_ACCOUNT_CODE)) GLOBAL_LOCAL
FROM V_LOCAL_ACCOUNTS#GDW_LIVE a,V_GLOBAL_ACCOUNTS#GDW_LIVE c --V_LOCAL_ACCOUNTS#GDWPP_ANY a,V_GLOBAL_ACCOUNTS#GDWPP_ANY c
WHERE a.enabled_flag = 'Y'
AND a.GLOBAL_ACCOUNT_CODE=c.GLOBAL_ACCOUNT_CODE
AND not exists (SELECT 1
FROM conto_gdw b -- considero solo i conti nuovi
WHERE a.LOCAL_ACCOUNT_CODE = b.c_conto_local_gdw
AND a.GLOBAL_ACCOUNT_CODE=b.C_CONTO_GLOBAL_GDW);
But at the time of insertion the code fails....
INSERT INTO conto_gdw
(C_CONTO_LOCAL_GDW,
S_CONTO_LOCAL_GDW,
C_CONTO_GLOBAL_GDW,
T_CONTO,
D_INIZIO,
GLOBAL_LOCAL)
VALUES (Rec_Crs_c1.LOCAL_ACCOUNT_CODE,
nvl(Rec_Crs_c1.LOCAL_ACCOUNT_NAME,'.'),
Rec_Crs_c1.GLOBAL_ACCOUNT_CODE,
Rec_Crs_c1.t_conto,
(v_anno_in * 100) + v_mese_in,
Rec_Crs_c1.GLOBAL_LOCAL);
May be this problem is occuring because of a single tuple (dataset). How can I find that culprit data?
I have checked all the data from cursor selection by taking it in a excel sheet, I could not find anything manually. Is there any way ORACLE would return me the failed data?
You need to have a proper exception handling while inserting data .Provide the output of the pl sql block .After than we can help you.
DECLARE
CURSOR Crs_c1
IS
SELECT a.GLOBAL_ACCOUNT_CODE,
a.LOCAL_ACCOUNT_CODE,
a.LOCAL_ACCOUNT_NAME,
c.GLOBAL_ACCOUNT_TYPE t_conto,
LTRIM (RTRIM (a.GLOBAL_ACCOUNT_CODE))
|| LTRIM (RTRIM (a.LOCAL_ACCOUNT_CODE))
GLOBAL_LOCAL
FROM V_LOCAL_ACCOUNTS#GDW_LIVE a, V_GLOBAL_ACCOUNTS#GDW_LIVE c
WHERE a.enabled_flag = 'Y'
AND a.GLOBAL_ACCOUNT_CODE = c.GLOBAL_ACCOUNT_CODE
AND NOT EXISTS
(SELECT 1
FROM conto_gdw b -- considero solo i conti nuovi
WHERE a.LOCAL_ACCOUNT_CODE = b.c_conto_local_gdw
AND a.GLOBAL_ACCOUNT_CODE =
b.C_CONTO_GLOBAL_GDW);
Rec_Crs_c1 Crs_c1%ROWTYPE;
BEGIN
OPEN Crs_c1;
LOOP
FETCH Crs_c1 INTO Rec_Crs_c1;
EXIT WHEN Crs_c1%NOTFOUND;
INSERT INTO conto_gdw (C_CONTO_LOCAL_GDW,
S_CONTO_LOCAL_GDW,
C_CONTO_GLOBAL_GDW,
T_CONTO,
D_INIZIO,
GLOBAL_LOCAL)
VALUES (Rec_Crs_c1.LOCAL_ACCOUNT_CODE,
NVL (Rec_Crs_c1.LOCAL_ACCOUNT_NAME, '.'),
Rec_Crs_c1.GLOBAL_ACCOUNT_CODE,
Rec_Crs_c1.t_conto,
(v_anno_in * 100) + v_mese_in,
Rec_Crs_c1.GLOBAL_LOCAL);
END LOOP;
CLOSE Crs_c1;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line ('Error Message' || SQLERRM);
DBMS_OUTPUT.put_line( 'GLOBAL_ACCOUNT_CODE :'
|| Rec_Crs_c1.LOCAL_ACCOUNT_CODE
|| 'LOCAL_ACCOUNT_CODE '
|| Rec_Crs_c1.LOCAL_ACCOUNT_NAME
|| 'GLOBAL_ACCOUNT_CODE :'
|| Rec_Crs_c1.GLOBAL_ACCOUNT_CODE
|| 'GLOBAL_ACCOUNT_TYPE '
|| Rec_Crs_c1.t_conto
|| 'GLOBAL_LOCAL'
|| Rec_Crs_c1.GLOBAL_LOCAL);
END;
I have a supplier_product table ( supp_id, prod_id, invoice_id,price) and an invoice table (invoice_id, balance). I tried a stored proc. given (supp_id) it should all the existing invoice_id and display the balance. here's my code:
set serverouput on;
create or replace
Procedure SUP_loop
(v_SUPPLIER_ID int )
AS
CURSOR c_SUP IS
select SUPPLIER_ID , SUPP_INVOICE_ID, balance
from SUPPLIER_PRODUCT, supplier_invoice
where SUPPLIER_ID=v_SUPPLIER_ID
and supp_invoice_id.supplier_product=supp_invoice_id.supplier_invoice;
BEGIN
--LOOP WITH IMPLICIT VARIABLE DECLARED
--AUTOMATIC, OPEN FETCH, CLOSE
FOR v_SUP_data IN c_SUP LOOP
DBMS_OUTPUT.PUT_LINE(TO_CHAR(v_SUP_data.SUPPLIER_ID) || ' ' ||
TO_CHAR(v_SUP_data.SUPP_INVOICE_ID) || ' ' ||
TO_CHAR(v_SUP_data.balance) );
END LOOP;
END;
/
the error i am getting is v_sup_data Error(20,31): PLS-00364: loop index variable 'V_SUP_DATA' use is invalid
Error(9,74): PL/SQL: ORA-00904: "SUPP_INVOICE_ID"."SUPPLIER_INVOICE": invalid identifier
You have the field and the table names swapped round the wrong way.
You have...
supp_invoice_id.supplier_invoice
...where you should have...
supplier_invoice.supp_invoice_id
:D
The syntax for referring to a column is <>.<>. So your cursor query needs the join condition to be supplier_produce.supp_invoice_id = supplier_invoice.supp_invoice_id, i.e.
create or replace
Procedure SUP_loop
(v_SUPPLIER_ID int )
AS
CURSOR c_SUP IS
select SUPPLIER_ID , SUPP_INVOICE_ID, balance
from SUPPLIER_PRODUCT, supplier_invoice
where SUPPLIER_ID=v_SUPPLIER_ID
and supplier_product.supp_invoice_id = supplier_invoice.supp_invoice_id;
BEGIN
--LOOP WITH IMPLICIT VARIABLE DECLARED
--AUTOMATIC, OPEN FETCH, CLOSE
FOR v_SUP_data IN c_SUP LOOP
DBMS_OUTPUT.PUT_LINE(TO_CHAR(v_SUP_data.SUPPLIER_ID) || ' ' ||
TO_CHAR(v_SUP_data.SUPP_INVOICE_ID) || ' ' ||
TO_CHAR(v_SUP_data.balance) );
END LOOP;
END;
/
The stored procedures being written here currently concats the parameters to the queries:
SELECT *
FROM Names
WHERE Name = ' || prmName || '
ORDER BY ' || prmSortField
Is it possible to parameterize this query inside the stored procedure? Possibly like:
query = 'select * From Names Where Name = #name Order By ' || prmSortField
call(query, prmName)
Note: In case you wonder why we do so, there are two common parameters for our sp's: sortFieldIndex and sortDirection. Since we cannot directly parameterize these, the query is dynamically generated. But other parameters make the queries open for injection. So I am looking a way to parameterize some of the parameters.
Absolutely. Use cursors.
DECLARE
CURSOR c1 (job VARCHAR2, max_wage NUMBER) IS
SELECT * FROM employees WHERE job_id = job AND salary > max_wage;
BEGIN
FOR person IN c1('CLERK', 3000)
LOOP
-- process data record
DBMS_OUTPUT.PUT_LINE('Name = ' || person.last_name || ', salary = ' ||
person.salary || ', Job Id = ' || person.job_id );
END LOOP;
END;
For a dynamic query with bind values, do this:
procedure p (prmName varchar2, prmSortField varchar2)
is
query varchar2(100);
rc sys_refcursor;
names_rec names%rowtype;
begin
query = 'select * From Names Where Name = :name Order By ' || prmSortField
open rc for query using prmName;
loop
fetch rc into names_rec;
exit when rc%notfound;
-- process this row
end loop;
close rc;
end;
For a more elaborate procedure that supports optional parameter values (but uses sys context), check out the following post on Asktom.com
PRATTY -- Thanks for the question regarding 'CURSOR'...