Oracle Cast using %TYPE attribute - oracle

I want to cast a value using %TYPE attribute in my SQL statement. The %TYPE attribute lets you use the datatype of a field, record, nested table, database column, or variable in your own declarations, rather than hardcoding the type names.
This works:
insert into t1 select cast(v as varchar2(1)) from t2;
But I would like to
insert into t1 select cast(v as t1.v%TYPE) from t2;
Error starting at line 16 in command:
insert into t1 select cast(v as t1.v%TYPE) from t2
Error at Command Line:16 Column:37
Error report:
SQL Error: ORA-00911: Ongeldig teken.
00911. 00000 - "invalid character"
*Cause: identifiers may not start with any ASCII character other than
letters and numbers. $#_ are also allowed after the first
character. Identifiers enclosed by doublequotes may contain
any character other than a doublequote. Alternative quotes
(q'#...#') cannot use spaces, tabs, or carriage returns as
delimiters. For all other contexts, consult the SQL Language
Reference Manual.
*Action:
Can this (or something similar) be done?
EDIT:
What I'm trying to achieve is: when t2.v is to large I want to truncate it. I'm trying to avoid using substr with a hard coded field length. So cast(v as t1.v%TYPE) instead of substr(v,1,1)

%TYPE is only available in PL/SQL, and can only be used in the declaration section of a block. So, you can't do what you're attempting.
You might think you could declare your own PL/SQL (sub)type and use that in the statement:
declare
subtype my_type is t1.v%type;
begin
insert into t1 select cast(v as my_type) from t2;
end;
/
... but that also won't work, because cast() is an SQL function not a PL/SQL one, and only recognises built-in and schema-level collection types; and you can't create an SQL type using the %TYPE either.
As a nasty hack, you could do something like:
insert into t1 select substr(v, 1,
select data_length
from user_tab_columns
where table_name = 'T1'
and column_name = 'V') from t2;
Which would be slightly more palatable if you could have that length stored in a variable - a substitution or bind variable in SQL*Plus, or a local variable in PL/SQL. For example, if it's a straight SQL update through SQL*Plus you could use a bind variable:
var t1_v_len number;
begin
select data_length into :t1_v_len
from user_tab_columns
where table_name = 'T1' and column_name = 'V';
end;
/
insert into t1 select substr(v, 1, :t1_v_len) from t2;
Something similar could be done in other set-ups, it depends where the insert is being performed.

Related

Copy table from another schema doesn't work dynamically

I'm trying to copy tables (schema+data) from one schema to another by using:
create table as select * from my_table
I want to do it from a certain table list, so I wrote a cursor
DECLARE
p_site nvarchar2(200);
v_cmd nvarchar2(1000);
v_tablename nvarchar2(100);
CURSOR export_running IS
SELECT tablename FROM TABLES_TABLE;
BEGIN
p_site:='site_name';
OPEN export_running;
LOOP
FETCH export_running INTO v_tablename;
EXIT WHEN export_running%NOTFOUND;
v_cmd:='create table '||v_tablename||' as select * from '||p_site||'.'||v_tablename;
execute immediate v_cmd;
END LOOP;
CLOSE export_running;
END;
When I run the code I get
PLS-00382: expression is of wrong type
ORA-06550: line 20, column 6:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
But if I print the statement and run it, it works well.
Datapump is not an option here.
I'm doing it on SQL Developer, Oracle version 12.1.
I have full privileges on both schemas.
Is it a known issue that I cannot dynamically create a table from one schema to the other?
Any suggestion?
Thanks!
Don't use NVARCHAR2 data type. From the documentation:
execute_immediate_statement
dynamic_sql_stmt
String literal, string variable, or string expression that represents
a SQL statement. Its type must be either CHAR, VARCHAR2, or CLOB.

Parameters wrong assignment (PL/SQL, ORACLE)

I'm trying to run below pl/sql block but I'm getting error:
set serveroutput on
declare
rowBefore VARCHAR2(32000);
myschema VARCHAR2(32000) := 'abc';
mytable VARCHAR2(32000) := 'table1';
param1 VARCHAR2(32000) := 'Tom';
begin
select count(*) into rowBefore from myschema.mytable where colA = param1;
--select count(*) into rowBefore from abc.table1 where colA = 'Tom';
DBMS_OUTPUT.PUT_LINE(rowBefore);
End;
How to correctly use my all parameters into select statement?
Update error:
Error report -
ORA-06550: linia 7, kolumna 50:
PL/SQL: ORA-00942: tabela lub perspektywa nie istnieje
ORA-06550: linia 7, kolumna 5:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
You can't use a variable as an identifier in a SQL statement. It is looking for a table that is actually called mytable - not one with the value of the variable with that name as you expect. And the same for the schema.
From the documentation:
If the statement is a SELECT statement, the PL/SQL compiler removes the INTO clause.
The PL/SQL compiler sends the statement to the SQL subsystem.
The SQL subsystem checks the syntax of the statement.
If the syntax is incorrect, the compilation of the PL/SQL unit fails. If the syntax is correct, the SQL subsystem determines the names of the tables and tries to resolve the other names in the scope of the SQL statement.
...
It tries to resolve other names, e.g. functions; but table names (and schema names, and column names) have to be valid to the SQL parser.
You have to use dynamic SQL to create a string that contains the statement, concatenating in the identifiers:
execute immediate 'select count(*) from ' || myschema ||'.'|| mytable
|| ' where colA = :value'
into rowBefore using param1;
If you print out that generated statement with dbms_output you should see it matching the commented-out version, except for the use of a bind variable for the columns value. That would also help highlight any errors - like not including whitespace between the concatenated parts of the statement, which is a common and easy mistake.
It's often a good idea to build the statement as a string variable to make such debugging easier:
declare
...
v_stmt varchar2(4000):
begin
v_stmt := 'select count(*) from ' || myschema ||'.'|| mytable
|| ' where colA = :value';
-- for debugging
dbms_output.put_line(v_stmt);
-- and run it
execute immediate vStmt into rowBefore using param1;
...
If you are really getting the identifiers passed in by a user or client you should validate them to avoid unexpected errors and SQL injection attacks. Also, if the identifiers might be case sensitive (i.e. schemas/tables created with quoted identifiers) then you may need to add double-quotes to the generated statement, and make sure the variables are the correct case.
error is ORA-942: table or view does not exists. run first below query and see if error still occurs.
-- should you use abc.table1, instead of myschema.mytable?
select count(*) rowBefore from myschema.mytable where colA = 'a';
if there is still error, then fix the SELECT statement first before you use it in the PL/SQL block.

Oracle, Execute immediate Insert

I'm trying to insert random generating data into table, here's code"
begin
FOR x in 1..300 LOOP
Execute immediate 'insert into emp values ('||prac_seq.nextval||','''||'name'||x||''','||trunc(dbms_random.value(1,300))||');';
end loop;
/
table emp has 3 columns - id,name,idmgr;
above query in execute immediate statement looks like:
insert into emp values (13,'name25',193);
This block did not run. When I tried to run single execute immediate statement (f.e.
begin
Execute immediate 'insert into emp values ('||prac_seq.nextval||','''||'name23'','||trunc(dbms_random.value(1,300))||');'
end;
/
ORA gives me an error:
Execute immediate 'insert into emp values
('||prac_seq.nextval||','''||'name23'','||trunc(dbms_random.value(1,300))||');';
end; Error report: ORA-00911: invalid character ORA-06512: at line 3
00911. 00000 - "invalid character"
*Cause: identifiers may not start with any ASCII character other than
letters and numbers. $#_ are also allowed after the first
character. Identifiers enclosed by doublequotes may contain
any character other than a doublequote. Alternative quotes
(q'#...#') cannot use spaces, tabs, or carriage returns as
delimiters. For all other contexts, consult the SQL Language
Reference Manual.
*Action:
And why? Commas, quotes.. everything is checked and fine.
Why are you using execute immediate for this. Try connect by level.
select prac_seq.nextval, 'name'||level, trunc(dbms_random.value(1,300)) as rnd
from dual
connect by level <= 300;
Try remove ; from your dynamic query.

execute query defined in column as String

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

How to locate the accurate postion in pl/sql promptly (ORA-06502: PL/SQL)

I have a select statement which needs to select dozens of column into self-defined variable in my pl/sql. Like as below:
select col1,
col2,
....
col30
into var1,
...
var30
from table
where ....
While executing the SP I encounter the error:
ORA-06502: PL/SQL: numeric or value error: character string buffer too
small
The error information only points out the first line number of select statement. Even if i can figure out that my defined variable is too small to hold the column, it still makes me hard to locate the error-defined variable precisely. This is not an efficient way for me to debug this sp.
Is there any better idea, please advise me.
Two options are typically used in pl/sql:
1.Define your variables in PL/SQL to match the table's definition, using %type.
define
v_col1 my_table.col1%type;
v_col2 my_table.col2%type;
begin
select col1,col2
into v_col1, v_col2
from my_table
-- some condition that pulls 1 row
where rownum = 1;
end;
2.Define a row variable, using %rowtype
define
v_my_table_row my_table%rowtype;
begin
select *
into v_my_table_row
from my_table
where rownum = 1;
end;

Resources