I have an application in oracle apex and there is a section which is called search_result and it returns this sql query
select
"TITLE" CARD_TITLE,
"POSTER_PATH" CARD_TEXT,
"PRODUCTION_COMPANY" CARD_SUBTEXT,
apex_string.get_initials("TITLE") CARD_INITIALS,
'https://www.imdb.com/title/'||IMDB_ID CARD_LINK,
null CARD_MODIFIERS,
null CARD_COLOR,
null CARD_ICON,
"GENRE",
"COUNTRY",
"PRODUCTION_COMPANY",
"RUNTIME",
"BUDGET",
"VOTE_AVERAGE",
"TITLE",
"POSTER_PATH"
from "MOVIES"
order by vote_average desc
But I need to rewrite this query with pl/sql function body and
I've created package with function
CREATE OR REPLACE PACKAGE PRINTS AS
FUNCTION MOVIES_LIST RETURN SYS_REFCURSOR;
FUNCTION ORDER_VOTE RETURN SYS_REFCURSOR;
END PRINTS;
CREATE OR REPLACE PACKAGE BODY PRINTS AS
FUNCTION ORDER_VOTE
RETURN SYS_REFCURSOR
is
r_movie sys_refcursor;
BEGIN
open r_movie for select
"TITLE" CARD_TITLE,
"POSTER_PATH" CARD_TEXT,
"PRODUCTION_COMPANY" CARD_SUBTEXT,
apex_string.get_initials("TITLE") CARD_INITIALS,
null CARD_MODIFIERS,
null CARD_COLOR,
null CARD_ICON,
"GENRE",
"COUNTRY",
"PRODUCTION_COMPANY",
"RUNTIME",
"BUDGET",
"VOTE_AVERAGE",
"TITLE",
"POSTER_PATH"
from "MOVIES"
order by vote_average desc;
return r_movie;
END ORDER_VOTE;
END PRINTS;
Then I've tried to return this cursor in pl/sql returning sql query block:
declare
movie_cur sys_refcursor;
a_movie movies%rowtype;
begin
movie_cur := PRINTS.ORDER_VOTE; -- call the movie function
loop
fetch movie_cur into a_movie;
exit when movie_cur%notfound;
end loop;
return a_movie;
end;
but it throws this error:
ORA-06550: line 12, column 11: PLS-00382: expression is of wrong type
what I did wrong and how should I fix it.
My db and some screenshots:
Column Name Data Type Nullable Default Primary Key
ID NUMBER No "WKSP_DBMSPROJECT1"."ISEQ$$_111377832".nextval 1
TITLE VARCHAR2(255) Yes - -
GENRE VARCHAR2(50) Yes - -
COUNTRY VARCHAR2(50) Yes - -
PRODUCTION_COMPANY VARCHAR2(255) Yes - -
RUNTIME NUMBER Yes - -
RELEASE_DATE DATE Yes - -
OVERVIEW VARCHAR2(4000) Yes - -
TAGLINE VARCHAR2(255) Yes - -
BUDGET NUMBER Yes - -
REVENUE NUMBER Yes - -
POPULARITY NUMBER Yes - -
VOTE_AVERAGE NUMBER Yes - -
VOTE_COUNT NUMBER Yes - -
POSTER_PATH VARCHAR2(255) Yes - -
TMDB_ID NUMBER Yes - -
IMDB_ID VARCHAR2(50) Yes - -
IMDB DB
page design
page itself
When using the type PL/SQL Function Body returning SQL Query, you need to return your SQL statement as a string. So the source of your region should look something like this:
BEGIN
return 'SELECT * FROM MOVIES';
END;
Using the select that you provided in your comments you will need to use two single quotes to represent a quote in your string that you are returning.
BEGIN
RETURN 'SELECT "TITLE" CARD_TITLE,
"POSTER_PATH" CARD_TEXT,
"PRODUCTION_COMPANY" CARD_SUBTEXT,
apex_string.get_initials ("TITLE") CARD_INITIALS,
''https://www.imdb.com/title/'' || IMDB_ID CARD_LINK,
NULL CARD_MODIFIERS,
NULL CARD_COLOR,
NULL CARD_ICON,
"GENRE",
"COUNTRY",
"PRODUCTION_COMPANY",
"RUNTIME",
"BUDGET",
"VOTE_AVERAGE",
"TITLE",
"POSTER_PATH"
FROM "MOVIES"
ORDER BY vote_average DESC';
END;
Your code is mostly correct. However you have written it an an anonymous block, which unfortunately cannot return results. You need to convert either to a function that returns a reference cursor or a procedure with an out parameter of a reference cursor.
create or replace
function movies_list
return sys_refcursor
is
r_movies sys_refcursor;
begin
open r_movies for
select * from movies;
return r_movies;
end movies_list;
Since it would seem your current code processes a column list instead of a select *. In that case you can the select with the exact query you have.
Processing:
declare
movie_cur sys_refcursor;
a_movie movies%rowtype;
begin
dbms_output.enable;
movie_cur := movies_list; -- call the movie function
dbms_output.put_line("Movie List');
loop
fetch movie_cur into a_movie;
exit when movie_cur%not_found;
dbms_output.put_line(a_movie.title);
end loop;
end;
The function is compiled and stored in the database (a stored procedure). THE ref-cursor is the result set of the query - just as your Apex section does not return the query, by the result set of the query.
How to use ref cursor fully. (Well mostly). In response to wrong type exception.
This error is the result of your query returning different columns than defined in the table but then trying to fetch into a variable defined as movies%rowtype; Now that definition creates a structure that exactly match the tables column definition. However, your query has at least 4 columns that are not part of the table definition and perhaps 3 others. The columns Title, Production_Company, and Poster_Path are each used twice in query. Even though aliased they still represent an additional column in the result set. There is the function call, and the null selections. Further there seems additional columns in the table that are not in the query. This does not make the query bad, just that it cannot match the movies%rowtype declaration.
Two solutions come to mind. Just "select * from movies" and fetch into the
movies%rowtype declaration and derive the additional columns after the table data is retrieved. The other is to set up an appropriate cursor definition in the package spec. Where it can be references externally to the package. The issue being that you need to basically repeat that query in the body when you actually open the reference cursor.
Assuming that you actually need those columns and you have table columns you want to avoid selecting since you do not need them (I like that part), I put together a full demo. Since I do not have your table definition and I have absolutely no idea what the function apex_string.get_initials even is, I just made something up for them. But they seem reasonable, and you should problems replacing with the actual.
Related
I need to create a procedure which will take collection as an input and compare the data with staging table data row by row for every field (approx 50 columns).
Business logic :
whenever a staging table column value will mismatch with the corresponding collection variable value then i need to update 'FAIL' into staging table STATUS column and reason into REASON column for that row.
If matched then need to update 'SUCCESS' in STATUS column.
Payload will be approx 500 rows in each call.
I have created below sample script:
PKG Specification :
CREATE OR REPLACE
PACKAGE process_data
IS
TYPE pass_data_rec
IS
record
(
p_eid employee.eid%type,
p_ename employee.ename%type,
p_salary employee.salary%type,
p_dept employee.dept%type
);
type p_data_tab IS TABLE OF pass_data_rec INDEX BY binary_integer;
PROCEDURE comp_data(inpt_data IN p_data_tab);
END;
PKG Body:
CREATE OR REPLACE
PACKAGE body process_data
IS
PROCEDURE comp_data (inpt_data IN p_data_tab)
IS
status VARCHAR2(10);
reason VARCHAR2(1000);
cnt1 NUMBER;
v_eid employee_copy.eid%type;
v_ename employee_copy.ename%type;
BEGIN
FOR i IN 1..inpt_data.count
LOOP
SELECT ec1.eid,ec1.ename,COUNT(*) over () INTO v_eid,v_ename,cnt1
FROM employee_copy ec1
WHERE ec1.eid = inpt_data(i).p_eid;
IF cnt1 > 0 THEN
IF (v_eid=inpt_data(i).p_eid AND v_ename = inpt_data(i).p_ename) THEN
UPDATE employee_copy SET status = 'SUCCESS' WHERE eid = inpt_data(i).p_eid;
ELSE
UPDATE employee_copy SET status = 'FAIL' WHERE eid = inpt_data(i).p_eid;
END IF;
ELSE
NULL;
END IF;
END LOOP;
COMMIT;
status :='success';
EXCEPTION
WHEN OTHERS THEN
status:= 'fail';
--reason:=sqlerrm;
END;
END;
But in this approach i have below mentioned issues.
Need to declare all local variables for each column value.
Need to compare all variable data using 'and' operator. Not sure whether it is correct way or not because if there are 50 columns then if condition will become very heavy.
IF (v_eid=inpt_data(i).p_eid AND v_ename = inpt_data(i).p_ename) THEN
Need to update REASON column when any column data mismatched (first mismatched column name) for that row, in this approach i am not able to achieve.
Please suggest any other good way to achieve this requirement.
Edit :
There is only one table at my end i.e target table. Input will come from any other source as collection object.
REVISED Answer
You could load the the records into t temp table, but unless you want additional processing it's not necessary. AFAIK there is no way to identify the offending column (first one only) without slugging through column-by-column. However, your other concern having to declare a variable is not necessary. You can declare a single variable defined as %rowtype which gives you access to each column by name.
Looping through an array of data to find the occasional error is just bad (imho) with SQL available to eliminate the good ones in one fell swoop. And it's available here. Even though your input is a array we can use as a table by using the TABLE operator, which allows an array (collection) as though it were a database table. So the MINUS operator can till be employed. The following routine will set the appropriate status and identify the first miss matched column for each entry in the input array. It reverts to your original definition in package spec, but replaces the comp_data procedure.
create or replace package body process_data
is
procedure comp_data (inpt_data in p_data_tab)
is
-- define local array to hold status and reason for ecah.
type status_reason_r is record
( eid employee_copy.eid%type
, status employee_copy.status%type
, reason employee_copy.reason%type
);
type status_reason_t is
table of status_reason_r
index by pls_integer;
status_reason status_reason_t := status_reason_t();
-- define error array to contain the eid for each that have a mismatched column
type error_eids_t is table of employee_copy.eid%type ;
error_eids error_eids_t;
current_matched_indx pls_integer;
/*
Helper function to identify 1st mismatched column in error row.
Here is where we slug our way through each column to find the first column
value mismatch. Note: There is actually validate the column sequence, but
for purpose here we'll proceed in the input data type definition.
*/
function identify_mismatch_column(matched_indx_in pls_integer)
return varchar2
is
employee_copy_row employee_copy%rowtype;
mismatched_column employee_copy.reason%type;
begin
select *
into employee_copy_row
from employee_copy
where employee_copy.eid = inpt_data(matched_indx_in).p_eid;
-- now begins the task of finding the mismatched column.
if employee_copy_row.ename != inpt_data(matched_indx_in).p_ename
then
mismatched_column := 'employee_copy.ename';
elsif employee_copy_row.salary != inpt_data(matched_indx_in).p_salary
then
mismatched_column := 'employee_copy.salary';
elsif employee_copy_row.dept != inpt_data(matched_indx_in).p_dept
then
mismatched_column := 'employee_copy.dept';
-- elsif continue until ALL columns tested
end if;
return mismatched_column;
exception
-- NO_DATA_FOUND is the one error that cannot actually be reported in the customer_copy table.
-- It occurs when an eid exista in the input data but does not exist in customer_copy.
when NO_DATA_FOUND
then
dbms_output.put_line( 'Employee (eid)='
|| inpt_data(matched_indx_in).p_eid
|| ' does not exist in employee_copy table.'
);
return 'employee_copy.eid ID is NOT in table';
end identify_mismatch_column;
/*
Helper function to find specified eid in the initial inpt_data array
Since the resulting array of mismatching eid derive from a select without sort
there is no guarantee the index values actually match. Nor can we sort to build
the error array, as there is no way to know the order of eid in the initial array.
The following helper identifies the index value in the input array for the specified
eid in error.
*/
function match_indx(eid_in employee_copy.eid%type)
return pls_integer
is
l_at pls_integer := 1;
l_searching boolean := true;
begin
while l_at <= inpt_data.count
loop
exit when eid_in = inpt_data(l_at).p_eid;
l_at := l_at + 1;
end loop;
if l_at > inpt_data.count
then
raise_application_error( -20199, 'Internal error: Find index for ' || eid_in ||' not found');
end if;
return l_at;
end match_indx;
-- Main
begin
-- initialize status table for each input enter
-- additionally this results is a status_reason table in a 1:1 with the input array.
for i in 1..inpt_data.count
loop
status_reason(i).eid := inpt_data(i).p_eid;
status_reason(i).status :='SUCCESS';
end loop;
/*
We can assume the majority of data in the input array is valid meaning the columns match.
We'll eliminate all value rows by selecting each and then MINUSing those that do match on
each column. To accomplish this cast the input with TABLE function allowing it's use in SQL.
Following produces an array of eids that have at least 1 column mismatch.
*/
select p_eid
bulk collect into error_eids
from (select p_eid, p_ename, p_salary, p_dept from TABLE(inpt_data)
minus
select eid, ename, salary, dept from employee_copy
) exs;
/*
The error_eids array now contains the eid for each miss matched data item.
Mark the status as failed, then begin the long hard process of identifying
the first column causing the mismatch.
The following loop used the nested functions to slug the way through.
This keeps the main line logic clear.
*/
for i in 1 .. error_eids.count -- if all inpt_data rows match then count is 0, we bypass the enttire loop
loop
current_matched_indx := match_indx(error_eids(i));
status_reason(current_matched_indx).status := 'FAIL';
status_reason(current_matched_indx).reason := identify_mismatch_column(current_matched_indx);
end loop;
-- update employee_copy with appropriate status for each row in the input data.
-- Except for any cid that is in the error eid table but doesn't exist in the customer_copy table.
forall i in inpt_data.first .. inpt_data.last
update employee_copy
set status = status_reason(i).status
, reason = status_reason(i).reason
where eid = inpt_data(i).p_eid;
end comp_data;
end process_data;
There are a couple other techniques used you may want to look into if you are not familiar with them:
Nested Functions. There are 2 functions defined and used in the procedure.
Bulk Processing. That is Bulk Collect and Forall.
Good Luck.
ORIGINAL Answer
It is NOT necessary to compare each column nor build a string by concatenating. As you indicated comparing 50 columns becomes pretty heavy. So let the DBMS do most of the lifting. Using the MINUS operator does exactly what you need.
... the MINUS operator, which returns only unique rows returned by the
first query but not by the second.
Using that this task needs only 2 Updates: 1 to mark "fail", and 1 to mark "success". So try:
create table e( e_id integer
, col1 varchar2(20)
, col2 varchar2(20)
);
create table stage ( e_id integer
, col1 varchar2(20)
, col2 varchar2(20)
, status varchar2(20)
, reason varchar2(20)
);
-- create package spec and body
create or replace package process_data
is
procedure comp_data;
end process_data;
create or replace package body process_data
is
package body process_data
procedure comp_data
is
begin
update stage
set status='failed'
, reason='No matching e row'
where e_id in ( select e_id
from (select e_id, col1, col2 from stage
except
select e_id, col1, col2 from e
) exs
);
update stage
set status='success'
where status is null;
end comp_data;
end process_data;
-- test
-- populate tables
insert into e(e_id, col1, col2)
select (1,'ABC','def') from dual union all
select (2,'No','Not any') from dual union all
select (3,'ok', 'best ever') from dual union all
select (4,'xx','zzzzzz') from dual;
insert into stage(e_id, col1, col2)
select (1,'ABC','def') from dual union all
select (2,'No','Not any more') from dual union all
select (4,'yy', 'zzzzzz') from dual union all
select (5,'no e','nnnnn') from dual;
-- run procedure
begin
process_data.comp_date;
end;
-- check results
select * from stage;
Don't ask. Yes, you to must list every column you wish compared in each of the queries involved in the MINUS operation.
I know the documentation link is old (10gR2), but actually finding Oracle documentation is a royal pain. But the MINUS operator still functions the same in 19c;
We have a requirement where we have to pass the table name to a pl/SQL object at the runtime.
Below is the example
create or replace FUNCTION ABC
(P_TABLE VARCHAR2) RETURN NUMBER IS
C_REFERENCE SYS_REFCURSOR;
V_TABLE VARCHAR2(50):=P_TABLE;
V_C_REF v_table%rowtype;
BEGIN
OPEN C_REFERENCE FOR 'SELECT * FROM '||V_TABLE||;
LOOP
FETCH C_REFERENCE INTO V_C_REF;
EXIT WHEN C_REFERENCE%NOTFOUND;
/*some processing*/
END LOOP;
return(1);
END;
The above code will give me an error. Is there any workaround for it? Table name can vary and different tables will have different structures.
The rowtype declaration, has to be static (if it's not, the compiler can't tell if the fields referenced from it are valid).
Possible solutions are:
Put a comma separated list of variables in the INTO clause:
FETCH C_REFERENCE INTO var1, var2, var3;
Create your own record
TYPE V_C_REF IS RECORD ( col1 VARCHAR2(20), col2 VARCHAR2(25) );
Use a table with the expected estructure
vc_ref table1%ROWTYPE;
write a PL/SQL function to query dba_tab_columns and get the column size and type definition.
I just want to ask a simple question, I have a procedure in my package: procedure(p_x in default sysdate). However, when i try to use this p_x to do query in my procdure, i got nothing from my table. My assumption is p_x does not get value from sysdate and it is null. Why does this happen? How can I fix it?
Thanks in advance.
In Package:
Procedure M_m(p_x in default sysdate)
In Package Body:
Procedure M_m(p_x in default sysdate) as
cursor cur is select manager_id, manager_name,manger_department
from employ_table where trunc(EmploymentDate)=trunc(p_x);
r_cur cur%rowtype;
Begin
for r_cur in cur
loop
DBMS_OUTPUT.PUT_LINE(employ_table.manager_id || employ_table.manager_name
|| employ_table.manager_department);
end loop;
end;
I guess Wernfried is right, it depends on how exactly you call it. Just add a DBMS_OUTPUT.PUT_LINE(p_x) immediately before the FOR to verify the value of the variable:
CREATE TABLE employ_table AS SELECT * FROM scott.emp;
UPDATE employ_table SET hiredate=SYSDATE WHERE ename='SCOTT';
CREATE OR REPLACE PACKAGE P IS
PROCEDURE M_m(p_x IN DATE DEFAULT sysdate);
END;
/
CREATE OR REPLACE PACKAGE BODY p IS
PROCEDURE M_m(p_x IN DATE DEFAULT sysdate) IS
CURSOR cur IS
SELECT * FROM employ_table WHERE TRUNC(hiredate)=TRUNC(p_x);
BEGIN
DBMS_OUTPUT.PUT_LINE('p_x='||p_x);
FOR r_cur IN cur LOOP
DBMS_OUTPUT.PUT_LINE(r_cur.mgr);
END LOOP;
END M_m;
END P;
/
EXEC P.m_m(DATE '1980-12-17');
p_x=1980-12-17 00:00:00
7902
EXEC P.m_m();
p_x=2018-05-26 17:50:11
7566
EXEC P.m_m(NULL);
p_x=
Currently (as of 18c) the default or := clause in a PLSQL parameter specification behaves like a pre-12c default clause in a table column definition. That is, the default value is only applied when the corresponding column is not specified at all.
If you define a table column as
somedate date default sysdate
then it will only be assigned the default value if the insert statement does not mention column somedate at all. If the insert sets somedate to null then it will be null, regardless of any default. The same behaviour is true of PL/SQL parameters. An explicit null value passed in overrides any default that you set for a parameter.
You will need to define a local constant along the lines of
k_x constant date := coalesce(p_x,sysdate);
and then have the rest of the procedure refer to that instead of the parameter.
In 12c we have default on null for table columns, but in my opinion PL/SQL lags behind, as it has no equivalent syntax for parameter defaults. If you pass null to a PL/SQL parameter, it respects that null and not any default that you specified, as it has no on null syntax option.
If you would like Oracle to extend PL/SQL parameter defaults along the same lines as table columns then consider voting for my suggestion here:
https://community.oracle.com/ideas/18189
Hello again I need some help,
I have table where in column "query" is defined query statement. I would like to run it and as output get the result.For example:
Create table table1
(
ID Number,
Query Varchar2(400)
)
insert into table1(id,query) values (1,'select name from table2 where table2.id=table1.id and table2.type = variable');
create table table2
(ID number,
Name varchar2(400),
Type Varchar2(400)
)
insert into table2 values (1,'Mathew','M');
insert into table2 values (1,'Thomas','G');
insert into table2 values (2,'Jerry','P');
For now query :
'select name from table2 where table2.id=table1.id and table2.type = variable'
should return only "Mathew" (assuming variable as 'M' - procedure variable input)
As procedure input I want to have variable which I will replace somehow in query statement.
Could you give me some tips how to handle with that?
------------Edit
I did stmh like that:
create or replace procedure queryrun
(var1 varchar2) as
str VARCHAR2(200);
BEGIN
execute immediate 'select replace(query,''variable'','''||var1||''') from table1' into str;
dbms_output.put_line('Value is '||str);
END;
But as result it present query... no result of select statement...
You are only selecting your query, not running it; and you're replacing the string "'variable'" - including the single quotes - with your value, but your original query string doesn't have the single quotes around it - so nothing matches.
You should not really substitue a hard-coded value anyway. Change your stored query to include a bind variable placeholder instead:
insert into table1(id,query)
values (1,'select name from table2 where table2.id=table1.id and table2.type = :variable');
Although that query is invalid anyway - you don't have table1 defined in the from clause or a join clause. When you have a valid query you can run standalone, use that, but with a bind variable (denoted by the leading colon).
But let's assume you have a valid query string in your table, that will only return one row, say:
insert into table1(id,query)
values (1,'select name from table2 where type = :variable');
Your procedure then needs a local variable to hold that query string. You select your query into that using static SQL, and then use dynamic SQL via execute immediate to run the query from that string, and provide the bind value with the using clause. The result goes into another local variable, which you are already doing.
So a simple version might look like this:
create or replace procedure queryrun (p_var1 varchar2) as
l_query table1.query%type;
l_name table2.name%type;
begin
select query into l_query from table1 where id = 1;
execute immediate query into l_name using p_var1;
dbms_output.put_line('Value is ' || l_name);
end;
This is obviously rather contrived. If you have multiple queries in your table, and perhaps pass a second ID variable into the procedure to choose which one to run, they would all have to take a single bind variable, and would have to all be able to put the result into the same type and size of result variable. You're also restricted to queries that return exactly one row. You can adapt and extend this of course, but hopefully this will get you started.
You can have bind variable and use plsql execute immediate.
Examples:
http://www.dba-oracle.com/t_oracle_execute_immediate.htm
I select data from several tables. Then i need to edit the data returned from the cursor before returning. The cursor will then be passed to a perl script to display the rows.
To that i build a pl/sql table as in the following code. What i need to know is how to return the to that table ?
At present i get the error "table or view doesn't exist". Test code i use for a simple table is attached here.
CREATE OR REPLACE FUNCTION test_rep
RETURN SYS_REFCURSOR
AS
CURSOR rec_Cur IS
SELECT table1.NAME,
table1.ID
FROM TESTREPORT table1;
TYPE rec_Table IS TABLE OF rec_Cur%ROWTYPE INDEX BY PLS_INTEGER;
working_Rec_Table rec_Table;
TYPE n_trade_rec IS RECORD
(
NAME VARCHAR2(15),
ID NUMBER
);
TYPE ga_novated_trades IS TABLE OF n_trade_rec index by VARCHAR2(15);
va_novated_trades ga_novated_trades;
v_unique_key VARCHAR2(15);
TYPE db_cursor IS REF CURSOR;
db_cursor2 db_cursor;
BEGIN
OPEN rec_Cur;
FETCH rec_Cur BULK COLLECT INTO working_Rec_Table;
FOR I IN 1..working_Rec_Table.COUNT LOOP
v_unique_key := working_Rec_Table(I).NAME;
va_novated_trades(v_unique_key).NAME := working_Rec_Table(I).NAME;
va_novated_trades(v_unique_key).ID := working_Rec_Table(I).ID;
END LOOP; --FOR LOOP
OPEN db_cursor2 FOR SELECT * FROM va_novated_trades; --ERROR LINE
CLOSE rec_Cur;
RETURN db_cursor2;
END test_rep;
/
Basically there is a way to select from a table type in oracle using the TABLE() function
SELECT * FROM table(va_novated_trades);
But this works only for schema table types and on plsql tables (table types defined in the SCHEMA and not in a plsql package):
CREATE TYPE n_trade_rec AS OBJECT
(
NAME VARCHAR2(15),
ID NUMBER
);
CREATE TYPE ga_novated_trades AS TABLE OF n_trade_rec;
But I still think you should try to do it all in a query (and/or in the perl script),
For example, there is one field where i have to analyse the 4th
character and then edit other fields accordingly
This can be achieved in the query, could be something like:
select case when substr(one_field, 4, 1) = 'A' then 'A.' || sec_field
when substr(one_field, 4, 1) = 'B' then 'B.' || sec_field
else sec_field
end as new_sec_field,
case when substr(one_field, 4, 1) = 'A' then 100 * trd_field
when substr(one_field, 4, 1) = 'B' then 1000 * trd_field
else trd_field
end as new_trd_field,
-- and so on
from TESTREPORT