How to create multiple table synonyms in oracle 10g? - oracle

I have multiple table.
I tried to this query:
CREATE PUBLIC SYNONYM CHKWEBUSER FOR NEST.CHKWEBUSER;
But I have multiple table like 5000. SO how can I create synonym in one query?

You cannot simply create synonyms for all tables with a single command. What you need to do is to create / generate a script that will do that for you.
Here, you have a simple script that will generate a list of CREATE PUBLIC SYNONYM... commands which you can later run.
DECLARE
CURSOR cTables IS
SELECT *
FROM ALL_TABLES TAB
WHERE TAB.OWNER = 'NEST'; /* Tune the where clause to your needs */
sSql VARCHAR2(20000);
BEGIN
FOR rTable in cTables LOOP
sSql := 'CREATE PUBLIC SYNONYM ' || rTable.TABLE_NAME || ' FOR NEST.' || rTable.TABLE_NAME || ';';
DBMS_OUTPUT.PUT_LINE(sSql);
END LOOP;
END;
This script will print out commands like:
CREATE PUBLIC SYNONYM MY_TABLE FOR NEST.MY_TABLE
Additionally, instead of using DBMS_OUTPUT.PUT_LINE you can go with directly calling:
EXECUTE IMMEDIATE sSql;
instead. This will automagically create the synonyms for you.

The following statement will generate the necessary statements.
You need to spool/export the output of that statement into a SQL script and then run that script.
How you do that depends totally on which SQL client you are using. With SQL*Plus you would e.g. use the spool command.
select 'create public synonym '||table_name||' for '||owner||'.'||table_name||';'
from all_tables
where owner = 'NEST';

Related

Trying to run consecutive queries in Oracle

I am creating a temp table
CREATE TABLE TMP_PRTimeSum AS
SELECT DISTINCT p.employee,
SUM(p.wage_amount) AS wage_sum,
SUM(p.hours) AS hour_sum
I then want to do a select from that temp table and show the results. I want to do it all in one query. It works if I run them as two separate queries. Is there any way to do this in one query?
CREATE GLOBAL TEMPORARY TABLE a
ON COMMIT PRESERVE ROWS
AS
select * from b;
(add where 1=0 too if you didn't want to initially populate it for the current session with all the data from b).
Is there any way to do this in one query?
No, you need to use one DDL statement (CREATE TABLE) to create the table and one DML statement (SELECT) to query the newly created table.
If you want to be really pedantic then you could do it in a single PL/SQL statement:
DECLARE
cur SYS_REFCURSOR;
v_employee VARCHAR2(20);
v_wage NUMBER;
v_hours NUMBER;
BEGIN
EXECUTE IMMEDIATE
'CREATE TABLE TMP_PRTimeSum (employee, wage_sum, hour_sum) AS
SELECT P.EMPLOYEE,
SUM(P.WAGE_AMOUNT),
SUM(P.HOURS)
FROM table_name p
GROUP BY p.Employee';
OPEN cur FOR 'SELECT * FROM TMP_PRTimeSum';
LOOP
FETCH cur INTO v_employee, v_wage, v_hours;
EXIT WHEN cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_employee || ', ' || v_wage || ', ' || v_hours);
END LOOP;
END;
/
db<>fiddle here
However, you are then wrapping the two SQL statements in a PL/SQL anonymous block so, depending on how you want to count it then it is either one composite statement, two SQL statements or three (2 SQL and 1 PL/SQL) statements.

Creating a synonym for all objects in another schema

I am interested in creating a SYNONYM for another schema and all objects in them (Tables, View, Procedures, Packages, etc). Currently users are getting an error that a certain table doesn't exist. It's happening because they are running a query like SELECT * FROM mytable. If the query were SELECT * FROM myschema.mytable it would work.
Does creating public synonym make the schema available to all users or roles? Are there any security issues with granting a public synonym? Should I use a non-public synonym? We are allocating permissions to the schema via roles.
Is there a query or script that would enable this?
Yes, you probably want a public synonym. From the docs:
Specify PUBLIC to create a public synonym. Public synonyms are
accessible to all users. However each user must have appropriate
privileges on the underlying object in order to use the synonym.
I think the main security issue they mention is to not create a public synonym with the same name as an existing schema.
See also object name resolution.
There's not a simple command, but you could script it with something like this:
begin
for r in (select owner, table_name from all_tables where owner = 'MYSCHEMA')
loop
execute immediate 'create public synonym ' || r.table_name || ' for ' || r.owner || '.' || r.table_name;
end loop;
end;
/
Edit: if you want more than just tables, you can change the query to loop over all_objects instead and pick the object types you want:
begin
for r in (select owner, object_name from all_objects
where owner = 'MYSCHEMA'
and object_type in ('TABLE','VIEW','PROCEDURE','FUNCTION','PACKAGE'))
loop
execute immediate 'create public synonym ' || r.object_name
|| ' for ' || r.owner || '.' || r.object_name;
end loop;
end;
/

Oracle Stored Procedure posing a prob

[EDIT]Editing the code to reflect changes coming from comments
I have a problem with one of the stored procedures I'm trying to create in an Oracle database.
The goal is to update every table which has an indiv column.
CREATE OR REPLACE PROCEDURE sp_majUserOnAllK (lastU IN VARCHAR2, newU IN VARCHAR2)
AS
BEGIN
FOR item IN (
select table_name , owner
from all_tab_columns
where column_name = 'INDIV' AND OWNER ='K'
)
LOOP
EXECUTE IMMEDIATE 'UPDATE K.' || item.table_name || ' SET indiv = :newValue WHERE indiv = :oldValue' USING newU, lastU;
END LOOP;
END sp_majUserOnAllK;
exec sp_majUserOnAllK( 'hum','hum');
Problem is, when I try to execute the stored procedure, I got an error message with no detail at all ('non valid SQL').
I tried taking the code out of the stored procedure. And there, it works. Only the beginning is changing to :
DECLARE
newU NVARCHAR2(50);
lastU NVARCHAR2(50);
req VARCHAR2(100);
CURSOR ctable IS
select table_name , owner from all_tab_columns where column_name = 'INDIV' AND OWNER ='KEXPLOIT';
BEGIN
newU := 'hum';
lastU := 'hum';
FOR item IN ctable
....
Like that, it works perfectly and does exactly what it is supposed to do.
As the only difference is the assignation of the variable, I think I may have a problem with my procedure declaration but I can't find a solution. The compilation is ok.
Any idea ?
Your procedure's syntax is not correct. Try this.
CREATE OR REPLACE PROCEDURE sp_majUserOnAllK (lastU IN VARCHAR2, newU IN VARCHAR2)
IS
req VARCHAR2(100);
BEGIN
FOR item IN (select table_name , owner from all_tab_columns where column_name = 'INDIV' AND OWNER ='K')
LOOP
req := 'UPDATE K.' || item.table_name || ' SET indiv = :newValue WHERE indiv = :oldValue';
EXECUTE IMMEDIATE req USING newU, lastU;
END LOOP;
-- return 1; -- note: procedures do not return values
END;
/
A five-second Google search on "dbeaver exec command" brought this up among the first few hits:
https://github.com/dbeaver/dbeaver/issues/749
In it, we learn that EXEC is not supported by dbeaver.
EXEC is an SQL*Plus command. It is not Oracle SQL, and it is not PL/SQL. SQL*Plus is a shell program of sorts for interacting with Oracle databases; it has its own language, distinct from SQL and PL/SQL.
SQL Developer and Toad (and perhaps other similar programs) support (most of) SQL*Plus, but apparently dbeaver (with which I am not familiar) does not.
The link I copied above suggests using the CALL command instead. See the link for examples.
As an aside, when we use EXEC in SQL*Plus and SQL Developer, there is no semicolon at the end of the procedure call. Adding an unnecessary semicolon, however, does not throw an error (SQL*Plus is, apparently, smart enough to simply ignore it).

ORACLE: Cursor with dynamic query - throws error "invalid identifier" for cursor field

I have a logic to implement where I have to use dynamic sql(column names and where clause is decided on the fly).So here my cursor(emp_ref_cursor) has a dynamic sql, and has 3 cursor fields(emp_id,emp_name,dept).
Using these cursor fields in WHERE clause I am trying to execute another dynamic sql inside the loop.Bt oracle isn't able to identify the cursor field and throws an error like "ORA-00904: "EMP_REC"."EMP_ID": invalid identifier" though I am able to output emp_rec.emp_id through DBMS_OUTPUT.
NOTE: Please don't comment on the code quality this is not the actual code.
This is just used to describe the problem. I can't post the actual code due to
some compliance related stuff.
DECLARE
emp_ref_cursor sys_refcursor;
v_sql varchar2(3900);
TYPE emp_rec_type IS RECORD (emp_id number,emp_name varchar2(100),dept_id varchar2(100));
emp_rec emp_rec_type;
v_dept_id number:='1234';
v_dob varchar2(100);
v_desig varchar2(100);
x_dynamic_col_1 varchar2(100):='dob'; --dynamic column(based on some condition)
x_dynamic_col_2 varchar2(100):='designation'; --dynamic column(based on some condition)
x_dynamic_col_3 varchar2(100):='emp_id'; --dynamic column(based on some condition)
BEGIN
v_sql:='SELECT emp_id,emp_name,dept FROM employee WHERE dept_id=' || v_dept_id;
OPEN emp_ref_cursor FOR v_sql;
LOOP
FETCH emp_ref_cursor INTO emp_rec;
exit WHEN emp_ref_cursor%NOTFOUND;
stmt:='SELECT ' || x_dynamic_col_1 || ',' || x_dynamic_col_2 || '
FROM employee A
WHERE emp_id=emp_rec.' || x_dynamic_col_3;
DBMS_OUTPUT.PUT_LINE(stmt);
--Prints the SQL query as expected
DBMS_OUTPUT.PUT_LINE('emp_rec.emp_id:'||emp_rec.emp_id);
--Displays the value!!!
execute immediate stmt into v_dob, v_desig;
--But why is it saying emp_rec.emp_id is invalid identifier??
END LOOP;
END;
You have emp_rec defined as a local PL/SQL variable. None of the PL/SQL data is in scope to the dynamic SQL execution. When it is executed it as if you tried to run the statement - as it is displayed by your dbms_output standalone in a separate SQL context. If you did that it would be clear that emp_rec doesn't exist to the query.
You refer to it you would need to use a bind variable:
WHERE emp_id=:dynamic_col_3';
And then execute it with:
execute immediate stmt using emp_rec.emp_id;
But you can't use the x_dynamic_col_3 local variable in the using clause. Since - in this example anyway - the query would also need to change to use a different table column is the dynamic record field changed - that doesn't seem too much of a problem. But you said the where clause will change on the fly too. In that case you could have another local variable that you set to the relevant x field before the executin.
You have incorrect using of EXECUTE IMMEDIATE. You don't need to put INTO clause to SQL query. Use this instead:
stmt:='SELECT ' || x_dynamic_col_1 || ',' || x_dynamic_col_2 || '
FROM employee A
WHERE emp_id=emp_rec.' || x_dynamic_col_3;
execute immediate stmt into v_dob, v_desig;

Writing a PL SQL change script (sql developer/oracle)

I have an assignment for uni now where I need to write a database change script and then a rollback script. I should also do some simple checks whether the changes has been done or not. I have spent enormous time by writing the scripts because I am not skilled in plsql. The prodcut is here:
-- the check could be more extensive, e.g. checking the type of the column
declare
titleExists number;
begin
select count(*) into titleExists
from user_tab_columns
where table_name = 'TITLE'
and column_name = 'TITLE';
if titleExists > 0 then
execute immediate 'alter table title rename column title to name';
end if;
end;
/
declare
typeExists number;
begin
select count(*) into typeExists
from user_tab_columns
where table_name = 'TITLE'
and column_name = 'TYPE'
and data_type = 'CHAR';
if typeExists > 0 then
execute immediate 'alter table title add (new_type varchar2(12) check (new_type in (''business'', ''mod_cook'', ''psychology'', ''popular_comp'', ''trad_cook'')))';
execute immediate 'update title set new_type = trim(type)';
execute immediate 'alter table title modify (new_type not null)';
execute immediate 'alter table title drop column type';
execute immediate 'alter table title rename column new_type to type';
end if;
end;
/
The first part renames a column and the second part changes columns type and adds a check, basically turns a char column into an enum.
I would really like to know whehter I need to put every alteration in execute immediate block. Is there a simpler way of writing this?
There are a number of issues with the script, but keeping myself limited to the question whether to use execute immediate:
There is not really a simpler way of writing this. The PL/SQL is modifying the database it runs on, so adding for instance the update title occur as a hard-coded section in the PL/SQL is not possible (it would not compile).
Also, sometimes it is possible to merge multiple statements executed through execute immediate in one big PL/SQL being send over. But PL/SQL itself does not support the DDL statements, so you need some way of dynamic SQL.
Note also that each DDL does an implicit commit, so your update is always executed and committed by the following alter table statement.
I think you have done great being this your first PL/SQL assignment.

Resources