Oracle - Cannot update table record using bind variable - oracle

This query returns 1 row:
SELECT col1, col2 FROM table1 WHERE col1 = :column1;
But this updates 0 rows:
UPDATE table1 SET col2 = :column2 WHERE col1 = :column1;
COMMIT;
I added this constraint to set col1 as primary key, but it didn't fix it.
ALTER TABLE table1 ADD CONSTRAINT col1_pk PRIMARY KEY (col1);
I am trying this from SQL Developer, any idea why it does not update the row?
EDIT:
col1 is VARCHAR2(32 BYTE) NOT NULL
col2 is CLOB NOT NULL
EDIT 2: Test Case, set :var1 to 0011223344556677 in the select and update sentences.
CREATE TABLE MY_TABLE
( COL1 VARCHAR2(32 BYTE) NOT NULL ENABLE,
COL2 CLOB,
CONSTRAINT "MY_TABLE_PK" PRIMARY KEY ("COL1")
)
INSERT INTO MY_TABLE (COL1, COL2) VALUES ('0011223344556677', '1434407992143440799214344079921434407992');
SELECT * FROM MY_TABLE WHERE COL1 = :var1;
UPDATE MY_TABLE SET COL2 = 'test' WHERE COL1 = :var1;
COMMIT;

TL;DR - Make sure the value being stored in the bind variable is parsed as a character string not a number.
I've run this in SQL Developer (Version 4.0.3.16):
CREATE TABLE MY_TABLE
( COL1 VARCHAR2(32 BYTE) NOT NULL ENABLE,
COL2 CLOB,
CONSTRAINT "MY_TABLE_PK" PRIMARY KEY ("COL1")
);
/
INSERT INTO MY_TABLE (COL1, COL2) VALUES ('0011223344556677', '1434407992143440799214344079921434407992');
/
VARIABLE var1 VARCHAR2(32);
/
BEGIN
:var1 := '0011223344556677';
END;
/
SELECT * FROM MY_TABLE WHERE COL1 = :var1;
/
UPDATE MY_TABLE SET COL2 = 'test' WHERE COL1 = :var1;
/
COMMIT;
/
SELECT * FROM MY_TABLE;
/
And it runs fine:
table MY_TABLE created.
1 rows inserted.
anonymous block completed
COL1 COL2
-------------------------------- --------------------------------------------------------------------------------
0011223344556677 1434407992143440799214344079921434407992
1 rows updated.
committed.
COL1 COL2
-------------------------------- --------------------------------------------------------------------------------
0011223344556677 test
If you change the variable assignment to (remove quotes):
BEGIN
:var1 := 0011223344556677;
END;
Then the value is parsed as a number and the leading zeros are ignored and the output is:
table MY_TABLE created.
1 rows inserted.
anonymous block completed
no rows selected
0 rows updated.
committed.
COL1 COL2
-------------------------------- --------------------------------------------------------------------------------
0011223344556677 1434407992143440799214344079921434407992

Related

Retain Original Value/State of "GENERATED AS IDENTITY" Column

I have a table that has a unique increment column created using command "GENERATED AS IDENTITY" and the data type is NUMBER(20,0).
Now, the dev want to change the data type to NUMBER(19,0). Since there are hundreds of tables and millions of data, creating a new set of tables just for one column change is not ideal.
So, I manage to create a workflow as follows:
ALTER TABLE my_schema.my_table
ADD REC_ID_TEMP NUMBER(19,0);
UPDATE my_schema.my_table
SET REC_ID_TEMP = REC_ID;
ALTER TABLE my_schema.my_table
DROP COLUMN REC_ID;
ALTER TABLE my_schema.my_table
ADD REC_ID NUMBER(19,0) GENERATED BY DEFAULT AS IDENTITY;
UPDATE my_schema.my_table
SET REC_ID = REC_ID_TEMP;
ALTER TABLE my_schema.my_table
MODIFY REC_ID NUMBER(19,0) GENERATED AS IDENTITY MINVALUE 1 MAXVALUE 9999999999999999999999999999 INCREMENT BY 1 START WITH 1515 //Must start with the last REC_ID value +1// CACHE 20 NOORDER NOCYCLE NOKEEP NOSCALE NOT NULL ENABLE;
This is the best way possible that I can think of with this peanut size brain of mine.
This way, I can make sure that the REC_ID is the exact same as before I change the data type.
But, the problem is, if you read my the last line of my script, I have to check the last REC_ID for the hundreds of table before I can run the last script. I need help to figure out a way that I can retain the original state.
You can use the START WITH LIMIT VALUE clause instead of specifying a number. From the documentation:
START WITH LIMIT VALUE, which is specific to identity_options, can only be used with ALTER TABLE MODIFY. If you specify START WITH LIMIT VALUE, then Oracle Database locks the table and finds the maximum identity column value in the table (for increasing sequences) or the minimum identity column value (for decreasing sequences) and assigns the value as the sequence generator's high water mark. The next value returned by the sequence generator will be the high water mark + INCREMENT BY integer for increasing sequences, or the high water mark - INCREMENT BY integer for decreasing sequences.
A little automation can make this as easily as calling a procedure per table, eg
SQL>
SQL> create table t1 as select * from scott.emp;
Table created.
SQL> create table t2 as select * from dba_objects;
Table created.
SQL> create table t3 as select * from dba_procedures;
Table created.
SQL>
SQL> alter table t1 add rec_id number(20) generated by default as identity start with 100000;
Table altered.
SQL> alter table t2 add rec_id number(20) generated by default as identity start with 100000;
Table altered.
SQL> alter table t3 add rec_id number(20) generated by default as identity start with 100000;
Table altered.
SQL>
SQL> update t1 set rec_id = rownum;
14 rows updated.
SQL> update t2 set rec_id = rownum;
81264 rows updated.
SQL> update t3 set rec_id = rownum;
37644 rows updated.
SQL>
SQL> alter table t1 add primary key ( rec_id);
Table altered.
SQL> alter table t2 add primary key ( rec_id);
Table altered.
SQL> alter table t3 add primary key ( rec_id);
Table altered.
SQL>
SQL> alter table t1 modify rec_id number(20) generated always as identity start with 100000;
Table altered.
SQL> alter table t2 modify rec_id number(20) generated always as identity start with 100000;
Table altered.
SQL> alter table t3 modify rec_id number(20) generated always as identity start with 100000;
Table altered.
SQL>
SQL> create or replace
2 procedure fix_up_my_recid(p_table varchar2) is
3 begin
4 execute immediate 'alter table '||p_table||' add tmp$rec_id number(19,0)';
5
6 execute immediate 'update '||p_table||' set tmp$rec_id = rec_id';
7
8 execute immediate 'alter table '||p_table||' set unused column rec_id';
9
10 execute immediate 'alter table '||p_table||' add rec_id number(19,0) generated by default as identity';
11
12 execute immediate 'update '||p_table||' set rec_id = tmp$rec_id';
13
14 execute immediate 'alter table '||p_table||' set unused column tmp$rec_id';
15
16 execute immediate 'alter table '||p_table||' move online';
17
18 execute immediate 'alter table '||p_table||' modify rec_id number(19,0) generated always as identity start with limit value';
19
20 end;
21 /
Procedure created.
SQL> sho err
No errors.
SQL>
SQL>
SQL>
SQL> desc t1
Name Null? Type
----------------------------------------------------------------------- -------- ------------------------------------------------
EMPNO NOT NULL NUMBER(4)
ENAME VARCHAR2(10)
JOB VARCHAR2(9)
MGR NUMBER(4)
HIREDATE DATE
SAL NUMBER(7,2)
COMM NUMBER(7,2)
DEPTNO NUMBER(2)
REC_ID NOT NULL NUMBER(20)
SQL> exec fix_up_my_recid('T1')
PL/SQL procedure successfully completed.
SQL> exec fix_up_my_recid('T2')
PL/SQL procedure successfully completed.
SQL> exec fix_up_my_recid('T3')
PL/SQL procedure successfully completed.
SQL> desc t1
Name Null? Type
----------------------------------------------------------------------- -------- ------------------------------------------------
EMPNO NOT NULL NUMBER(4)
ENAME VARCHAR2(10)
JOB VARCHAR2(9)
MGR NUMBER(4)
HIREDATE DATE
SAL NUMBER(7,2)
COMM NUMBER(7,2)
DEPTNO NUMBER(2)
REC_ID NOT NULL NUMBER(19)
SQL>
SQL>
SQL>
SQL>
SQL> desc t2
Name Null? Type
----------------------------------------------------------------------- -------- ------------------------------------------------
OWNER VARCHAR2(128)
OBJECT_NAME VARCHAR2(128)
SUBOBJECT_NAME VARCHAR2(128)
OBJECT_ID NUMBER
DATA_OBJECT_ID NUMBER
OBJECT_TYPE VARCHAR2(23)
CREATED DATE
LAST_DDL_TIME DATE
TIMESTAMP VARCHAR2(19)
STATUS VARCHAR2(7)
TEMPORARY VARCHAR2(1)
GENERATED VARCHAR2(1)
SECONDARY VARCHAR2(1)
NAMESPACE NUMBER
EDITION_NAME VARCHAR2(128)
SHARING VARCHAR2(18)
EDITIONABLE VARCHAR2(1)
ORACLE_MAINTAINED VARCHAR2(1)
APPLICATION VARCHAR2(1)
DEFAULT_COLLATION VARCHAR2(100)
DUPLICATED VARCHAR2(1)
SHARDED VARCHAR2(1)
CREATED_APPID NUMBER
CREATED_VSNID NUMBER
MODIFIED_APPID NUMBER
MODIFIED_VSNID NUMBER
REC_ID NOT NULL NUMBER(19)
SQL> desc t3
Name Null? Type
----------------------------------------------------------------------- -------- ------------------------------------------------
OWNER VARCHAR2(128)
OBJECT_NAME VARCHAR2(128)
PROCEDURE_NAME VARCHAR2(128)
OBJECT_ID NUMBER
SUBPROGRAM_ID NUMBER
OVERLOAD VARCHAR2(40)
OBJECT_TYPE VARCHAR2(13)
AGGREGATE VARCHAR2(3)
PIPELINED VARCHAR2(3)
IMPLTYPEOWNER VARCHAR2(128)
IMPLTYPENAME VARCHAR2(128)
PARALLEL VARCHAR2(3)
INTERFACE VARCHAR2(3)
DETERMINISTIC VARCHAR2(3)
AUTHID VARCHAR2(12)
RESULT_CACHE VARCHAR2(3)
ORIGIN_CON_ID NUMBER
POLYMORPHIC VARCHAR2(5)
REC_ID NOT NULL NUMBER(19)
SQL>
Note that I've not used DROP COLUMN because that's incredibly expensive. Generally better to just go with SET UNUSED, and I've thrown in an MOVE ONLINE at the end because all those updates could make a mess of your table row structure.

How to add current hour and minutes to inserted date

I'm inserting data into table with date value, how to add to inserting date value current system time in format HH:MM
My insert
INSERT INTO tab1 VALUES to_date('11-OCT-2021');
I wanna insert '11-OCT-2021 22:08' where 22:08 current system time. How to do it
Thx
You can use:
INSERT INTO tab1 (column_name)
VALUES ( DATE '2021-10-11' + (SYSDATE - TRUNC(SYSDATE)) );
or
INSERT INTO tab1 (column_name)
VALUES (
TO_DATE(
'11-OCT-2021' || TO_CHAR(SYSDATE, 'HH24:MI:SS'),
'DD-MON-YYYYHH24:MI:SS',
'NLS_DATE_LANGUAGE=American'
)
);
db<>fiddle here
Here's one option:
SQL> create table tab1 (datum date);
Table created.
SQL> insert into tab1 values (date '2021-10-11');
1 row created.
SQL> select * from tab1;
DATUM
-------------------
11.10.2021 00:00:00
SQL> update tab1 set datum = to_date(to_char(datum, 'dd.mm.yyyy') || to_char(sysdate, 'hh24:mi'), 'dd.mm.yyyy hh24:mi');
1 row updated.
SQL> select * from tab1;
DATUM
-------------------
11.10.2021 21:47:00
SQL>
On the other hand, why wouldn't you insert the "whole" value immediately?
SQL> rollback;
Rollback complete.
SQL> insert into tab1 values (sysdate);
1 row created.
SQL> select * from tab1;
DATUM
-------------------
12.10.2021 21:48:21
SQL>

Oracle-Using optional prameters in function?

I want to add at most three optional parameters to my Oracle function named TEST.
Returned value should be 2.
How to modify my function to make these queries work in a simplest way?
SELECT TEST('eq1','md1') TEST0 FROM DUAL; -- Shows 2 correctly
--How to make these queries also work?
SELECT TEST('eq1','md1','c1') TEST1 FROM DUAL;
SELECT TEST('eq1','md1','c1','d1') TEST2 FROM DUAL;
SELECT TEST('eq1','md1','c1','d1','e1') TEST3 FROM DUAL;
Table and Function are as below.
Table as below
CREATE TABLE T5 (
COL1 VARCHAR2(10),
COL2 VARCHAR2(10),
COL3 VARCHAR2(10),
COL4 VARCHAR2(10),
COL5 VARCHAR2(10),
VAL VARCHAR2(10)
);
INSERT INTO T5 VALUES ('eq1','md1','c1','d1','e1','2');
INSERT INTO T5 VALUES ('eq2','md2','c2','d2','e2','5');
INSERT INTO T5 VALUES ('eq3','md3','c3','d3','e3','3');
My funtion is,
CREATE OR REPLACE FUNCTION TEST
(p1 IN VARCHAR2,
p2 IN VARCHAR2
--How to add optional parameter p3?
--How to add optional parameter p4?
--How to add optional parameter p5?
)
RETURN NUMBER AS V_VALUE VARCHAR2(10);
BEGIN
SELECT(
SELECT VAL FROM T5
WHERE COL1 = p1
AND COL2 = p2
--How to add constraint COL3=p3?
--How to add constraint COL4=p4?
--How to add constraint COL5=p5?
)
INTO V_VALUE
FROM DUAL;
RETURN V_VALUE;
END;
/
You need to define the default value and that's it.
Your function code should look like as follows:
CREATE OR REPLACE FUNCTION TEST (
P1 IN VARCHAR2,
P2 IN VARCHAR2,
P3 IN VARCHAR2 DEFAULT NULL,
P4 IN VARCHAR2 DEFAULT NULL,
P5 IN VARCHAR2 DEFAULT NULL
) RETURN NUMBER AS
V_VALUE VARCHAR2(10);
BEGIN
SELECT
(
SELECT
VAL
FROM
T5
WHERE
COL1 = P1
AND COL2 = P2
AND COL3 = COALESCE(P3, COL3)
AND COL4 = COALESCE(P4, COL4)
AND COL5 = COALESCE(P5, COL5)
)
INTO V_VALUE
FROM
DUAL;
RETURN V_VALUE;
END TEST;
/
Now, all the queries will run:
SELECT TEST('eq1','md1') TEST0 FROM DUAL;
SELECT TEST('eq1','md1','c1') TEST1 FROM DUAL;
SELECT TEST('eq1','md1','c1','d1') TEST2 FROM DUAL;
SELECT TEST('eq1','md1','c1','d1','e1') TEST3 FROM DUAL;
But, Please make sure that order of the passed parameter in the function is not broken.
You can not pass the P5 without passing P4 parameter value except the parameter names are used while calling the function.
Cheers!!

How get another column value from the same row if we update any column of that row in oracle trigger?

TABLE NAME : TEST
COLUMNS : ID,NAME
I am using ORACLE database.
I have written one trigger after update.
If I update name column value then I want to get ID of that updated name.
Please give me any suggestion.
Thanks....
you can use :OLD check the below example
CREATE OR REPLACE TRIGGER EX_TRIGGER
AFTER UPDATE ON TAB1
BEGIN
SET VAR = :OLD.ID
END;
Oracle Setup:
CREATE TABLE table_name ( id INT, name VARCHAR2(20) );
CREATE TABLE table_name_log( prev_id INT, id INT, update_date DATE );
CREATE TRIGGER log_id
AFTER UPDATE
ON table_name
FOR EACH ROW
BEGIN
IF :old.name <> :new.name THEN
INSERT INTO table_name_log (
prev_id, id, update_date
) VALUES (
:old.id, :new.id, SYSDATE
);
END IF;
END;
/
INSERT INTO table_name
SELECT 1, 'Alf' FROM DUAL UNION ALL
SELECT 2, 'Ben' FROM DUAL UNION ALL
SELECT 3, 'Carl' FROM DUAL;
Query:
UPDATE table_name SET name = 'Ann' WHERE id = 1;
SELECT * FROM table_name_log;
Output
PREV_ID ID UPDATE_DATE
------- -- -------------------
1 1 2016-06-02 09:56:23

If statement inside create table query -Oracle-

I would like to create table which asks user input first. Then based on the input, it select which columns are added.
for example, if the response is 'N', then table is created including columns col1, col2, col3.
If the response is 'Y', table is created including columns col1, col2, col3, col4, col5.
Is this possible?
If yes, please provide me simple and primitive query so that I can apply it to my case.
Thanks,
Using SQL*Plus it's simple:
ACCEPT table_option -
PROMPT 'Create more columns? '
SET TERM OFF
COLUMN extra_columns NEW_VALUE extra_columns
SELECT
CASE '&table_option'
WHEN 'Y' THEN ', C4 NUMBER, C5 VARCHAR2(255), C6 DATE'
END extra_columns FROM DUAL;
CREATE TABLE tmp (
C1 NUMBER,
C2 VARCHAR2(255),
C3 DATE &extra_columns
);
SET TERM ON
You can store the script as a file and invoke it from SQL*Plus using #filename.
CREATE OR REPLACE FUNCTION tmp_custom_DDL( p_input VARCHAR2 IN, p_resp CHAR IN OUT) RETURN CHAR
AS
v_str VARCHAR2(4000);
IF p_resp = 'Y' THEN
v_str := 'col1 varchar2(10), col2 varchar2(10), col3 varchar2(10)';
ELSE v_str := 'col1 varchar2(10), col2 varchar2(10), col3 varchar2(10), col4 varchar2(10), col4 varchar2(10) ' ;
EXECUTE IMMEDIATE v_comm_1 || v_str || v_comm2;
--v_comm_1 is the first half of create table command till the specified cols
--v_comm_2 is the rest of the create table command
RETURN p_resp;
END;
this is only a quick draft, fix the few lexical bug and the missing definitions :) (this is the first step)

Resources