PLSQL convert single row in one string - oracle

Hi I'm looking for a Oracle function for convert all record of one row in a single string
TABLE_A
|a|b|c|1|
|d|e|f|2|
|g|h|i|3|
select * from TABLE_A where COL4='1';
must return
"abc"
select * from TABLE_A where COL4='2';
"def"
I can't use CONCAT
SELECT COL1||COL2||COL3 from TABLE_A
Becouse I don't know the number and the name of the columns in the table.
Is it possible?

Please, try this, working for record with ID=1, you can change that, or add a loop if needed:
DECLARE
CURSOR CUR IS
SELECT COLUMN_NAME
FROM ALL_TAB_COLS
WHERE TABLE_NAME='TABLE_A';
STMT VARCHAR(1000);
RESULT VARCHAR(1000);
BEGIN
STMT := 'SELECT ';
FOR COL in CUR
LOOP
STMT := STMT || COL.COLUMN_NAME || '||';
END LOOP;
STMT := STMT || ''''' FROM TABLE_A WHERE ID=1';
DBMS_OUTPUT.PUT_LINE( STMT );
EXECUTE IMMEDIATE
STMT
INTO RESULT;
DBMS_OUTPUT.PUT_LINE( 'RESULT: ' || RESULT );
END;

Related

error ORA-00998: must name this expression with a column alias

I tried to create table from all_tab_columns but it throws me an error.
like error ORA-00998: must name this expression with a column alias.
i tried to figure it out but not working.
Declare
CURSOR c1 IS
SELECT COLUMN_NAME FROM (select 'standard_hash(MY_NAME) AS MY_NAME' COLUMN_NAME from DUAL
UNION
SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS WHERE TABLE_NAME='TABLE1' AND COLUMN_NAME<>'MY_NAME');
cols c1%ROWTYPE;
sqlstmt VARCHAR2(4000);
BEGIN
OPEN c1;
LOOP
FETCH c1 into cols;
EXIT WHEN c1%NOTFOUND;
sqlstmt := sqlstmt ||cols.column_name||',';
END LOOP;
CLOSE c1;
sqlstmt := 'CREATE TABLE TABLE2 AS SELECT '||substr(sqlstmt, 1, length(sqlstmt)-1)||' FROM TABLE1';
DBMS_OUTPUT.PUT_LINE(sqlstmt);
EXECUTE IMMEDIATE sqlstmt;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('error '||sqlerrm);
END;
/
You just have to give the name to the expression as follows:
sqlstmt := 'CREATE TABLE TABLE2 AS SELECT '
||substr(sqlstmt, 1, length(sqlstmt)-1)
||' as column_name FROM TABLE1'; -- column_name will bre name of the column of new table
As it will become the name of the column of newly created table.
I think this can be simplified somewhat.
You don't need to declare an explicit cursor and a record type and fetch each row into it, as this is already built into the language as the Cursor FOR loop construction:
declare
sqlStmt long := 'create table table2 as select ';
begin
for r in (
select 'standard_hash(my_name) as my_name' column_name
from dual
union
select column_name
from all_tab_columns
where table_name = 'TABLE1'
and column_name <> 'MY_NAME'
)
loop
sqlStmt := sqlStmt || r.column_name || ', ';
end loop;
sqlStmt := sqlStmt || rtrim(sqlStmt,', ') || ' from table1';
dbms_output.put_line(sqlStmt);
execute immediate sqlStmt;
end;
/
But you can do this without any loop, as the listagg function can already build a comma-separated list for you. It also makes it easier to retain the order of columns in the original table.
declare
selectList long;
sqlStmt long;
begin
select listagg(
case column_name
when 'MY_NAME' then 'standard_hash(my_name) as my_name'
else lower(column_name)
end, ', '
) within group (order by column_id) as select_list
into selectList
from user_tab_columns c
where c.table_name = 'TABLE1';
sqlStmt :=
'create table table2 pctfree 0 nologging parallel as '||chr(10)||
'select '||selectList||chr(10)||
'from table1';
dbms_output.put_line(sqlStmt||';');
execute immediate sqlstmt;
end;

PL/SQL nested loop (loop within a loop)

Below is a PL/SQL I'm working on
declare
v_sql varchar2(500);
BEGIN
for t in (
SELECT distinct ID
FROM TABLEB
) loop
for c in (
select * from (
select 'delete from ' as test
from dual
union all
select 'TABLEA'||' where ' as test
from dual
union all
select 'ID='||t.ID
from dual
)
) loop
v_sql := v_sql || c.test;
end loop;
dbms_output.put_line(v_sql);
end loop;
END;
/
The result I'm getting is this
delete from TABLEA where ID=1
delete from TABLEA where ID=1delete from TABLEA where ID=2
I want
delete from TABLEA where ID=1
delete from TABLEA where ID=2
Any help on the PLSQL will be appreciated
What is the purpose of the inner FOR loop? It does nothing that requires a loop, and can be simply rewritten like this:
declare
v_sql varchar2(500);
begin
for t in (select distinct id from tableb) loop
v_sql := 'delete from tablea where id = ' || t.id ||';';
dbms_output.put_line(v_sql);
end loop;
end;
/
BTW, it seems that you're missing the terminating semicolon in line v_sql := ...
Demonstration on HR's DEPARTMENTS table:
SQL> declare
2 v_sql varchar2(500);
3 begin
4 for t in (select distinct department_id id from departments) loop
5 v_sql := 'delete from tablea where id = ' || t.id ||';';
6 dbms_output.put_line(v_sql);
7 end loop;
8 end;
9 /
delete from tablea where id = 10;
delete from tablea where id = 20;
delete from tablea where id = 30;
delete from tablea where id = 40;
delete from tablea where id = 50;
delete from tablea where id = 60;
<snip>
You're not clearing the buffer after you've printed the statement, so you're appending the next statement to the first one. To clear the buffer, add
v_sql := NULL;
after the line which reads
dbms_output.put_line(v_sql);
Best of luck.

Oracle PL/SQL: Help resolving "PLS-00103: Encountered the symbol "LOOP" when expecting one of the following: if"

See Second Edit. New coding gets different error.
I am getting the "encountered a loop when expecting an if" error. My goal is to return a list of tables that have been updated by a specific project_id. The project_id is stored in a column on each table labeled project_id.
I used a with statement to create 2 tables. One table (sb_table) is a single column of all the tables that I want to look at. There are many more tables, but I figured it would speed things up if I narrowed down the list first. The other table I am creating (project) returns a single value turning the prjt_name provided into the actual project number (don't ask me why, but this is how my company has it set up, user creates a prct_name and is never aware of the project number).
Then I am trying to loop through the tables to see if they have the project number in their project_id column. If they do not, I delete them from the sb_table.
Ultimately, I am going to want to get all of the updated rows from all of the updated tables, but I am currently stuck on getting a list of the updated tables.
declare
query varchar2(10000);
table_count NUMBER;
update_count number;
prjt_name varchar2 not null := "01213264B";
cursor my_cur is select sbt.table_name from sb_table sbt;
begin
with sb_tables as (select table_name from all_tab_columns#db2 where
column_name = 'PROJECT_ID' and owner = 'SANDBOX'),
project as (select project_id from sandbox.sb_project#db2 where
project_name = upper(prjt_name))
--select sbt.table_name
--from sb_table sbt
for tableName in my_cur loop
query := 'select count(t.project_id) as "CNT" '||
'from sandbox.' || tableName || '#db2 t, project p '||
' where t.project_id = p.project_id ';
Execute immediate query
into update_count;
if update_count <= 0 then
query := 'DELETE FROM sb_tables where table_name = ' || tableName;
execute immediate query ;
end loop;
end;
Edit 1: Per comments, moved the select statement to the declare and am looping through my_cur now. I still get the same error.
Edit 2: Updated coding based on suggestions. I now get a different error message.
NEW ERROR: ORA-06550: line 12, column 16:
PLS-00306: wrong number or types of arguments in call to '||'
ORA-06550: line 12, column 7:
Edit 3: I was missing an = in my sub query which produced the error "Missing expression at line 13."
Edit 4: Now I get some results then error out with the following message
ORA-29913: error in executing ODCIEXTTABLEOPEN callout
ORA-29400: data cartridge error
KUP-04040: file ext_qsp_benefit.dat in DATA_DIR not found
ORA-02063: preceding 3 lines from ADHOC_POS15
ORA-06512: at line 13
Edit 5: Success! Apparently I cannot query certain tables. So I just took those tables out.
Final coding is:
declare
query varchar2(10000);
update_count integer := 0;
prjt_name varchar2(100) := '01213264B';
cursor my_cur is (select table_name from all_tab_columns#db2 where column_name = 'PROJECT_ID' and owner = 'SANDBOX' and table_name in ('X') );
tableName varchar2(100);
begin
open my_cur;
loop
fetch my_cur into tableName;
exit when my_cur%NOTFOUND;
update_count := 0;
execute immediate
'select count(project_id) as "CNT" from sandbox.' || tableName || '#db2 '
|| ' where project_id = (select project_id from sandbox.sb_project#db2 where project_name = ''' || prjt_name || ''' ) '
into update_count;
if update_count > 0 then
dbms_output.put_line (tableName);
end if;
end loop;
close my_cur;
end;
This doesn't do exactly what I wanted. It sends the results to dbms_output. But It is a start! Thanks everyone for you help!
DECLARE
update_count integer := 0;
prjt_name varchar2(100) := 'tttt';
mysql varchar2(100);
tablename varchar2(100);
cursor my_cur is
select 'DUAL'
from dual
where 'PROJECT_ID' = 'PROJECT_ID' and 'SANDBOX' = 'SANDBOX';
begin
open my_cur;
LOOP
FETCH my_cur into tablename;
EXIT WHEN my_cur%NOTFOUND;
update_count := 0;
mysql := 'select count(*) ' || ' from '
|| tablename
|| ' where ''TTTT'' = upper('''
|| prjt_name
|| ''')' ;
Execute immediate mysql INTO update_count;
dbms_output.put_line (mysql);
dbms_output.put_line (update_count);
END LOOP;
CLOSE my_cur;
end;
I tried one like yours. This executes successfully
declare
query varchar2(10000);
update_count integer := 0;
prjt_name varchar2(100) := '01213264B';
cursor my_cur is
select table_name
from all_tab_columns#adhoc_pos15
where column_name = 'PROJECT_ID' and owner = 'SANDBOX';
tableName varchar2(100);
begin
open my_cur;
loop
fetch my_cur into tableName;
exit when my_cur%NOTFOUND;
update_count := 0;
query := 'select count(t.project_id) as ''CNT'' from sandbox.'
|| tableName
|| '#adhoc_pos15 t'
|| ' where t.project_id = (select project_id from sandbox.sb_project#adhoc_pos15 where project_name = ''' || prjt_name || ''') ' ;
execute immediate query into update_count;
dbms_output.put_line (query);
dbms_output.put_line (update_count);
end loop;
close my_cur;
end;
i have tried your code with some execution, but before that you need to correct your with clause query. below code has executed except with clause so, make some change dependence on your requirements.
**code :**
declare
query varchar2(10000);
table_count NUMBER;
update_count number;
prjt_name varchar2 not null := '01213264B';
cursor my_cur is
select sbt.table_name from sb_table sbt;
begin
/* with sb_tables as (select table_name from all_tab_columns#db2 where
column_name = 'PROJECT_ID' and owner = 'SANDBOX'),
project as (select project_id from sandbox.sb_project#db2 where
project_name = upper(prjt_name))*/
for tableName in my_cur
loop
query := 'select count(t.project_id) into '|| update_count || 'from sandbox.' || tableName || '#db2 t, project p '||' where t.project_id = p.project_id ';
Execute immediate query;
--into update_count;
if update_count <= 0 then
query := 'DELETE FROM sb_tables where table_name = ' || tableName;
execute immediate query ;
end if;
end loop;
end;
If you run it in sql-developer please note, there are different buttons for running query (green triangle) and running script (green smaller triangle over a white document). Use 'run script' button

In this case how the parameters pass

I have a procedure in oracle
CREATE OR REPLACE
PROCEDURE ReportCalculate
(
param_acTypeId in NUMBER
)
AS
sqlstr VARCHAR2(500);
result NUMBER;
BEGIN
sqlstr:='select count(1) from table1 where 1=1 and AC_TYPE_LEVEL1_ID=:acTypeId';
execute immediate sqlstr into result using param_acTypeId;
DBMS_OUTPUT.PUT_LINE(result);
END;
But sometimes I would like to query all the data, sql looks like this
select count (1) from table1 where 1 = 1 and AC_TYPE_LEVEL1_ID = AC_TYPE_LEVEL1_ID,
then how should the parameters pass, or param_acTypeId should have any default value? Is it only in the stitching sql when to judge it?
A typical method would be to accept NULL as meaning "all":
sqlstr := 'select count(1) from table1 where AC_TYPE_LEVEL1_ID = :acTypeId or :acTypeId is null';
I should note that this version precludes the use of indexes. If performance is an issue, then use two queries:
if param_acTypeId is null then
sqlstr := 'select count(1) from table1';
execute immediate sqlstr into result;
else
sqlstr := 'select count(1) from table1 where AC_TYPE_LEVEL1_ID = :acTypeId';
execute immediate sqlstr into result using param_acTypeId;
end if;
DBMS_OUTPUT.PUT_LINE(result);
You do not need dynamic SQL. If you pass in NULL then it will count all rows:
CREATE OR REPLACE PROCEDURE ReportCalculate (
param_acTypeId in NUMBER
)
AS
result NUMBER;
BEGIN
select count(1)
into result
from table1
where ( param_acTypeId IS NULL OR AC_TYPE_LEVEL1_ID = param_acTypeId );
DBMS_OUTPUT.PUT_LINE(result);
END;

How to trim all columns in all rows in all tables of type string?

In Oracle 10g, is there a way to do the following in PL/SQL?
for each table in database
for each row in table
for each column in row
if column is of type 'varchar2'
column = trim(column)
Thanks!
Of course, doing large-scale dynamic updates is potentially dangerous and time-consuming. But here's how you can generate the commands you want. This is for a single schema, and will just build the commands and output them. You could copy them into a script and review them before running. Or, you could change dbms_output.put_line( ... ) to EXECUTE IMMEDIATE ... to have this script execute all the statements as they are generated.
SET SERVEROUTPUT ON
BEGIN
FOR c IN
(SELECT t.table_name, c.column_name
FROM user_tables t, user_tab_columns c
WHERE c.table_name = t.table_name
AND data_type='VARCHAR2')
LOOP
dbms_output.put_line(
'UPDATE '||c.table_name||
' SET '||c.column_name||' = TRIM('||c.column_name||') WHERE '||
c.column_name||' <> TRIM('||c.column_name||') OR ('||
c.column_name||' IS NOT NULL AND TRIM('||c.column_name||') IS NULL)'
);
END LOOP;
END;
Presumably you want to do this for every column in a schema, not in the database. Trying to do this to the dictionary tables would be a bad idea...
declare
v_schema varchar2(30) := 'YOUR_SCHEMA_NAME';
cursor cur_tables (p_schema_name varchar2) is
select owner, table_name, column_name
from all_tables at,
inner join all_tab_columns atc
on at.owner = atc.owner
and at.table_name = atc.table_name
where atc.data_type = 'VARCHAR2'
and at.owner = p_schema;
begin
for r_table in cur_tables loop
execute immediate 'update ' || r.owner || '.' || r.table_name
|| ' set ' || r.column_name || ' = trim(' || r.column_name ||');';
end loop;
end;
This will only work for fields that are VARCHAR2s in the first place. If your database contains CHAR fields, then you're out of luck, because CHAR fields are always padded to their maximum length.

Resources