With Database Actions (SQL Developer Web), it's quite easy to click on the 'New JSON Document' button to add a new JSON document to the collection.
The collection of course is actually a table in Oracle, and the table itself has a number of columns:
I have created modules in ORDS with PL/SQL handlers. While I am able to update JSON documents here by using
UPDATE "Collection" SET json_document = '{"key": "value"}' WHERE JSON_VALUE(json_document, '$.id') = :id'
I am not able to add a new document easily with
INSERT INTO "Collection" (json_document) VALUES ('{"key": "value"}')
because the id is set as a PK column and must be specified. How might I use PL/SQL to add a new document with auto generated fields elsewhere? Or should I use SODA for PL/SQL to achieve this only?
Thanks!
You use the dbms_soda package to access the collection. Then use the methods on soda_colletion_t to manipulate it.
For example:
soda create students;
declare
collection soda_collection_t;
document soda_document_t;
status number;
begin
-- open the collection
collection := dbms_soda.open_collection('students');
document := soda_document_t(
b_content => utl_raw.cast_to_raw (
'{"id":1,"name":"John Blaha","class":"1980","courses":["c1","c2"]}'
)
);
-- insert a document
status := collection.insert_one(document);
end;
/
select * from students;
ID CREATED_ON LAST_MODIFIED VERSION JSON_DOCUMENT
-------------------------------- ------------------------ ------------------------ -------------------------------- ---------------
A92F68753B384F87BF12557AC38098CB 2021-12-22T14:15:12.831Z 2021-12-22T14:15:12.831Z FE8C80FED46A4F18BFA070EF46073F43 [object Object]
For full documentation and examples on how to use SODA for PL/SQL, see here.
Related
I'm exploring cx_Oracle's JSON features within a CLOB. I have an index on the table that allows me to query for direct equality
SELECT * FROM mytable m WHERE m.jsonclob.jsonattribute = 'foo';
I'd like to be able to do the same thing with a LIKE statement.
SELECT * FROM mytable m WHERE m.jsonclob.jsonattribute LIKE 'foo.%';
This works for me with Oracle DB 12.2:
SQL> CREATE TABLE j_purchaseorder_b (po_document CLOB CHECK (po_document IS JSON)) LOB (po_document) STORE AS (CACHE);
Table created.
SQL> INSERT INTO j_purchaseorder_b VALUES ('{"userId":2,"userName":"Bob","location":"USA"}');
1 row created.
SQL> SELECT pob.po_document.location FROM j_purchaseorder_b pob where pob.po_document.location LIKE 'US%';
LOCATION
--------------------------------------------------------------------------------
USA
For reference check the Oracle JSON manual chapter Query JSON Data.
A side note: the JSON team like recommending BLOB for storage for performance reasons. Check the doc etc etc etc.
As the title said : I want to create a type in oracle based on an existing Table.
I did as follow :
create or replace type MY_NEW_TYPE as object( one_row EXISTING_TABLE%rowtype);
The Aim is to be able to use this into a function which will return a table containing sample row of the table EXISTING_TABLE :
create or replace function OUTPUT_FCT() return MY_NEW_TYPE AS
...
If you only need to create a function that returns a row from your table, you could try something like the following, without creating types.
setup:
create table EXISTING_TABLE( a number, b varchar2(100));
insert into EXISTING_TABLE values (1, 'one');
function:
create or replace function OUTPUT_FCT return EXISTING_TABLE%rowtype AS
retVal EXISTING_TABLE%rowType;
begin
select *
into retVal
from EXISTING_TABLE
where rownum = 1;
--
return retVal;
end;
function call
SQL> begin
2 dbms_output.put_line(OUTPUT_FCT().a);
3 dbms_output.put_line(OUTPUT_FCT().b);
4 end;
5 /
1
one
However, I would not recommend such an approach, because things like select * can be really dangerous; I would much prefer defining a type with the fields I need, and then explicitly query my table for the needed columns.
No, you can't do that, you'll get a compilation error:
create or replace type my_new_type as object(one_row t42%rowtype);
/
Type MY_NEW_TYPE compiled
Errors: check compiler log
show errors
Errors for TYPE STACKOVERFLOW.MY_NEW_TYPE:
LINE/COL ERROR
-------- -----------------------------------------------------------------------
0/0 PL/SQL: Compilation unit analysis terminated
1/36 PLS-00329: schema-level type has illegal reference to MYSCHEMA.T42
You will need to specify each field in the object type, and you will have to specify the data types manually too - you can't use table.column%type either.
You could create the type dynamically based on column and data type information from the data dictionary, but as this will (hopefully) be a one-off task and not something you'd do at runtime, that doesn't really seem worth it.
You can create a PL/SQL table type based on your table's rowtype, but you would only be able to call a function returning that from PL/SQL, not from plain SQL - so you couldn't use it in a table collection expression for example. If you were only returning a single sample row you could return a record rather than a table, but the same applies. You can also have a function that returns a ref cursor which could match the table's structure, but you wouldn't be able to treat that as a table either.
Read more about object type creation in the documentation. Specifically the attribute and datatype sections.
I have created a VIEW by joining some number of tables. I used a SP to call that VIEW and in the SP I'm filtering the data set of the view using an ID. Now I need to pass this ID to VIEW and do the filtering inside the VIEW itself. What is the way of passing this ID as a parameter to view in Oracle 10g?
Current View
CREATE OR REPLACE FORCE VIEW "MY_VIEW"
//SELECT statements goes here
FROM MY_TABLE_1, MY_TABLE_2
//TABLE JOINS
where
//FILTERS
Current Stored Procedure
CREATE OR REPLACE PROCEDURE MY_SP
(
REQUESTACCOUNTID IN NUMBER
, p_cursor out SYS_REFCURSOR
) AS
internal_flag NUMBER;
BEGIN
open p_cursor for
SELECT //SELECT THE COLUMNS
from MY_VIEW my
WHERE my.account_id = REQUESTACCOUNTID;
END MY_SP;
What I need to do is, parse the parameter REQUESTACCOUNTID to the view while selecting
this is sorted out using a package variable. More explanation can be found using this URL
Pl/SQL:
Intent: My intent was to access employee tuple object defied as cursor below by using key as the employee_id.
Problem: I created a cursor - *l_employees_cur* and want to create type table as below type *l_employees_t*, as below but the compiler is complaining saying that PLS-00315 implementation restriction unsupported table index type.
CURSOR l_employees_cur
IS
SELECT employee_id,manager_id,first_name,last_name FROM employees;
type l_employees_t
IS
TABLE OF l_employees_cur%rowtype INDEX BY employees.employee_id%TYPE;
The definition of employees.employee_id is:
EMPLOYEE_ID NUMBER(6) NOT NULL
why can't I do this ? or Am I doint something wrong.
From the Oracle Documenation:
Associative Arrays
An associative array (formerly called PL/SQL table or index-by table) is a set of key-value pairs. Each key is a unique index, used to locate the associated value with the syntax variable_name(index).
The data type of index can be either a string type or PLS_INTEGER. Indexes are stored in sort order, not creation order. For string types, sort order is determined by the initialization parameters NLS_SORT and NLS_COMP.
I think that your mistake is the declaration of the plsql table.
Why don't you try the next one:
type l_employees_t
IS
TABLE OF l_employees_cur%rowtype INDEX BY pls_integer;
I also have a question for you:
What is the meaning of EMPLOYEE_ID NOT NULL NUMBER(6) in your code above?
Greetings
Carlos
Storing and Retreiving SQL Query Output in a PL/SQL Collection
The example in the OP looks a lot like Oracle's new sample HR data schema. (For those old-timers who know, the successor to the SCOTT-TIGER data model). This solution was developed on an Oracle 11g R2 instance.
The Demo Table Design - EMP
Demonstration Objectives
This example will show how to create a PL/SQL collection from an object TYPE definition. The complex data type is derived from the following cursor definition:
CURSOR l_employees_cur IS
SELECT emp.empno as EMPLOYEE_ID, emp.mgr as MANAGER_ID, emp.ename as LAST_NAME
FROM EMP;
After loading the cursor contents into an index-by collection variable, the last half of the stored procedure contains an optional step which loops back through the collection and displays the data either through DBMS_OUTPUT or an INSERT DML operation on another table.
Stored Procedure Example Source Code
This is the stored procedure used to query the demonstration table, EMP.
create or replace procedure zz_proc_employee is
CURSOR l_employees_cur IS
SELECT emp.empno as EMPLOYEE_ID, emp.mgr as MANAGER_ID, emp.ename as LAST_NAME
FROM EMP;
TYPE employees_tbl_type IS TABLE OF l_employees_cur%ROWTYPE INDEX BY PLS_INTEGER;
employees_rec_var l_employees_cur%ROWTYPE;
employees_tbl_var employees_tbl_type;
v_output_string varchar2(80);
c_output_template constant varchar2(80):=
'Employee: <<EMP>>; Manager: <<MGR>>; Employee Name: <<ENAME>>';
idx integer;
outloop integer;
BEGIN
idx:= 1;
OPEN l_employees_cur;
FETCH l_employees_cur INTO employees_rec_var;
WHILE l_employees_cur%FOUND LOOP
employees_tbl_var(idx):= employees_rec_var;
FETCH l_employees_cur INTO employees_rec_var;
idx:= idx + 1;
END LOOP;
CLOSE l_employees_cur;
-- OPTIONAL (below) Output Loop for Displaying The Array Contents
-- At this point, employees_tbl_var can be handed off or returned
-- for additional processing.
FOR outloop IN 1 .. idx LOOP
-- Build the output string:
v_output_string:= replace(c_output_template, '<<EMP>>',
to_char(employees_tbl_var(outloop).employee_id));
v_output_string:= replace(v_output_string, '<<MGR>>',
to_char(employees_tbl_var(outloop).manager_id));
v_output_string:= replace(v_output_string, '<<ENAME>>',
employees_tbl_var(outloop).last_name);
-- dbms_output.put_line(v_output_string);
INSERT INTO zz_output(output_string, output_ts)
VALUES(v_output_string, sysdate);
COMMIT;
END LOOP;
END zz_proc_employee;
I commented out the dbms_output call due to problems with the configuration of my server beyond my control. The alternate insert command to a output table is a quick way of visually verifying that the data from the EMP table found its way successfully into the declared collection variable.
Results and Discussion of the Solution
Here is my output after calling the procedure and querying my output table:
While the actual purpose behind the access to this table isn't clear in the very terse detail of the OP, I assumed that the first approach was an attempt to understand the use of collections and custom data types for efficient data extraction and handling from structures such as PL/SQL cursors.
The portion of this example procedure is very reusable, and the initial steps represent a working way of making and loading PL/SQL collections. If you notice, even if your own version of this EMP table is different, the only place that requires redefinition is the cursor itself.
Working with types, arrays, nested tables and other collection types will actually simplify work in the long run because of their dynamic nature.
I need to be able to reconstruct a table column by using the column data in DBA_TAB_COLUMNS, and so to develop this I need to understand what each column refers to. I'm looking to understand what DATA_TYPE_MOD is -- the documentation (http://docs.oracle.com/cd/B19306_01/server.102/b14237/statviews_2094.htm#I1020277) says it is a data type modifier, but I can't seem to find any columns with this field populated or any way to populate this field with a dummy column. Anyone familiar with this field?
Data_type_mod column of the [all][dba][user]_tab_columns data dictionary view gets populated when a column of a table is declared as a reference to an object type using REF datatype(contains object identifier(OID) of an object it points to).
create type obj as object(
item number
) ;
create table tb_1(
col ref obj
)
select t.table_name
, t.column_name
, t.data_type_mod
from user_tab_columns t
where t.table_name = 'TB_1'
Result:
table_name column_name data_type_mod
-----------------------------------------
TB_1 COL REF
Oracle has a PL/SQL package that can be used to generate the DDL for creating a table. You would probably be better off using this.
See GET_DDL on http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_metada.htm#i1019414
And see also:
How to get Oracle create table statement in SQL*Plus