RETURNING ... INTO ... clause throwing an error with Oracle and ColdFusion - oracle

Here is the query I am running.
<cfquery name="myquery" datasource="mydatasource">
INSERT INTO
myTable (myfield)
VALUES
(<cfqueryparam cfsqltype="cf_sql_varchar" value="#ARGUMENTS.myValue#" />)
RETURNING
id INTO <cfqueryparam cfsqltype="cf_sql_varchar" value="#LOCAL.myid#" />
</cfquery>
The error I am getting is ORA-00439: feature not enabled: RETURNING clause from this client type
I've run across a feature in cfquery called GENERATEDKEY, I think this can be used to mitigate any database server settings, which I don't have access to. However I can't seem to get GENERATEDKEY to return my primary key instead I get something like AAFO9cAAEAAB8vYAAH, which is the Oracle rowid...

According to the docs you linked, there isn't a generatedkey member of the query result. there's a generated_key for mySQL, but for oracle it's ROWID, although the docs say this "Oracle only. The ID of an inserted row. This is not the primary key of the row, although you can retrieve rows based on this ID." I'm not sure how to map from one to the other though. If you leave out the RETURNING part of the statement, do you get anything for ROWID?

I'm not familiar with coldfusion, and there may be some simple solution.
But if not, a workaround could be to create a stored procedure that is using the returning clause and retuns it.
This way you can call the stored procedure instead.
create or replace function insert_my_table(my_value varchar2) return number is
i number;
begin
insert into my_table (value_col_name) values (my_value) returning id into i;
return i;
end insert_my_table;

Related

Oracle PL/SQL function to return rows or rowids

I want to write a function that returns table records that I can show in my app's grid. My app shall be oblivious to the table's structure. I want it to be still working when someone adds columns to the table or removes them.
In my app it shall look something like this:
myQuery = "select * from table(myfunction)";
...
The function should hence be something like:
CREATE OR REPLACE FUNCTION myfunction RETURN TABLE OF mytable%ROWTYPE AS ...
But RETURN TABLE OF mytable%ROWTYPE is not allowed. One has to create an SQL type.
But CREATE TYPE table_of_mytable_rows IS TABLE OF mytable%ROWTYPE is not allowed either.
So, is there a way to achieve exactly what I want?
As an alternative I thought of working with IDs. This would make my app's code look something like this:
myQuery = "select * from mytable where rowid in (select * from table(myfunction))";
...
And the function would then be
CREATE OR REPLACE FUNCTION myfunction RETURN TABLE OF UROWID AS ...
But again, RETURN TABLE OF UROWID is not allowed. Neither is creating the SQL table type with CREATE TYPE table_of_rowids IS TABLE OF UROWID.
I know I could create an object type resembling the table's primary key columns and then create a table type on this object. Then my app would have to know the table's primary key in order to
myQuery = "select * from mytable where (key1, key2) in (select key1, key2 from table(myfunction))";
...
I would much prefer my function to return table rows or rowids. Is this possible?
I'm currently still working on Oracle 11.2, but I would also be interested in solutions for newer versions.

Mybatis, insert in Oracle with sequence id

I have tried with this:
<insert id="insertPersonalizacionUsuario" useGeneratedKeys="true" keyProperty="param1.id" keyColumn="id">
INSERT INTO dsk_prop_personali (idpersonalizacion, idusuario, valor, centro)
VALUES (#{param1.idPersonalizacion}, #{param1.idUsuario}, #{param1.valor}, #{param2})
And with this:
<insert id="insertPersonalizacionUsuario" useGeneratedKeys="true" keyProperty="param1.id" keyColumn="id">
<selectKey keyProperty="id" resultType="int">
SELECT id.nextVal from dual
</selectKey>
INSERT INTO dsk_prop_personali (id, idpersonalizacion, idusuario, valor, centro)
VALUES (#{id}, #{param1.idPersonalizacion}, #{param1.idUsuario}, #{param1.valor}, #{param2})
But not working. Thanks
You must add the order attribute with BEFORE value to <selectKey> element. In your case you are using an Oracle database which until version 12c (review your case) it doesn't have auto-generated column types and works with a sequence is not related with your column by the rdbms.
If you take a look the documentation reference there is a section which explains your case:
MyBatis has another way to deal with key generation for databases that
don't support auto-generated column types, or perhaps don't yet
support the JDBC driver support for auto-generated keys.
Here's a simple (silly) example that would generate a random ID
(something you'd likely never do, but this demonstrates the
flexibility and how MyBatis really doesn't mind):
<insert id="insertAuthor">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
</selectKey>
insert into Author
(id, username, password, email,bio, favourite_section)
values
(#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR})
In the example above, the selectKey statement would be run first, the
Author id property would be set, and then the insert statement would
be called. This gives you a similar behavior to an auto-generated key
in your database without complicating your Java code.
So, to be sure the selectKey statement will run first, you would need to use the Order attribute with BEFORE value, the attribute is explained very good after this example in the reference documentation:
order This can be set to BEFORE or AFTER. If set to BEFORE, then it
will select the key first, set the keyProperty and then execute the
insert statement. If set to AFTER, it runs the insert statement and
then the selectKey statement – which is common with databases like
Oracle that may have embedded sequence calls inside of insert
statements.
Therefore, you must match your keyProperty value with the insert param as you have done (keyProperty="id" will be the Param in insert statement:#{id}), and specify the resultType as int so it is a numeric sequence.
Otherwise, you must do your select using the sequence id name, in your case be sure your sequence is called id (because you are using id.NEXTVAL):
SELECT YOUR_SEQ.NEXTVAL FROM DUAL
<insert id="insertAuthor">
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select MYSEQUENCE.nextval from dual
</selectKey>
insert into Author
(id, username, password, email,bio, favourite_section)
values
(#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR}

How alter column type with using toUInt32OrZero function in clickhouse?

I have String column in clickhouse table.
I try alter table with modify type to UInt32:
ALTER TABLE main.abonents
MODIFY COLUMN
device_type UInt32 DEFAULT 0
but have error:
Received exception from server:
Code: 6. DB::Exception: Received from 5.200.55.122:9000. DB::Exception: Cannot parse string 'mo' as UInt32: syntax error at begin of string. Note: there are toUInt32OrZero function, which returns zero instead of throwing exception..
It's clear, clickhouse use toUint32 function on string like 'mobile' and throw exception. And its advice to use function toUInt32OrZero to convert type.
How can i use toUInt32OrZero function with ALTER TABLE??
There's no such way (as far as I know).
You can achieve it with a second table. Let's create one:
CREATE TABLE main.abonents_new AS main.abonents;
Then we have to alter column in that new table.
This table has no data yet, so it won't raise exceptions:
ALTER TABLE main.abonents_new MODIFY COLUMN device_type UInt32 DEFAULT 0;
Then, make sure no new data is written to main.abonents. We'd like to keep everything in place when we'll transfer the data to the new table.
Insert the data using INSERT INTO SELECT query. Make sure to list all the fields with the same order; wrap device_type to the converter function (toUInt32OrZero) :
INSERT INTO main.abonents_new SELECT field1, field2, ..., toUInt32OrZero(device_type) AS device_type, ..., fieldN FROM main.abonents;
Then, make sure that everything's alright (that rows count is the same, device_type was converted as intended, etc), then rename the tables:
RENAME TABLE main.abonents TO main.abonents_old;
RENAME TABLE main.abonents_new TO main.abonents;
(or, you may DROP the older table instead; although I'd keep the old data to be able to restore if things'd go south)

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 merge constants into single table

In Oracle, given a simple data table:
create table data (
id VARCHAR2(255),
key VARCHAR2(255),
value VARCHAR2(511));
suppose I want to "insert or update" a value. I have something like:
merge into data using dual on
(id='someid' and key='testKey')
when matched then
update set value = 'someValue'
when not matched then
insert (id, key, value) values ('someid', 'testKey', 'someValue');
Is there a better way than this? This command seems to have the following drawbacks:
Every literal needs to be typed twice (or added twice via parameter setting)
The "using dual" syntax seems hacky
If this is the best way, is there any way around having to set each parameter twice in JDBC?
I don't consider using dual to be a hack. To get rid of binding/typing twice, I would do something like:
merge into data
using (
select
'someid' id,
'testKey' key,
'someValue' value
from
dual
) val on (
data.id=val.id
and data.key=val.key
)
when matched then
update set data.value = val.value
when not matched then
insert (id, key, value) values (val.id, val.key, val.value);
I would hide the MERGE inside a PL/SQL API and then call that via JDBC:
data_pkg.merge_data ('someid', 'testKey', 'someValue');
As an alternative to MERGE, the API could do:
begin
insert into data (...) values (...);
exception
when dup_val_on_index then
update data
set ...
where ...;
end;
I prefer to try the update before the insert to save having to check for an exception.
update data set ...=... where ...=...;
if sql%notfound then
insert into data (...) values (...);
end if;
Even now we have the merge statement, I still tend to do single-row updates this way - just seems more a more natural syntax. Of course, merge really comes into its own when dealing with larger data sets.

Resources