Pl/SQL problem with syntax in validation code - sql

Trying to write this type of script in PL / SQL on the second line with "WHEN", I get a syntax error. Please help
The function should validate and contain the logic it is trying to write
I don't have much experience. How could I write it differently?
create or replace FUNCTION BUSINESS_PROVIDER_GET(valueGet IN VARCHAR2)
RETURN VARCHAR2
IS
v_value business_provider_configuration.billing_account_id%TYPE ;
BEGIN
DECLARE
V_BUSINESS_PROVIDER business_provider_configuration.business_provider%TYPE;
V_TRADING_NAME business_provider_configuration.trading_name%TYPE;
V_CUSTOMER_ID business_provider_configuration.customer_id%TYPE;
V_PROVIDER BUSINESS_PROVIDER_CONFIGURATION%TYPE;
BEGIN
SELECT
*
INTO V_BUSINESS_PROVIDER
FROM
BUSINESS_PROVIDER_CONFIGURATION
WHERE
V_BUSINESS_PROVIDER = valueGet
AND
Upper(V_BUSINESS_PROVIDER) = Upper (valueGet);
EXCEPTION
WHEN no_data_found THEN
SELECT
*
INTO V_TRADING_NAME
FROM
BUSINESS_PROVIDER_CONFIGURATION
WHERE
V_TRADING_NAME = valueGet
AND
Upper(V_TRADING_NAME) = Upper (valueGet);
EXCEPTION
WHEN no_data_found THEN
SELECT
*
INTO V_CUSTOMER_ID
FROM
BUSINESS_PROVIDER_CONFIGURATION
WHERE
V_CUSTOMER_ID = valueGet
AND
Upper(V_CUSTOMER_ID) = Upper (valueGet);
EXCEPTION
WHEN no_data_found
raise_application_error(-20000, 'Not found');

Exception is 'end-of-block' keyword - you can't just pull it out of nowhere multiple times in the same block.
In function you are allowed to have one exception statement, because function body is block on it's own.
If inside exception block you want to handle another exception, the code which may raise that exception must be inside a block.
It helps a lot to fix such issues when code is properly indented - i had to do it here:
create or replace FUNCTION BUSINESS_PROVIDER_GET(valueGet IN VARCHAR2)
RETURN VARCHAR2
IS
v_value business_provider_configuration.billing_account_id%TYPE ;
BEGIN
DECLARE
V_BUSINESS_PROVIDER business_provider_configuration.business_provider%TYPE;
V_TRADING_NAME business_provider_configuration.trading_name%TYPE;
V_CUSTOMER_ID business_provider_configuration.customer_id%TYPE;
V_PROVIDER BUSINESS_PROVIDER_CONFIGURATION%TYPE;
BEGIN
SELECT *
INTO V_BUSINESS_PROVIDER
FROM BUSINESS_PROVIDER_CONFIGURATION
WHERE V_BUSINESS_PROVIDER = valueGet
AND Upper(V_BUSINESS_PROVIDER) = Upper (valueGet);
EXCEPTION
WHEN no_data_found THEN
BEGIN -- Block that can raise second exception
SELECT *
INTO V_TRADING_NAME
FROM BUSINESS_PROVIDER_CONFIGURATION
WHERE V_TRADING_NAME = valueGet
AND Upper(V_TRADING_NAME) = Upper (valueGet);
EXCEPTION
WHEN no_data_found THEN
BEGIN -- Block that can raise third exception
SELECT *
INTO V_CUSTOMER_ID
FROM BUSINESS_PROVIDER_CONFIGURATION
WHERE V_CUSTOMER_ID = valueGet
AND Upper(V_CUSTOMER_ID) = Upper (valueGet);
EXCEPTION
WHEN no_data_found
raise_application_error(-20000, 'Not found');
END; -- End of block that can raise third exception
END; -- End of block that can raise second exception
END BUSINESS_PROVIDER_GET; -- End of function body
I do not have your tables so i was not able to test it, there might still be some issues here.
Also, the code is a little bit of mess- you should not have to look in multiple columns that have distinct business meaning for just one value.
And i have no idea, how do you want to 'select *' into single scalar variable from a table, that has multiple columns - just specify, which column you want to fetch.
I would argue that 'select *' should never be used in stored code, unless you fetch into record variable that inherits rowtype from table.

First of all, the conditions as you are using them will never return any values. You are defining the variable, for example V_BUSINESS_PROVIDER and right after that compare it against valueGet. Since the V_BUSINESS_PROVIDER is null the result of conditions will be always false
Second, the set of conditions
V_BUSINESS_PROVIDER = valueGet
AND Upper(V_BUSINESS_PROVIDER) = Upper(valueGet);
is reduntant. If you need values to be exactly the same case (e.g. 'Word' <> 'wORD') you have to use only lines like this
V_BUSINESS_PROVIDER = valueGet
otherwise, if you don't need to compare cases (e.g. 'Word' = 'wORD') you need to use such conditions only
AND Upper(V_BUSINESS_PROVIDER) = Upper(valueGet)
Third. As I see the aim is to get column values into variables named after them. In this case you have to specify the column names in the select statement. E.g.
select business_provider
into V_BUSINESS_PROVIDER
FROM BUSINESS_PROVIDER_CONFIGURATION
....
Fourth thing is the using exception handlers for non-handling purpose is a bad idea. I'd recomment you to rewrite this part like the following
create or replace FUNCTION BUSINESS_PROVIDER_GET(valueGet IN VARCHAR2)
RETURN VARCHAR2
IS
v_value business_provider_configuration.billing_account_id%TYPE ;
V_BUSINESS_PROVIDER business_provider_configuration.business_provider%TYPE;
V_TRADING_NAME business_provider_configuration.trading_name%TYPE;
V_CUSTOMER_ID business_provider_configuration.customer_id%TYPE;
V_PROVIDER BUSINESS_PROVIDER_CONFIGURATION%TYPE;
BEGIN
begin
select ... into V_BUSINESS_PROVIDER ...;
exception
when no_data_found then
V_BUSINESS_PROVIDER := null;
end;
if V_BUSINESS_PROVIDER is null then
begin
select ... into V_TRADING_NAME ...;
exception
when no_data_found then
V_TRADING_NAME := null;
end;
end if;
if V_TRADING_NAME is null then
begin
select ... into V_CUSTOMER_ID ...;
exception
when no_data_found then
raise(...); -- though I'm not sure if you have to raise it, the no_data_found will get raised anyway you can handle the native exception
end;
end if;
end BUSINESS_PROVIDER_GET;

Related

Is there other exceptions than no_data_needed that are caught under the hood by the oracle?

Info:
No data needed occurs too, when you query a pipelined function that should return n elements. But you have added the condition nb_row< m with m<n.
exemple:
select *
from table ( my_pieplined_function()) -- this function should returns 20 elements
where rownum < 10;
It should occurs with fetch next n rows only. But I've tested that yet.
I have learned that I don't need to handle the exception "no_data_needed" that occurs in a pipelined function. When I'm doing the query, logically oracle catch this exception , take the lines that have been piped and do nothing with the rest.
let oracle catch the exception is equivalent to that.
create my_pieplined_function return sys.odcinumberlist is
i integer:=0;
begin
for loop
pipe row (i);
i:=i+1;
if i=20 then return i; end;
end loop;
exception
when no_data_needed then null;
end;
more info there https://asktom.oracle.com/Misc/nodataneeded-something-i-learned.html
question
I would like to know if there are other exception that are caught by oracle without having to do something. If yes, where can I find the list of these exceptions.
other question :
why don't I find no_data_needed in [summary of predefined exception] (https://docs.oracle.com/cd/B14117_01/appdev.101/b10807/07_errs.htm)
why don't I find no_data_needed in [summary of predefined exception] (https://docs.oracle.com/cd/B14117_01/appdev.101/b10807/07_errs.htm)
Because it was not documented in that version.
It is in the Oracle 11g Database PL/SQL Language Reference.
I have learned that I don't need to handle the exception no_data_needed that occurs in a pipelined function.
You only do not need to handle that exception if you have nothing that needs doing when cleaning up after the pipelined function.
If you do:
CREATE FUNCTION my_pipelined_function
RETURN sys.odcinumberlist PIPELINED
IS
BEGIN
DBMS_OUTPUT.PUT_LINE( 'Initialise' );
FOR i IN 1 .. 2 LOOP
DBMS_OUTPUT.PUT_LINE( 'Loop ' || i );
pipe row (i);
END LOOP;
DBMS_OUTPUT.PUT_LINE( 'Cleanup' );
RETURN;
END;
/
If you call the function then using:
SELECT * FROM TABLE(my_pipelined_function());
Then the output is:
COLUMN_VALUE
1
2
Initialise
Loop 1
Loop 2
Cleanup
But if you limit the number of rows:
SELECT * FROM TABLE(my_pipelined_function()) WHERE rownum = 1;
Then the output will be:
COLUMN_VALUE
1
Initialise
Loop 1
and the Cleanup section is never reached as you stop calling the function in the middle of the loop.
If you define the function as:
CREATE FUNCTION my_pipelined_function
RETURN sys.odcinumberlist PIPELINED
IS
BEGIN
DBMS_OUTPUT.PUT_LINE( 'Initialise' );
FOR i IN 1 .. 2 LOOP
DBMS_OUTPUT.PUT_LINE( 'Loop ' || i );
pipe row (i);
END LOOP;
DBMS_OUTPUT.PUT_LINE( 'Cleanup 1' );
RETURN;
EXCEPTION
WHEN NO_DATA_NEEDED THEN
DBMS_OUTPUT.PUT_LINE( 'Cleanup 2' );
RETURN;
END;
/
and call it using:
SELECT * FROM TABLE(my_pipelined_function()) WHERE rownum = 1;
Then the output will be:
COLUMN_VALUE
1
Initialise
Loop 1
Cleanup 2
So you don't need to handle that exception if your pipelined function has nothing to do when the function finishes; however, if you initialise something and need to clean it up at the end of the function then you should handle the NO_DATA_NEEDED exception as it will allow you to perform the clean up when the loop is terminated early.
db<>fiddle here
I would like to know if there are other exception that are caught by oracle without having to do something.
In the SQL scope (but not the PL/SQL scope), Oracle will silently catch the NO_DATA_FOUND exception and replace it with a NULL value:
For example:
SELECT (SELECT 1 FROM DUAL WHERE 1 = 0) AS value
FROM DUAL;
The outer query expects a value:
SELECT <something> AS value
FROM DUAL;
The inner query:
SELECT 1 FROM DUAL WHERE 1 = 0
Generates zero rows and so no value; this raises a NO_DATA_FOUND exception which is silently caught and replaced by a NULL value.
This can be seen more clearly with:
WITH FUNCTION ndf RETURN NUMBER
IS
BEGIN
RAISE NO_DATA_FOUND;
END;
SELECT ndf() AS value FROM DUAL;
Which outputs:
VALUE
null
The behaviour is different in PL/SQL:
DECLARE
v_value NUMBER;
BEGIN
SELECT 1 INTO v_value FROM DUAL WHERE 1 = 0;
END;
/
Will not catch the NO_DATA_FOUND exception and it will cause the execution of the block to terminate early.

Cannot rollback while a subtransaction is active - Error 2D000

I have written a stored procedure that basically loops over an array of fields and performs some manipulation in the db for each iteration. What I want to achieve is, either all the iterations of loops should occur or neither one of them should occur.
So let's say there were 5 elements in the fields array and the loop iterates up to the 3rd element before noticing that some condition is true and throwing an error, I want to rollback all the changes that occurred during the first 2 iterations. I've used ROLLBACK statements to achieve the same, but every time it reaches the ROLLBACK statement it throws the following error:
Cannot rollback while a subtransaction is active : 2D000
Surprisingly, it works as normal if I comment out the outobj := json_build_object('code',0); statement within the EXCEPTION WHEN OTHERS THEN block or if I remove that whole block completely.
I've checked the PostgreSQL documentation for error codes, but it didn't really help. My stored procedure is as follows:
CREATE OR REPLACE PROCEDURE public.usp_add_fields(
field_data json,
INOUT outobj json DEFAULT NULL::json)
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
v_user_id bigint;
farm_and_bussiness json;
_field_obj json;
_are_wells_inserted boolean;
BEGIN
-- get user id
v_user_id = ___uf_get_user_id(json_extract_path_text(field_data,'user_email'));
IF(v_user_id IS NULL) THEN
outobj := json_build_object('code',17);
RETURN;
END IF;
-- Loop over entities to create farms & businesses
FOR _field_obj IN SELECT * FROM json_array_elements(json_extract_path(field_data,'fields'))
LOOP
-- check if irrigation unit id is already linked to some other field
IF(SELECT EXISTS(
SELECT field_id FROM user_fields WHERE irrig_unit_id LIKE json_extract_path_text(_field_obj,'irrig_unit_id') AND deleted=FALSE
)) THEN
outobj := json_build_object('code',26);
-- Rollback any changes made by previous iterations of loop
ROLLBACK;
RETURN;
END IF;
-- check if this field name already exists
IF( SELECT EXISTS(
SELECT uf.field_id FROM user_fields uf
INNER JOIN user_farms ufa ON (ufa.farm_id=uf.user_farm_id AND ufa.deleted=FALSE)
INNER JOIN user_businesses ub ON (ub.business_id=ufa.user_business_id AND ub.deleted=FALSE)
INNER JOIN users u ON (ub.user_id = u.user_id AND u.deleted=FALSE)
WHERE u.user_id = v_user_id
AND uf.field_name LIKE json_extract_path_text(_field_obj,'field_name')
AND uf.deleted=FALSE
)) THEN
outobj := json_build_object('code', 22);
-- Rollback any changes made by previous iterations of loop
ROLLBACK;
RETURN;
END IF;
--create/update user business and farm and return farm_id
CALL usp_add_user_bussiness_and_farm(
json_build_object('user_email', json_extract_path_text(field_data,'user_email'),
'business_name', json_extract_path_text(_field_obj,'business_name'),
'farm_name', json_extract_path_text(_field_obj,'farm_name')
), farm_and_bussiness);
IF(json_extract_path_text(farm_and_bussiness, 'code')::int != 1) THEN
outobj := farm_and_bussiness;
-- Rollback any changes made by previous iterations of loop
ROLLBACK;
RETURN;
END IF;
-- insert into users fields
INSERT INTO user_fields (user_farm_id, irrig_unit_id, field_name, ground_water_percent, surface_water_percent)
SELECT json_extract_path_text(farm_and_bussiness,'farm_id')::bigint,
json_extract_path_text(_field_obj,'irrig_unit_id'),
json_extract_path_text(_field_obj,'field_name'),
json_extract_path_text(_field_obj,'groundWaterPercentage'):: int,
json_extract_path_text(_field_obj,'surfaceWaterPercentage'):: int;
-- add to user wells
CALL usp_insert_user_wells(json_extract_path(_field_obj,'well_data'), v_user_id, _are_wells_inserted);
END LOOP;
outobj := json_build_object('code',1);
RETURN;
EXCEPTION WHEN OTHERS THEN
raise notice '% : %', SQLERRM, SQLSTATE;
outobj := json_build_object('code',0);
RETURN;
END;
$BODY$;
If you have an EXCEPTION clause in a PL/pgSQL block, that whole block will be executed in a subtransaction that is rolled back when an exception happens. So you cannot use COMMIT or ROLLBACK in such a block.
If you really need that ROLLBACK, rewrite your code like this:
DECLARE
should_rollback boolean := FALSE;
BEGIN
FOR ... LOOP
BEGIN -- inner block for exception handling
/* do stuff */
IF (/* condition that should cause a rollback */) THEN
should_rollback := TRUE;
EXIT; -- from LOOP
END IF;
EXCEPTION
WHEN OTHERS THEN
/* handle the error */
END;
END LOOP;
IF should_rollback THEN
ROLLBACK;
/* do whatever else is needed */
END IF;
END;
Now the rollback does not happen in a block with an exception handler, and it should work the way you want.
Explanation:
Based on the clue provided by #Laurez Albe, I came up with a cleaner way to solve the above problem.
Basically, what I've done is, I've raised a custom exception whenever a condition is true. So when an exception is thrown, all the changes made by block X are rolled back gracefully. I can even perform last minute cleanup within the exception conditional blocks.
Implementation:
CREATE OR REPLACE procedure mProcedure(INOUT resp json DEFAULT NULL::JSON)
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
field_data json := '{ "fields": [1,2,3,4,5] }';
_field_id int;
BEGIN
-- Start of block X
FOR _field_id IN SELECT * FROM json_array_elements(json_extract_path(field_data,'fields'))
LOOP
INSERT INTO demo VALUES(_field_id);
IF(_field_id = 3) THEN
RAISE EXCEPTION USING ERRCODE='22013';
END IF;
IF(_field_id = 5) THEN
RAISE EXCEPTION USING ERRCODE='22014';
END IF;
END LOOP;
SELECT json_agg(row_to_json(d)) INTO resp FROM demo d;
RETURN;
-- end of block X
-- if an exception occurs in block X, then all the changes made within the block are rollback
-- and the control is passed on to the EXCEPTION WHEN OTHERS block.
EXCEPTION
WHEN sqlstate '22013' THEN
resp := json_build_object('code',26);
WHEN sqlstate '22014' THEN
resp := json_build_object('code',22);
END;
$BODY$;
Demo:
Dbfiddle

Raise exception in stored procedure based on view entries

I have a stored procedure. I would like to implement the below logic, which I have written in pseudocode.
If the below query has one of more entries:
SELECT
NULL
FROM
table1
WHERE
condition
GROUP BY
column
HAVING
COUNT(1) > 1
UNION ALL
SELECT
NULL
FROM
table1 a
WHERE
condition
AND EXISTS (
SELECT
NULL
FROM
table2 b
WHERE
condition
);
Then raise an exception and stop the stored procedure.
Here is an example of raising an exception if a particular value is found from a query:
declare
somevar dual.dummy%type;
begin
select 'Y' into somevar
from dual;
if somevar = 'Y' then
raise_application_error(-20123, 'Hull breach on deck 15. Abandon ship.');
end if;
end;
The "select from dual" can be any query, so feel free to substitute your unions and counts (though we should really stick to the standard count(*), not count('Dracula') etc).
Let's do this with the sample emp/dept schema - just plug in your own statement for your use case. You do need to declare since in pl/sql you cannot "just select". You always need to select into a variable. I usually just select the number 1 into a dummy variable of type number. The trick is to raise the exception after the SELECT INTO and do nothing on NO_DATA_FOUND.
You can use named exceptions to distinguish different cases but since a no data found will throw an exception you have to do each of the cases in its own block. The cleanest is to handle all named exceptions in the final exception block.
DECLARE
l_dummy NUMBER;
king_exists EXCEPTION;
dave_exists EXCEPTION;
BEGIN
BEGIN
SELECT 1 INTO l_dummy FROM emp WHERE ename = 'DAVE';
RAISE dave_exists;
EXCEPTION WHEN NO_DATA_FOUND THEN
NULL;
END;
BEGIN
SELECT 1 INTO l_dummy FROM emp WHERE ename = 'KING';
RAISE king_exists;
EXCEPTION WHEN NO_DATA_FOUND THEN
NULL;
END;
EXCEPTION
WHEN dave_exists THEN
raise_application_error(-20000,'My expection error message');
WHEN king_exists THEN
raise_application_error(-20001,'King exists');
END;
/

Identify when a function is executed in a SQL Query or in a PL/SQL procedure

Is there any way to identify when a pl/sql function is executed in SQL Query and when is executed in a procedure or PL/SQL anonymous block? (I don't want to pass any parameter for manual identification)
The main reason I need that is when a function is executed in a SQL query I wouldn't like to raise an exception in case of failure, I would be satisfied just with a returned value NULL. But the same function when is executed in pl/sql script I want to raise exception.
Thank you in advance.
Why don't you add a parameter to the function to indicate whether or not to throw an exception/return null? When you call the function you can choose the behaviour you need.
create or replace function do_something(p_parameter1 < some_type >
,p_raise_exception varchar2 default 'Y') return < sometype > is
begin
--.. calculating .. .
return result;
exception
when others then
if p_raise_exception is 'Y'
then
raise;
else
return null;
end if;
end;
Alternatively owa_util seems to provide some functionality you can use.
create or replace function do_something(p_parameter1 < some_type >) return < sometype > is
l_owner varchar2(100);
l_name varchar2(100);
l_lineno number;
l_caller_t varchar2(100);
begin
--.. calculating .. .
return result;
exception
when others then
owa_util.who_called_me(l_owner, l_name, l_lineno, l_caller_t)
-- who called me result seems empty when called from sql.
if l_owner is not null
then
raise;
else
return null;
end if;
end;
Of course :
Hiding all errors is bad practise
Well, looking around I found that there is a hack available:
The exception NO_DATA_FOUND isn't propagated when you call PL/SQL in SQL. So you can use this to "return null" instead of get an exception when calling it from SQL:
create or replace function f
return int as
begin
raise no_data_found;
return 1;
end f;
/
select f from dual;
F
null;
declare
v integer;
begin
v := f;
end;
Error report -
ORA-01403: no data found

Error in plsql . here flag has value 1 or no data. when no data i have to insert somethings but error coming.suggestions please

CREATE OR REPLACE PACKAGE BODY BAS_NUMB_UPD AS
PROCEDURE BAS_NUM_UPDN AS
CURSOR cur IS
SELECT DISTINCT o.obj_id,LTRIM(substr(convert_171_to_711(cp.T_C_CP),1,7),'0') bas_n
FROM t_obj o, mat_tea_rel mpr, coorg_tea cp
WHERE o.obj_type = 'Resin'
AND o.obj_id = mpr.obj_id
AND mpr.p_k_id = cp.p_k_id;
l_b_num_attr_id number(10) := get_attribute_id('Ba Nu');
flag1 VARCHAR2(10);
BEGIN
FOR x IN cur LOOP
dbms_output.put_line(x.obj_id||'contains b n'||x.bas_n);
SELECT flag INTO flag1
FROM t_xc_s_values
WHERE attr_id = l_b_num_attr_id
AND Obj_id = x.obj_id
AND VALUE = x.bas_n;
EXCEPTION
WHEN NO_DATA_FOUND THEN
flag1 :='Nothing';
WHEN OTHERS THEN
raise_application_error(-20011,'Unknown Exception in PROCEDURE');
END;
IF flag1 = 1 THEN
dbms_output.put_line('flag equal to one');
ELSE
INSERT INTO t_xc_s_values (obj_id, at_id, VALUE,)
VALUES (x.obj_id, l_b_num_attr_id, x.bas_n);
END IF;
END LOOP;
END;
END BAS_NUM_UPDN;
END BAS_NUMB_UPD;
These are the errors
Error(28,1): PLS-00103: Encountered the symbol "EXCEPTION" when
expecting one of the following: begin case declare end exit for
goto if loop mod null pragma raise return select update while with
<< close current delete fetch lock insert open rollback
savepoint set sql execute commit forall merge pipe
Error(35,1): PLS-00103: Encountered the symbol "IF" when expecting one
of the following: end not pragma final instantiable order
overriding static member constructor map
Error(47,3): PLS-00103: Encountered the symbol "END" when expecting
one of the following: ;
Your code is not properly nested:
If you have an EXCEPTION part, you need to have the triple BEGIN / EXCEPTION / END on the same level. So you probably need to insert a BEGIN after LOOP.
Each END must match a BEGIN. Besides the missing BEGIN mention before, you have too many END statements at the end. Remove the one on the third last line.
These are just syntactical errors. I didn't check whether the procedure does what you intend.
The errors you get are quite straightforward. You have an EXCEPTION ... END but no matching BEGIN. You want to trap the no_data_found for your flag select, so start that block with a BEGIN
(Why would you use a WHEN OTHERS? Why would you trap it and then say it is 'Unknown'? The error is never unknown.)
BEGIN
FOR x IN cur LOOP
dbms_output.put_line(x.obj_id||'contains b n'||x.bas_n);
BEGIN -- added here
SELECT flag INTO flag1
FROM t_xc_s_values
WHERE attr_id = l_b_num_attr_id
AND Obj_id = x.obj_id
AND VALUE = x.bas_n;
EXCEPTION
WHEN NO_DATA_FOUND THEN
flag1 :='Nothing';
WHEN OTHERS THEN
raise_application_error(-20011,'Unknown Exception in PROCEDURE');
END;
Next error:
your insert statement has one comma too many:
INSERT INTO t_xc_s_values (obj_id, at_id, VALUE,)
Next error:
one END too many
END LOOP;
END; -- this will END your procedure block
END BAS_NUM_UPDN; -- this will complain it has to match the procedure begin
Your PLSQL code is also unnecessary. You could write your logic as a SQL statement, improving your performance.
PROCEDURE BAS_NUM_UPDN AS
IS
l_b_num_attr_id number(10) := get_attribute_id('Ba Nu');
BEGIN
INSERT INTO t_xc_s_values (obj_id, at_id, VALUE)
SELECT DISTINCT o.obj_id,
l_b_num_attr_id,
LTRIM(substr(convert_171_to_711(cp.T_C_CP),1,7),'0') bas_n
FROM t_obj o, mat_tea_rel mpr, coorg_tea cp
WHERE o.obj_type = 'Resin'
AND o.obj_id = mpr.obj_id
AND mpr.p_k_id = cp.p_k_id;
AND NOT EXISTS (SELECT flag
FROM t_xc_s_values
WHERE attr_id = l_b_num_attr_id
AND Obj_id = o.obj_id
AND VALUE = LTRIM(substr(convert_171_to_711(cp.T_C_CP),1,7),'0'));
END;