cx_Oracle query JSON CLOB with 'LIKE' - oracle

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.

Related

Inserting new JSON document into Oracle Autonomous JSON Database

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.

Unable to create table through another table in oracle SQL developer

I have been trying to create a new table by using below query :
"Create table d1_details_test2
as
select * from d1_details"
this above query gives me an error :
actually "d1_details" table has one column which has "Long" datatype and i cannot change it.
so i want to know the any other way to create the table.
Thanks
The long data type is subject to many restrictions. Create table as select is one of these.
You can get around it by applying to_lob in the select, which converts it to a clob:
create table views as
select view_name, text from user_views;
ORA-00997: illegal use of LONG datatype
create table views as
select view_name, to_lob ( text ) lob
from user_views;
desc views
Name Null? Type
VIEW_NAME VARCHAR2(128)
LOB CLOB

Creation of Oracle temporary table with same table structure to that of a existing table

How to create a global temporary table with same table structure to that of a existing table?
I know this concept is available in SQL server like "select * into #temp123 from abc". But I want to perform the same in Oracle.
Create global temporary table mytemp
as
select * from myTable
where 1=2
Global temporary tables in Oracle are very different from temporary tables in SQL Server. They are permanent data structures, it is merely the data in them which is temporary (limited to the session or transaction, depending on how a table is defined).
Therefore, the correct way to use global temporary tables is very different to how we use temporary tables in SQL Server. The CREATE GLOBAL TEMPORARY TABLE statement is a one-off exercise (like any other table). Dropping and recreating tables on the fly is bad practice in Oracle, which doesn't stop people wanting to do it.
Given the creation of a global temporary table should a one-off exercise, there is no real benefit to using the CREATE TABLE ... AS SELECT syntax. The statement should be explicitly defined and the script stored in source control like any other DDL.
You have tagged your question [oracle18c]. If you are really using Oracle 18c you have a new feature open to you, private temporary tables, which are closer to SQL Server temporary tables. These are tables which are genuinely in-memory and are dropped automatically at the end of the transaction or session (again according to definition). These are covered in the Oracle documentation but here are the headlines.
Creating a private temporary table data with a subset of data from permanent table T23:
create table t23 (
id number primary key
, txt varchar2(24)
);
insert into t23
select 10, 'BLAH' from dual union all
select 20, 'MEH' from dual union all
select 140, 'HO HUM' from dual
/
create private temporary table ORA$PTT_t23
on commit preserve definition
as
select * from t23
where id > 100;
The ORA$PTT prefix is mandatory (although it can be changed by setting the init.ora parameter PRIVATE_TEMP_TABLE_PREFIX, but why bother?
There after we can execute any regular DML on the table:
select * from ORA$PTT_t23;
The big limitation is that we cannot use the table in static PL/SQL. The table doesn't exist in the data dictionary as such, and so the PL/SQL compiler hurls - even for anonymous blocks:
declare
rec t23%rowtype;
begin
select *
into rec
from ORA$PTT_t23';
dbms_output.put_line('id = ' || rec.id);
end;
/
ORA-06550: line 6, column 10: PL/SQL: ORA-00942: table or view does not exist
Any reference to a private temporary table in PL/SQL must be done with dynamic SQL:
declare
n pls_integer;
begin
execute immediate 'select id from ORA$PTT_t23' into n;
dbms_output.put_line('id = ' || n);
end;
/
Basically this restricts their usage to SQL*Plus (or sqlcl scripts which run a series of pure SQL statements. So, if you have a use case which fits that, then you should check out private temporary tables. However, please consider that Oracle is different from SQL Server in many aspects, not least its multi-version consistency model: readers do not block writers. Consequently, there is much less need for temporary tables in Oracle.
In the SQL Server's syntax the prefix "#" (hash) in the table name #temp123 means - create temporary table that is only accessible via the current session ("##" means "Global").
To achive exactly the same thing in Oracle you can use private temporary tables:
SQL> show parameter private_temp_table
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
private_temp_table_prefix string ORA$PTT_
create table mytab as
select 1 id, cast ('aaa' as varchar2 (32)) name from dual
;
create private temporary table ora$ptt_mytab on commit preserve definition as
select * from mytab where 1=0
;
Private TEMPORARY created.
Afterwards you can use these tables in SQL and PL/SQL blocks:
declare
r mytab%rowtype;
begin
insert into ora$ptt_mytab values (2, 'bbb');
select id + 1, name||'x' into r from ora$ptt_mytab where rownum = 1;
insert into ora$ptt_mytab values r;
end;
/
select * from mytab
union all
select * from ora$ptt_mytab;
ID NAME
---------- --------------------------------
1 aaa
2 bbb
3 bbbx
Some important restrictions on private temporary tables:
The name must always be prefixed with whatever is defined with the parameter PRIVATE_TEMP_TABLE_PREFIX. The default is ORA$PTT_.
You cannot reference PTT in the static statements of the named PL/SQL blocks, e.g. packages, functions, or triggers.
The %ROWTYPE attribute is not applicable to that table type.
You cannot define column with default values.

Error with distinct, oracle and CLOB in Grails

I have an application written in grails 2.2.5 that needs to connect with MySQL, Oracle and SQL Server depending on my customers. We have more than 1000 queries that uses distinct returning instances of classes.
Example:
import br.com.aaf.auditoria.*
def query="select distinct tipo from Atividade c join c.tipoAtividade tipo order by tipo.nome"
def ret=Atividade.executeQuery(query)
So far so good, but now I need to include some CLOBs columns in oracle to expand some fields from VarChar 4000. When I do that these queries stop working because of the problem that Oracle does not compare CLOB columns.
Error:
ORA-00932: inconsistent datatypes: expected - got CLOB
I understand that Grails/Hibernate uses all properties of the domain class to make the sql to send to the database and return as an instance of that class.
The case is that I only need to compare or group the id of the domain class to make a distinct, but I need the result to be an instance of the class and not the id, so I don´t need to change all the queries.
Any of you know a way to change the behaviour of a distinct in HQL even if I need to customize a dialect to capture what Hibernate is doing in transforming HQL in SQL?
What I´m thinking is capture the SQL, change it to return and group only the id of the instance and execute a "get" in the Domain class before return this to "executeQuery".
The solution only fits to oracle db. You have to grant some privileges to your schema. "Create types" and " execute on DBMS_CRYPTO"
create table clob_test (id number, lob clob);
insert all
into clob_test values(1,'AAAAAAAA')
into clob_test values (2,'AAAAAAAA')
into clob_test values(3,'BBBBBBBB')
into clob_test values(4,'BBBBBBBB')
select * from dual;
commit;
CREATE OR REPLACE
type wrap_lob as object(
lob clob,
MAP MEMBER FUNCTION get_hash RETURN RAW
)
;
/
CREATE OR REPLACE
TYPE BODY wrap_lob is
MAP MEMBER FUNCTION get_hash RETURN RAW is
begin
return DBMS_CRYPTO.HASH(lob,1);
end;
end;
/
select tab.dist_lob.lob from (select distinct wrap_lob(lob) dist_lob from clob_test) tab;

Oracle Datatype Modifier

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

Resources