How to convert every Table from a specific User to JSON Format using the "trick" provided by SQL Developer - sql

I want to convert all tables from a specific user to JSON (or XML) Format. I've read about a "trick" mentioned by SQL Developer.
In other words, I already started to create a Procedure with two parameters:
p_format: The format (in my case it will be "json")
p_user: The username
As IDE I use Oracle SQL Developer and my database is an Oracle XE Database.
At first the procedure loops though all tables of the given user and in the loop, it should execute the following:
SELECT /*p_format*/ * FROM p_user || '.' || table
Unfortunately, I cannot use this SELECT Statement as mentioned above. I need to use the command EXECUTE IMMEDIATE <Statement>.
The next problem I faced was the following: I wanted to output the result of the EXECUTE IMMEDIATE command. Therefore I used the command EXECUTE IMMEDIATE <Statement> INTO <Variable>. After compiling the procedure and executing it, I stumpled across the following Error:
"inconsistent datatypes: expected %s got %s"
This is my code of the procedure:
CREATE OR REPLACE PROCEDURE EXPORT_TABLE_TO_FORMAT_FROM(p_format VARCHAR2, p_user VARCHAR2) IS
/***************************************************************************
Author:
Class:
School:
Date:
Function - EXPORT_TABLE_TO_JSON_FROM(p_user):
Displays the data of every table from a given User as JSON
Parameter: p_user ... User
***************************************************************************/
v_tableData VARCHAR2(32767);
v_sqlStatement VARCHAR2(200);
BEGIN
FOR tablerec IN (SELECT *
FROM ALL_TABLES
WHERE OWNER = p_user)
LOOP
v_sqlStatement := 'SELECT /*' || p_format || '*/ * FROM ' || p_user || '.' || tablerec.TABLE_NAME;
EXECUTE IMMEDIATE v_sqlStatement INTO v_tableData;
DBMS_OUTPUT.PUT_LINE (v_sqlStatement);
END LOOP;
END;
You can see that I loop though all tables of a given user and created a sql statement with p_format and p_user and with tablerec.TABLE_NAME.
The desired result should look exactly like that:
{"results":[{"columns":[{"name":"COUNTRY_ID","type":"CHAR"},
{"name":"COUNTRY_NAME","type":"VARCHAR2"},{"name":"REGION_ID","type":"NUMBER"}],"items":
[
{"country_id":"AR","country_name":"Argentina","region_id":2},
{"country_id":"AU","country_name":"Australia","region_id":3},
{"country_id":"BE","country_name":"Belgium","region_id":1},
{"country_id":"BR","country_name":"Brazil","region_id":2},
{"country_id":"CA","country_name":"Canada","region_id":2},
{"country_id":"CH","country_name":"Switzerland","region_id":1},
{"country_id":"CN","country_name":"China","region_id":3},
{"country_id":"DE","country_name":"Germany","region_id":1},
{"country_id":"DK","country_name":"Denmark","region_id":1},
{"country_id":"EG","country_name":"Egypt","region_id":4},
{"country_id":"FR","country_name":"France","region_id":1},
{"country_id":"IL","country_name":"Israel","region_id":4},
{"country_id":"IN","country_name":"India","region_id":3},
{"country_id":"IT","country_name":"Italy","region_id":1},
{"country_id":"JP","country_name":"Japan","region_id":3},
{"country_id":"KW","country_name":"Kuwait","region_id":4},
{"country_id":"ML","country_name":"Malaysia","region_id":3},
{"country_id":"MX","country_name":"Mexico","region_id":2},
{"country_id":"NG","country_name":"Nigeria","region_id":4},
{"country_id":"NL","country_name":"Netherlands","region_id":1},
{"country_id":"SG","country_name":"Singapore","region_id":3},
{"country_id":"UK","country_name":"United Kingdom","region_id":1},
{"country_id":"US","country_name":"United States of America","region_id":2},
{"country_id":"ZM","country_name":"Zambia","region_id":4},
{"country_id":"ZW","country_name":"Zimbabwe","region_id":4}]}]}

The JSON hint is specific to SQL Developer and SQLcl, not the database directly. So you need to run the entire thing within these tools.
Easiest way to do that is to have your script write a script that you can then run, eg
spool /tmp/get_all_json.sql
select 'select /*json*/ * from '||table_name||';'
from user_tables;
spool off
#/tmp/get_all_json.sql

Related

Check table access for Oracle SQL statements in BLOB

I have a table with a column containing SQL query (select statements only) in BLOB format.
I'm trying to figure out a way to analyse the SQL queries, and check which tables each SQL queries will be accessing.
Anyone done anything similar?
My thought right now is to create a PLSQL loop that:
Converts the BLOB to text to get the SQL query
Execute an explain plan for the query
Look at the result of the explain plan, and get all the lines with TABLE ACCESS
Edit: I'm on Oracle Database 11g Release 11.2.0.4.0 - 64bit if that helps, and have read-only access to the database, so I'll need to create a solution that's not intrusive to the DB.
This could be another simpler method.
Create dynamic views for those SQLs in the BLOB field.
EXECUTE IMMEDIATE 'CREATE OR REPLACE VIEW YOUR_VIEW_NAME AS '||
utl_raw.cast_to_varchar2(dbms_lob.substr(blob_field));
Query user_dependencies to get the referenced table names.
SELECT referenced_name AS table_name
FROM user_dependencies
WHERE type = 'VIEW'
AND NAME = 'YOUR_VIEW_NAME'
AND referenced_type = 'TABLE';
So, here is how I have done it:
DECLARE
b BLOB;
c CLOB;
n NUMBER;
BEGIN
-- << loop_through_table >>
FOR o IN (SELECT TRIM(LABEL) lbl, REQUEST req FROM REQUEST_TABLE) LOOP
b := o.req;
IF ((b is null) OR (LENGTH(b)=0)) THEN
DBMS_OUTPUT.PUT_LINE(o.lbl || ' : No query found');
CONTINUE;
END IF;
-- Convert the BLOB to text to get the SQL query
DBMS_LOB.CREATETEMPORARY(c,TRUE);
n:=1;
-- << loop_through_blob >>
WHILE (n+32767<=LENGTH(b)) LOOP
DBMS_LOB.WRITEAPPEND(c,32767,UTL_RAW.CAST_TO_VARCHAR2(DBMS_LOB.SUBSTR(b,32767,n)));
n:=n+32767;
END LOOP;
DBMS_LOB.WRITEAPPEND(c,length(b)-n+1,UTL_RAW.CAST_TO_VARCHAR2(DBMS_LOB.SUBSTR(b,length(b)-n+1,n)));
-- Execute explain plan
execute immediate 'EXPLAIN PLAN SET STATEMENT_ID=''temp1'' FOR ' || c;
-- Look at the result of the explain plan, and get all the lines of OBJECT_TYPE='TABLE'
-- << loop_through_plan >>
FOR p IN ( SELECT OBJECT_NAME FROM PLAN_TABLE WHERE STATEMENT_ID='temp1' AND OBJECT_TYPE='TABLE') LOOP
DBMS_OUTPUT.PUT_LINE(o.lbl || ', ' || p.OBJECT_NAME);
END LOOP;
execute immediate 'DELETE FROM PLAN_TABLE WHERE STATEMENT_ID=''temp1'' ';
END LOOP;
DBMS_OUTPUT.PUT_LINE('-- Completed --');
END;

Pass SQL string to oracle stored procedure and get results with execute immediate

I am trying to pass in a SQL string to a stored procedure and using EXECUTE IMMEDIATE to return the results. Something like this:
CREATE PROCEDURE P360_RCT_COUNT (sqlString IN VARCHAR2)
AS
BEGIN
EXECUTE IMMEDIATE sqlString;
END;
/
I am not sure how to accomplish it. With the above, when I execute the SP using the command below, I get an error:
EXECUTE P360_RCT_COUNT 'SELECT COUNT(DISTINCT ENTITY_ID),ADDR_COUNTY FROM P360_V_RCT_COUNT GROUP BY ADDR_COUNTY';
The error is: ORA-06550: line 1, column 22:
PLS-00103: Encountered the symbol "SELECT COUNT(ENTITY_ID),ADDR_COUNTY
FROM P360_V_RCT_COUNT GROUP " when expecting one of the following:
:= . ( # % ; The symbol ":=" was substituted for "SELECT
COUNT(DISTINCT ENTITY_ID),ADDR_COUNTY FROM P360_V_RCT_COUNT GROUP " to
continue.
Basically I am building a SQL string in a system and need to pass it in to the SP and get the results back to the system. I am relatively new to stored procedures in Oracle.
The easiest way to work with a result set is sys_refcursor. This can be used quite easily with JDBC or ODBC.
Your procedure would look like this:
CREATE PROCEDURE P360_RCT_COUNT (
sqlString IN VARCHAR2
, p_result_set out sys_refcursor)
AS
BEGIN
open p_result_set for sqlString;
END;
/
Obviously the precise details of how you call it will vary according to your client. But in SQL*Plus it would be:
var rc refcursor
exec P360_RCT_COUNT( 'SELECT COUNT(DISTINCT ENTITY_ID),ADDR_COUNTY FROM P360_V_RCT_COUNT GROUP BY ADDR_COUNTY', :rc);
print rc
To return lists of values in a OUT parameter you need to decide the type(s) to use.
Say, for example, you have to return some varchar2 and some date lists, you could use something like this:
create or replace type tabOfVarchar2 is table of varchar2(100);
create or replace type tabOfDates is table of date;
create or replace procedure testProc(pString IN varchar2,
pOutVarchar1 OUT tabOfVarchar2,
pOutVarchar2 OUT tabOfVarchar2,
pOutVarchar3 OUT tabOfVarchar2,
pOutDates OUT tabOfDates
) is
begin
execute immediate pString
bulk collect into pOutVarchar1, pOutVarchar2, pOutVarchar3, pOutDates;
end;
This is way you can test this procedure:
declare
v1 tabOfVarchar2 ;
v2 tabOfVarchar2;
v3 tabOfVarchar2;
d1 tabOfDates ;
vSQL varchar2(100) := 'select ''a'', ''b'', ''c'', sysdate from dual';
begin
testProc(vSQL, v1, v2, v3, d1);
--
for i in v1.first .. v1.last loop
dbms_output.put_line(v1(i) || '/' || v2(i) || '/' || v3(i) || '/' || to_char(d1(i), 'dd/mm/yyyy'));
end loop;
end;
which gives:
a/b/c/14/04/2017
This only works with queries that give exactly a fixed number of columns, of known types.

SQL query help - find table in unknown schema and query data

I have a couple of simple queries I run manually in Oracle as sysdba to check the version info for a java application. The tricky part is that the schema names vary, so sometimes I have to search for the table to find the owner/schema and then query the info.
Is there a way to take the output from one query and use it to perform a schema-qualified query, so it can be automated?
The queries I use are:
SELECT username FROM dba_tables WHERE table_name LIKE 'APPVERSDATA%';
SELECT max(appfullversion) from SCHEMA.APPVERSDATA;
There must be a way to pass the name of the schema from the first query as a variable to the second query?
Edit: Ultimately I would like to set the output as a variable in shell script.
TIA
I assume that SCHEMA.APPVERSDATA would be the output of the first query so the best thing to do might be execute some PL/SQL as a store function or procedure:
DECLARE
l_schema varchar2(10);
BEGIN
SELECT DISTINCT owner
INTO l_schema
FROM dba_tables
WHERE table_name LIKE 'APPVERSDATA%';
EXECUTE IMMEDIATE 'SELECT max(appfullversion) from ' || l_schema || '.APPVERSDATA';
end;
/
This doesn't address what you are going to do with the data once it executes though. Update the question and I'll update the answer.
Edit(Version 2!):
So your shell would look something like this:
x () {
sqlplus -s u/p<<EOF
set serveroutput on;
set feedback off;
DECLARE
l_schema varchar2(10);
BEGIN
SELECT DISTINCT owner
INTO l_schema
FROM dba_tables
WHERE table_name LIKE 'APPVERSDATA%';
EXECUTE IMMEDIATE 'SELECT max(appfullversion) from ' || l_schema || '.APPVERSDATA';
dbms_output.put_line(l_return);
end;
/
EOF
}
echo $(x $1)

Oracle SQL Developer, using dynamic SQL in function

I have the following script which contains a function named 'myFunction'. (declaration of types named rowValueTmp and rowValueTable are also attached for your information) Basically, I need to use a table name as an input parameter for myFunction. I found that I need to use dynamic SQL in order to use the table name as a parameter (Please correct me if there are alternative ways to do this). So the following code is what I have tried so far.
create or replace type rowValueTmp as object (
month number,
year number
);
/
create or replace type rowValueTable as table of rowValueTmp;
/
create or replace FUNCTION myFunction (TABLENAME in VARCHAR2)
return rowValueTable as
v_ret rowValueTable;
begin
execute immediate '
select rowValueTmp(month, year)
bulk collect into v_ret
from '||TABLENAME;
return v_ret;
end myFunction;
/
select * from table(myFunction('SCHEMA.TEST'));
But, this code gives me an error, and I assumed that this error is occurred because of using 'bulk collect' in execute immediate block.
ORA-03001: unimplemented feature
If I replace the content of execute immediate as the following, the above script is working..
select rowValueTmp(month, year)
bulk collect into v_ret
from SCHEMA.TEST;
Question
1] Is there any way(rather than Dynamic SQL) that I can use a table name as an input parameter for myFunction?
2] If I am not allowed to use bulk collect in execute immediate block, what do you suggest?
You can return values from execute immediately into a bulk collect:
CREATE OR REPLACE FUNCTION myfunction (tablename IN VARCHAR2)
RETURN rowvaluetable AS
v_ret rowvaluetable;
v_table VARCHAR2 (61) := DBMS_ASSERT.sql_object_name (tablename);
BEGIN
EXECUTE IMMEDIATE '
select rowValueTmp(month, year)
from ' || v_table
BULK COLLECT INTO v_ret;
RETURN v_ret;
END myfunction;
/
In the interest of an abundance of caution, I'd recommend using DBMS_ASSERT to validate the table parameter as well (as shown).

Oracle Stored Procedure with Alter command

I am trying to build an Oracle stored procedure which will accept a table name as a parameter. The procedure will then rebuild all indexes on the table.
My problem is I get an error while using the ALTER command from a stored procedure, as if PLSQL does not allow that command.
Use the execute immediate statement to execute DDL inside PL/SQL.
create procedure RebuildIndex(index_name varchar2) as
begin
execute immediate 'alter index ' || index_name || ' rebuild';
end;
I tested this code; it works.
Documentation.
Passing Schema Object Names As Parameters
Suppose you need a procedure that
accepts the name of any database
table, then drops that table from your
schema. You must build a string with a
statement that includes the object
names, then use EXECUTE IMMEDIATE to
execute the statement:
CREATE TABLE employees_temp AS SELECT last_name FROM employees;
CREATE PROCEDURE drop_table (table_name IN VARCHAR2) AS
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE ' || table_name;
END;
/
Use concatenation to build the string,
rather than trying to pass the table
name as a bind variable through the
USING clause.
In addition, if you need to call a
procedure whose name is unknown until
runtime, you can pass a parameter
identifying the procedure. For
example, the following procedure can
call another procedure (drop_table) by
specifying the procedure name when
executed.
CREATE PROCEDURE run_proc (proc_name IN VARCHAR2, table_name IN VARCHAR2) ASBEGIN
EXECUTE IMMEDIATE 'CALL "' || proc_name || '" ( :proc_name )' using table_name;
END;
/
If you want to drop a table with the
drop_table procedure, you can run the
procedure as follows. Note that the
procedure name is capitalized.
CREATE TABLE employees_temp AS SELECT last_name FROM employees;
BEGIN
run_proc('DROP_TABLE', 'employees_temp');
END;
/
Here are a couple of possibilities. First, you would have to treat the SQL as dynamic SQL. Second, Oracle DDL statements cannot be run in a transaction (or, they terminate the current transaction and cannot themselves be rolled back). This may affect whether you can use them in stored procedures, or where you can use stored procedures that contain them.
If none of the above apply at all - there could easily be something else astray - I suggest posting some code.