How to generate alphanumeric id in Oracle - oracle

In my vb application I want an autogenerated id of alphanumeric characters, like prd100. How can I increment it using Oracle as backend?

Any particular reason it needs to be alphanumeric? If it can just be a number, you can use an Oracle sequence.
But if you want just a random string, you could use the dbms_random function.
select dbms_random.string('U', 20) str from dual;
So you could probably combine these 2 ideas (in the code below, the sequence is called oid_seq):
SELECT dbms_random.string('U', 20) || '_' || to_char(oid_seq.nextval) FROM dual

There are two parts to your question. The first is how to create an alphanumeric key. The second is how to get the generated value.
So the first step is to determine the source of the alpha and the numeric components. In the following example I use the USER function and an Oracle sequence, but you will have your own rules. I put the code to assemble the key in a trigger which is called whenever a row is inserted.
SQL> create table t1 (pk_col varchar2(10) not null, create_date date)
2 /
Table created.
SQL> create or replace trigger t1_bir before insert on t1 for each row
2 declare
3 n pls_integer;
4 begin
5 select my_seq.nextval
6 into n
7 from dual;
8 :new.pk_col := user||trim(to_char(n));
9 end;
10 /
Trigger created.
SQL>
The second step requires using the RETURNING INTO clause to retrieve the generated key. I am using SQL*PLus for this example. I confess to having no idea how to wire this syntax into VB. Sorry.
SQL> var new_pk varchar2(10)
SQL> insert into t1 (create_date)
2 values (sysdate)
3 returning pk_col into :new_pk
4 /
1 row created.
SQL> print new_pk
NEW_PK
--------------------------------
APC61
SQL>
Finally, a word of warning.
Alphanumeric keys are a suspicious construct. They reek of "smart keys" which are, in fact, dumb. A smart key is a value which contains multiple parts. At somepoint you will find yourself wanting to retrieving all rows where the key starts with 'PRD', which means using SUBSTR() or LIKE. Even worse someday the definition of the smart key will change and you will have to cascade a complicated update to your table and its referencing foreign keys. A better ides is to use a surrogate key (number) and have the alphanumeric "key" defined as separate columns with a UNIQUE composite constraint to enforce the business rule.
SQL> create table t1 (id number not null
2 , alpha_bit varchar2(3) not null
3 , numeric_bit number not null
4 , create_date date
5 , constraint t1_pk primary key (id)
6 , constraint t1_uk unique (alpha_bit, numeric_bit)
7 )
8 /
Table created.
SQL>

Related

adding a sequence to an existing table

i created a table but i forgot to add a sequence to one of the PK, its a sequence on a form page, i just cant find anything about it, is it possible or do i have to do the form all over again.
i tried to replace the PK but it doesnt give me the option to add the sequence when creating a new one.
i searched everywhere and asked the support in chat (didn't really help since its not their job).
all i could find was this and this.
I'd suggest you to skip Apex in this matter and do the following: presume this is your table:
SQL> create table test
2 (id number constraint pk_test primary key,
3 name varchar2(20)
4 );
Table created.
This is the sequence:
SQL> create sequence myseq;
Sequence created.
As you forgot to specify PK source while creating Apex Form page, never mind - let the database handle it. How? Create a BEFORE INSERT trigger:
SQL> create or replace trigger trg_bi_test
2 before insert on test
3 for each row
4 when (new.id is null)
5 begin
6 :new.id := myseq.nextval;
7 end trg_bi_test;
8 /
Trigger created.
Let's test it: I'm inserting only the NAME (which is what your Apex Form will be doing):
SQL> insert into test (name) values ('Littlefoot');
1 row created.
What is table's contents?
SQL> select * from test;
ID NAME
---------- --------------------
1 Littlefoot
SQL>
See? Trigger automatically inserted ID (primary key) column value.
If it were an Interactive Grid (which lets you insert several records at a time):
SQL> insert into test (name)
2 select 'Bigfoot' from dual union all
3 select 'FAD' from dual;
2 rows created.
SQL> select * from test;
ID NAME
---------- --------------------
1 Littlefoot
2 Bigfoot
3 FAD
SQL>
Works just fine.
And what's another benefit: you don't have to modify Apex application at all.

Generate Alphanumber field and Increment

i have a transaction form in oracle apex within that form has a page item called transactioncode.
what i will like, is to generate a alphanumeric code on page load for eg. AA110 which will increment everytime they create a new transaction.
table: transactioncode, transactiondate, productcode, productname.
I have not tried any sql or pl/sql or trigger
You can use a SEQUENCE and assign the sequence.nextval to the field transactioncode or define the field as IDENTITY if you are using 12.1 onwards. If your version is prior to 12.1, then you need a trigger to associate the sequence.
Example below will show you column c1 as identity, column c2 using sequence
SQL> create sequence my_example start with 1000 increment by 1 maxvalue 9999999999999999 ;
Sequence created.
SQL> create table my_test ( c1 NUMBER GENERATED BY DEFAULT AS IDENTITY , c2 number default my_example.nextval , c3 varchar2(1) ) ;
Table created.
SQL> insert into my_test ( c3 ) values ( 1 ) ;
1 row created.
SQL> commit;
Commit complete.
SQL> select * from my_test ;
C1 C2 C
---------- ---------- -
1 1000 1
The following restrictions applied to IDENTITY fields
One identity column per table.
Identity columns must be numeric types, and can't be user-defined
data types.
Identity columns can't have a default clause.
Identity columns are implicitly have NOT NULL and NOT DEFERRABLE constraints.
They can't be explicitly alter to anything else. From the doc, "If an
identity column is encrypted, then the encryption algorithm may be
inferred. Oracle recommends that you use a strong encryption
algorithm on identity columns."
The CREATE TABLE ... AS SELECT will not inherit the identity property on a column. This is true for several structural definitions. If you care about structure, you
should always CREATE TABLE, then use INSERT INTO ... SELECT to
populate it.

How to increment the value of the unique constraint column value in ORACLE

How to increment the value of the unique constraint column value in ORACLE, in the select statement.
For example, in a table 'BILLING_TABLE' - column BLNG_Sk is the unique key (Autoincremented).
So while inserting a new record into the BILLING_TABLE, for the column BLNG_SK we need to give the value (Which is the increment by 1 from the present max value.)
For example, if BLNG_SK max value is 12321.
new record should be 12322.
how to achieve this in Oracle?
Oracle has a SEQUENCE object which provides the functionality you require.
You create one using the CREATE SEQUENCE SQL statement.
The Oracle documentation provides all the required information and the documentation is available via Oracle's Web site.
Assuming you are on Oracle 12.1 or later, define it as an identity column and do not pass any value when inserting:
create table testtable
( test_id number generated always as identity
constraint testtable_pk primary key
, othercol varchar2(10) );
insert into testtable (othercol) values ('Demo');
select * from testtable;
TEST_ID OTHERCOL
---------- ----------
1 Demo
insert into testtable (othercol) values ('Demo #2');
select * from testtable;
TEST_ID OTHERCOL
---------- ----------
1 Demo
2 Demo #2
Try creating a sequence and a trigger. This is the case when you provide the value manually.
CREATE SEQUENCE dept_seq START WITH 12322;
Trigger definition:
CREATE OR REPLACE TRIGGER dept_bir
BEFORE INSERT ON BILLING_TABLE
FOR EACH ROW
BEGIN
SELECT dept_seq.NEXTVAL
INTO :new.id
FROM dual;
END;
/

Can't use oracle associative array in object (for custom aggregate function)

Background : My purpose is to write an aggregate function in oracle to make a string contains number of occurrence of each element. For example "Jake:2-Tom:3-Jim:5" should means 2 times occurrence for Jake, 3 times for Tom and 5 times for Jim. So for writing a custom aggregate function I should write an object implements ODCIAggregate routines. And also a Map like data structure for counting each element occurrences. Only Map like data structure in oracle is associative array.
Problem : Unfortunately I can't know any approach to use associative arrays in object. I tried these approaches:
1 – Create a generic type for associative array and use it in object. Oracle doesn't let creating generic associative array types.
CREATE TYPE STR_MAP IS TABLE OF NUMBER INDEX BY VARCHAR2(100);
This get following error :
PLS-00355: use of pl/sql table not allowed in this context
2 – Create map like type in a package and use it in object. Oracle lets creating an associative array in a package, but doesn’t let using an 'in package type' in object. I checked all issues about grant execute on package or make a synonym for 'in package type'. But there is no way for use 'in package type' in object declaration.
P.S. 1 :
Of course we can do it for one column by nested group by. But I prefer to do it for many columns with only agg-func. It is very useful agg-func and I wonder why nobody wrote something like this before. For many columns we have limited number of distinct values, and with such an agg-func we can simply summarize all of them. For example if we had such a agg-func named ocur_count(), we can simply analyze an collection of transactions like this :
select ocur_count(trans_type), ocur_count(trans_state), ocur_count(response_code), ocur_count(something_status) from transaction;
You can use listagg and a simple group by with a count to get what you need (Note the listagg output is limited in size to 4k chars). Here I'm counting occurrences of first names, using ',' as the separator between names and ':' as the separator for count:
SQL> create table person_test
(
person_id number,
first_name varchar2(50),
last_name varchar2(50)
)
Table created.
SQL> insert into person_test values (1, 'Joe', 'Blow')
1 row created.
SQL> insert into person_test values (2, 'Joe', 'Smith')
1 row created.
SQL> insert into person_test values (3, 'Joe', 'Jones')
1 row created.
SQL> insert into person_test values (4, 'Frank', 'Rizzo')
1 row created.
SQL> insert into person_test values (4, 'Frank', 'Jones')
1 row created.
SQL> insert into person_test values (5, 'Betty', 'Boop')
1 row created.
SQL> commit
Commit complete.
SQL> -- get list of first names and counts into single string
SQL> --
SQL> -- NOTE: Beware of size limitations of listagg (4k chars if
SQL> -- used as a SQL statement I believe)
SQL> --
SQL> select listagg(person_count, ',')
within group(order by person_count) as person_agg
from (
select first_name || ':' || count(1) as person_count
from person_test
group by first_name
order by first_name
)
PERSON_AGG
--------------------------------------------------------------------------------
Betty:1,Frank:2,Joe:3
1 row selected.
NOTE: If you do run into a problem with string concatenation too long (exceeds listagg size limit), you can choose to return a CLOB using xmlagg:
-- this returns a CLOB
select rtrim(xmlagg(xmlelement(e,person_count,',').extract('//text()') order by person_count).GetClobVal(),',')
from (
select first_name || ':' || count(1) as person_count
from person_test
group by first_name
order by first_name
);
Hope that helps
EDIT:
If you want counts for multiple columns (firstname and lastname in this example), you can do:
select
typ,
listagg(cnt, ',') within group(order by cnt)
as name_agg
from (
-- FN=FirstName, LN=LastName
select 'FN' as typ, first_name || ':' || count(1) as cnt
from person_test
group by first_name
union all
select 'LN' as typ, last_name || ':' || count(1) as cnt
from person_test
group by last_name
)
group by typ;
Output:
"FN" "Betty:1,Frank:2,Joe:3"
"LN" "Blow:1,Boop:1,Jones:2,Rizzo:1,Smith:1"
I'd also note that you probably can create a custom aggregate function to do this, I just prefer to stick with built in functionality of SQL first if it can solve my problem.

insert multiple row into table using select however table has primery key in oracle SQL [duplicate]

This question already has answers here:
How to create id with AUTO_INCREMENT on Oracle?
(18 answers)
Closed 8 years ago.
I am facing issue while inserting multiple row in one go into table because column id has primary key and its created based on sequence.
for ex:
create table test (
iD number primary key,
name varchar2(10)
);
insert into test values (123, 'xxx');
insert into test values (124, 'yyy');
insert into test values (125, 'xxx');
insert into test values (126, 'xxx');
The following statement creates a constraint violoation error:
insert into test
(
select (SELECT MAX (id) + 1 FROM test) as id,
name from test
where name='xxx'
);
This query should insert 3 rows in table test (having name=xxx).
You're saying that your query inserts rows with primary key ID based on a sequence. Yet, in your insert/select there is select (SELECT MAX (id) + 1 FROM test) as id, which clearly is not based on sequence. It may be the case that you are not using the term "sequence" in the usual, Oracle way.
Anyway, there are two options for you ...
Create a sequence, e.g. seq_test_id with the starting value of select max(id) from test and use it (i.e. seq_test_id.nextval) in your query instead of the select max(id)+1 from test.
Fix the actual subselect to nvl((select max(id) from test),0)+rownum instead of (select max(id)+1 from test).
Please note, however, that the option 2 (as well as your original solution) will cause you huge troubles whenever your code runs in multiple concurrent database sessions. So, option 1 is strongly recommended.
Use
insert into test (
select (SELECT MAX (id) FROM test) + rownum as id,
name from test
where name='xxx'
);
as a workaround.
Of course, you should be using sequences for integer-primary keys.
If you want to insert an ID/Primary Key value generated by a sequence you should use the sequence instead of selecting the max(ID)+1.
Usually this is done using a trigger on your table wich is executed for each row. See sample below:
CREATE TABLE "MY_TABLE"
(
"MY_ID" NUMBER(10,0) CONSTRAINT PK_MY_TABLE PRIMARY KEY ,
"MY_COLUMN" VARCHAR2(100)
);
/
CREATE SEQUENCE "S_MY_TABLE"
MINVALUE 1 MAXVALUE 999999999999999999999999999
INCREMENT BY 1 START WITH 10 NOCACHE ORDER NOCYCLE NOPARTITION ;
/
CREATE OR REPLACE TRIGGER "T_MY_TABLE"
BEFORE INSERT
ON
MY_TABLE
REFERENCING OLD AS OLDEST NEW AS NEWEST
FOR EACH ROW
WHEN (NEWEST.MY_ID IS NULL)
DECLARE
IDNOW NUMBER;
BEGIN
SELECT S_MY_TABLE.NEXTVAL INTO IDNOW FROM DUAL;
:NEWEST.MY_ID := IDNOW;
END;
/
ALTER TRIGGER "T_MY_TABLE" ENABLE;
/
insert into MY_TABLE (MY_COLUMN) values ('DATA1');
insert into MY_TABLE (MY_COLUMN) values ('DATA2');
insert into MY_TABLE (MY_ID, MY_COLUMN) values (S_MY_TABLE.NEXTVAL, 'DATA3');
insert into MY_TABLE (MY_ID, MY_COLUMN) values (S_MY_TABLE.NEXTVAL, 'DATA4');
insert into MY_TABLE (MY_COLUMN) values ('DATA5');
/
select * from MY_TABLE;

Resources