How to create an oracle table with an auto increment column such that whenever exsisiting value is getting inserted it should increment the counter otherwise it should insert a new count
For instance if I have a column with phone number and status
There should be an another column named counter on which auto increment feature will be present
Whenever exsiting phonenumber is inserted again then counter must be increment and if a new value is inserted then counter should add a new initial value for that number
Depending on how you want to insert the data. If you are going to be inserting many rows at the same time then try a MERGEstatement.
Join with the phone number, if found increment the counter column value else set the counter to 1.
If you are going to be inserting one row at a time then this is best done in the code that performs an insert.
EDIT: I did not think this through. Now that I am, I think it is unnecessary to use a counter column.
If you are going to insert phone numbers multiple times anyway, why don't you simply count each phone number? It doesn't have to be stored.
You can't create a table like that.
You can, however, add your own logic into the place where you INSERT new rows - eg, it's not in the table itself. You can also go the route of a TRIGGER.
Additionally, you may wish to simply have your ID be a unique GUID that gets generated and create this duplicate counter whenever it is necessary, using ROW_NUMBER() OVER like EMP_ID in this example from the oracle website:
SELECT department_id, last_name, employee_id, ROW_NUMBER()
OVER (PARTITION BY department_id ORDER BY employee_id) AS emp_id
FROM employees;
DEPARTMENT_ID LAST_NAME EMPLOYEE_ID EMP_ID
------------- ------------------------- ----------- ----------
10 Whalen 200 1
20 Hartstein 201 1
20 Fay 202 2
30 Raphaely 114 1
30 Khoo 115 2
30 Baida 116 3
30 Tobias 117 4
30 Himuro 118 5
30 Colmenares 119 6
For Auto Increment You can create a sequence as below.
CREATE SEQUENCE name_of_sequence
START WITH 1
INCREMENT BY 1;
For the second part of your query you can define a trigger that automatically populates the primary key value using above sequence
Related
I have an EMP table with columns
emp_id(number(10)), ename varchar2(25) and DOB (date)
The count of records = 1billion.
The emp_id column is totally null and I have to fill it with unique values.
What are the 3 easy steps to complete the task?
Help me with Oracle PL/SQL code to finish this task.
Only 2 steps:
ALTER TABLE emp DROP COLUMN emp_id;
ALTER TABLE emp ADD (emp_id NUMBER GENERATED ALWAYS AS IDENTITY);
db<>fiddle here
Again, 2 steps:
CREATE SEQUENCE emp__emp_id__seq;
UPDATE emp
SET emp_id = emp__emp_id__seq.NEXTVAL;
db<>fiddle here
One step:
If you have overwritten the column data then either ROLLBACK the last transaction or restore the data from backups.
The emp_id column is totally null and I have to fill it with unique values.
If you want to do it one-time-only, then just one step would do:
update emp set emp_id = rownum;
and that column will have unique values. You don't need PL/SQL (but be patient as 1 billion rows is quite a lot, it'll take time).
If you want to automatically populate it in the future, then it depends on database version you use. Before 12c, you'll have to use a sequence and a database trigger. In later versions, you can still use the same (sequence + trigger) or - as MT0 showed - identity column.
I am trying to use the following statement for the Delete process and it has to delete around 23566424 Rows, but oracle takes almost 3 hours to complete the process and we have already created an index on " SCHEDULE_DATE_KEY" but still, the process is very slow.Can someone advise on how to make Deletes faster in oracle
DELETE
FROM
EDWSOURCE.SCHEDULE_DAY_F
WHERE
SCHEDULE_DATE_KEY >
(
SELECT
LAST_PAYROLL_DATE_KEY
FROM
EDWSOURCE.LAST_PAYROLL_DATE
WHERE
CURRENT_FLAG = 'Y'
);
I don't think any index will help here, probably Oracle will decide the best approach is a full table scan to delete 20M rows from 300M. It is deleting at a rate of over 2000 rows per second, which isn't bad. In fact any additional indexes will slow it down as it has to delete the row entry from the index as well.
A quicker approach could be to create a new table of the rows you want to keep, something like:
create table EDWSOURCE.SCHEDULE_DAY_F_KEEP
as
select * from EDWSOURCE.SCHEDULE_DAY_F
where SCHEDULE_DATE_KEY <=
(
SELECT
LAST_PAYROLL_DATE_KEY
FROM
EDWSOURCE.LAST_PAYROLL_DATE
WHERE
CURRENT_FLAG = 'Y'
);
Then recreate any constraints and indexes to use the new table.
Finally drop the old table and rename the new one.
You can try testing a filtered table move. This has an online clause. So you can do this while the application is still running.
Note 12.2 and later the indexes will remain valid. In earlier versions you will need to rebuild the indexes as they will become invalid. Good Luck
Move a Table
Create and populate a new test table.
DROP TABLE t1 PURGE;
CREATE TABLE t1 AS
SELECT level AS id,
'Description for ' || level AS description
FROM dual
CONNECT BY level <= 100;
COMMIT;
Check the contents of the table.
SELECT COUNT(*) AS total_rows,
MIN(id) AS min_id,
MAX(id) AS max_id
FROM t1;
TOTAL_ROWS MIN_ID MAX_ID
---------- ---------- ----------
100 1 100
SQL>
Move the table, filtering out rows with an ID value greater than 50.
ALTER TABLE t1 MOVE ONLINE
INCLUDING ROWS WHERE id <= 50;
Check the contents of the table.
SELECT COUNT(*) AS total_rows,
MIN(id) AS min_id,
MAX(id) AS max_id
FROM t1;
TOTAL_ROWS MIN_ID MAX_ID
---------- ---------- ----------
50 1 50
SQL>
The rows with an ID value between 51 and 100 have been removed.
As mentioned above if maybe best to PARTITION the table abs drop a PARTITION every N number of days as part of a daily task.
I want to insert 400000 random lines into a table with an oracle procedure.
I got an 'violated - parent key not found' Error.
create or replace
PROCEDURE TESTDATA AS
X INTEGER;
BEGIN
FOR X IN 1..400000 LOOP
INSERT INTO SALES
SELECT CAST(DBMS_RANDOM.VALUE(1, 10) as INTEGER) as "CUSTOMER_ID",
CAST(DBMS_RANDOM.VALUE(1, 2) as INTEGER) as "PRODUCT_ID",
TO_DATE(TRUNC(DBMS_RANDOM.VALUE(TO_CHAR(TO_DATE('1-jan-2000'),'J'),TO_CHAR(TO_DATE('21-dec-2012'),'J'))),'J') as "SALES_DATE",
CAST(DBMS_RANDOM.VALUE(1, 100) as INTEGER) as "PIECES"
FROM DUAL;
END LOOP;
END TESTDATA;
-- -----------------------------------------------------
-- Table SALES
-- -----------------------------------------------------
CREATE TABLE SALES (
CUSTOMER_ID INTEGER,
PRODUCT_ID INTEGER,
SALES_DATE DATE,
PIECES INTEGER,
PRIMARY KEY (CUSTOMER_ID, PRODUCT_ID, SALES_DATE),
FOREIGN KEY (CUSTOMER_ID) REFERENCES CUSTOMER (CUSTOMER_ID),
FOREIGN KEY (PRODUCT_ID) REFERENCES PRODUCT (PRODUCT_ID)
);
I hope that anyone could help me.
Roman
Your create table statement includes foreign keys on the CUSTOMER and PRODUCT tables. Your insert statement uses random values to populate CUSTOMER_ID and PRODUCT_ID. It is highly unlikely that random values will match existing keys in the referenced tables, so it's unsurprising that you get foreign key violations.
As to how you fix it, well that depends on what you actually want to achieve. As you're populating your table with random numbers you clearly don't care about the data, so you might as well drop the foreign keys. Alternatively you can use primary key values from the referenced tables in the insert statement.
"How can I use the primary key values from the referenced tables?"
You have twenty permutations of PRODUCT and CUSTOMER. So you will need to thrash through them 20000 times to generate 400000 records.
"In the customer table I have inserted 10 rows (1-10) and in the product table 2 rows (1-2)"
Here's a version of your procedure which loops through Products and Customers to generate random combinations of them. There's a third outer loop which allows you to produce as many sets of twenty combos as your heart desires.
create or replace procedure testdata
( p_iters in pls_integer := 1)
as
type sales_nt is table of sales%rowtype;
recs sales_nt;
rec sales%rowtype;
tot_ins simple_integer := 0;
begin
recs := new sales_nt();
dbms_random.seed(val=>to_number(to_char(sysdate,'sssss')));
<< iterz >>
for idx in 1..p_iters loop
<< prodz >>
for c_rec in ( select customer_id from customer
order by dbms_random.value )
loop
<< custz >>
for p_rec in ( select product_id from product
order by dbms_random.value )
loop
rec.customer_id := c_rec.customer_id;
rec.product_id := p_rec.product_id;
rec.sales_date := date '2000-01-01' + dbms_random.value(1, 6000);
rec.pieces := dbms_random.value(1, 100);
recs.extend();
recs(recs.count()) := rec;
end loop custz;
end loop prodz;
forall idx in 1..recs.count()
insert into sales
values recs(idx);
tot_ins := tot_ins + sql%rowcount;
recs.delete();
end loop iterz;
dbms_output.put_line('test records generated = '||to_char(tot_ins));
end testdata;
/
Proof of the pudding:
SQL> set serveroutput on
SQL> exec testdata(2)
test records generated = 40
PL/SQL procedure successfully completed.
SQL> select * from sales;
CUSTOMER_ID PRODUCT_ID SALES_DAT PIECES
----------- ---------- --------- ----------
9 2 21-FEB-02 42
9 1 10-AUG-05 63
7 1 23-FEB-12 54
7 2 21-NOV-12 80
1 2 06-NOV-15 56
1 1 08-DEC-09 47
4 2 08-JUN-10 58
4 1 19-FEB-09 43
2 2 04-SEP-02 64
2 1 09-SEP-15 69
6 2 20-FEB-08 60
...
2 1 11-JAN-16 19
3 2 03-FEB-10 58
3 1 25-JUL-10 66
9 2 26-FEB-16 70
9 1 15-MAY-14 90
6 2 03-APR-05 60
6 1 21-MAY-15 19
40 rows selected.
SQL>
note
As #mathguy points out, the OP's code constrains the range of random values to fit the range of primary keys in the referenced tables. However, I will leave this answer in place because its general approach is both safer (guaranteed to always match a referenced primary key) and less brittle (can cope with inserting or deleting PRODUCT or CUSTOMER records.
I have a Silverlight app that makes multiple (often concurrent) asynchronous calls to an Oracle database. The largest database table stores around 5 million records. Below is a summary of how the Silverlight app works, followed by my question.
The user sets query criteria to select a particular group of records, usually 500 to 5000 records in a group.
An asynchronous WCF call is made to the database to retrieve the values in four fields (latitude, longitude, heading, and time offset) over the selected group of records (meaning the call returns anywhere from 2k to 20k floating point numbers. These values are used to plot points on a map in the browser.
From here, the user can choose to graph the values in one or more of an additional twenty or so fields associated with the initial group of records. The user clicks on a field name, and another async WCF call is made to retrieve the field values.
My question is this: does it make sense in this case to store the records selected in step one in a temp table (or materialized view) in order to speed up and simplify the data access in step three?
If so, can anyone give me a hint regarding a good way to maintain the browser-to-temp-table link for a user's session?
Right now, I am just re-querying the 5 million points each time the user selects a new field to graph--which works until the user selects three or more fields at once. This causes the async calls to timeout before they can return.
We can do this using a CONTEXT. This is a namespace in session memory which we can use to store values. Oracle comes with a default namespace, 'USERENV', but we can define our own. The context has to be created by a user with the CREATE ANY CONTEXT privilege; this is usually a DBA. The statement references a PACKAGE which sets and gets values in the namespace, but this package does not have to exist in order for the statement to succeed:
SQL> create context user_ctx using apc.ctx_pkg
2 /
Context created.
SQL>
Now let's create the package:
SQL> create or replace package ctx_pkg
2 as
3 procedure set_user_id(p_userid in varchar2);
4 function get_user_id return varchar2;
5 procedure clear_user_id;
6 end ctx_pkg;
7 /
Package created.
SQL>
There are three methods, to set, get and unset a value in the namespace. Note that we can use one namespace to hold different valiables. I am just using this package to set one variable (USER_ID) in the USER_CTX namespace.
SQL> create or replace package body ctx_pkg
2 as
3 procedure set_user_id(p_userid in varchar2)
4 is
5 begin
6 DBMS_SESSION.SET_CONTEXT(
7 namespace => 'USER_CTX',
8 attribute => 'USER_ID',
9 value => p_userid);
10 end set_user_id;
11
12 function get_user_id return varchar2
13 is
14 begin
15 return sys_context('USER_CTX', 'USER_ID');
16 end get_user_id;
17
18 procedure clear_user_id
19 is
20 begin
21 DBMS_SESSION.CLEAR_CONTEXT(
22 namespace => 'USER_CTX',
23 attribute => 'USER_ID');
24 end clear_user_id;
25
26 end ctx_pkg;
27 /
Package body created.
SQL>
So, how does this solve anything? Here is a table for the temporary storage of data. I'm going to add a column which will hold a token to identify the user. When we populate the table the value for this column will be provided by CTX_PKG.GET_USER_ID():
SQL> create table temp_23 as select * from big_table
2 where 1=0
3 /
Table created.
SQL> alter table temp_23 add (user_id varchar2(30))
2 /
Table altered.
SQL> create unique index t23_pk on temp_23(user_id, id)
2 /
Index created.
SQL>
... and over that table I create a view:...
create or replace view v_23 as
select
id
, col1
, col2
, col3
, col4
from temp_23
where user_id = ctx_pkg.get_user_id
/
Now, when I want to store some data in the table I need to set the context with a value with uniquely identifies my user.
SQL> exec ctx_pkg.set_user_id('APC')
PL/SQL procedure successfully completed.
SQL>
This statement populates the temporary table with twenty random rows:
SQL> insert into temp_23
2 select * from
3 ( select b.*, ctx_pkg.get_user_id
4 from big_table b
5 order by dbms_random.random )
6 where rownum <= 20
7 /
20 rows created.
SQL>
I can retrieve those rows by querying the view. But when I change my USER_ID and run the same query I cannot see them any more:
SQL> select * from v_23
2 /
ID COL1 COL2 COL3 COL4
---------- ---------- ------------------------------ --------- ----------
277834 1880 GV_$MAP_EXT_ELEMENT 15-OCT-07 4081
304540 36227 /375c3e3_TCPChannelReaper 15-OCT-07 36
1111897 17944 /8334094a_CGCast 15-OCT-07 17
1364675 42323 java/security/PublicKey 15-OCT-07 42
1555115 3379 ALL_TYPE_VERSIONS 15-OCT-07 3
2073178 3355 ALL_TYPE_METHODS 15-OCT-07 3
2286361 68816 NV 15-OCT-07 68
2513770 59414 /5c3965c8_DicomUidDoc 15-OCT-07 59
2560277 66973 MGMT_MNTR_CA 15-OCT-07 66
2700309 45890 /6cc68a64_TrustManagerSSLSocke 15-OCT-07 45
2749978 1852 V_$SQLSTATS 15-OCT-07 6395
2829080 24832 /6bcb6225_TypesTypePair 15-OCT-07 24
3205157 55063 SYS_NTsxSe84BlRX2HiXujasKy/w== 15-OCT-07 55
3236186 23830 /de0b4d45_BaseExecutableMember 15-OCT-07 23
3276764 31296 /a729f2c6_SunJCE_n 15-OCT-07 31
3447961 60129 HHGROUP 15-OCT-07 60
3517106 38204 java/awt/im/spi/InputMethod 15-OCT-07 38
3723931 30332 /32a30e8e_EventRequestManagerI 15-OCT-07 30
3877332 53700 EXF$XPVARCLST 15-OCT-07 53
4630976 21193 oracle/net/nl/NetStrings 15-OCT-07 21
20 rows selected.
SQL> exec ctx_pkg.set_user_id('FOX_IN_SOCKS')
PL/SQL procedure successfully completed.
SQL> select * from v_23
2 /
no rows selected
SQL>
So, the challenges are:
to establish a token which you can use automatically to uniquely identify a user
to find a hook in your connecting code which can set the context each time the user gets a session
just as importantly, to find a hook in your dis-connecting code which can unset the context each time the user leaves a session
Also, remember to clear out the table once the user has finished with it.
when I first read this I thought 'global temporary table' (gtt) and realized that would not help you in the slightest! This is because the data in a GTT is visible only in a session, and with a stateless web app, probably using connection pooling, there is no guaranteed relationship between application user and database session (one user might be handed different sessions on successive connections, one session will be handed to several different users). Now a temp table should do the trick.
It seems that on each iterative hit, the person (via silverlight) is polling the same data (and a large amount to boot).
I do believe that a temp table would suffice. Here is an asktom that shows how to do this in a web environment. Keep in mind the moment the data is stored it is aging and possibly stale and there will need to be a cleanup job.
http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:76812348057
Now to tie it back to the user, not 100% sure how to do this in Silverlight (assuming via asp.net?) is the user authenticated prior to proceding? if so, you ought to be able to take their credentials and utilize that as the source to query against (utilize their user name and/or SID as their primary key and foreign key it against the data table as described in the asktom link).
http://www.codeproject.com/KB/silverlight/SL3WindowsIdentityName.aspx
this links appears to show how to get the current silverlight user in a window's authenticated scheme.
hth
How should I index a date column when some rows has null values?
We have to select rows between a date range and rows with null dates.
We use Oracle 9.2 and higher.
Options I found
Using a bitmap index on the date column
Using an index on date column and an index on a state field which value is 1 when the date is null
Using an index on date column and an other granted not null column
My thoughts to the options are:
to 1: the table have to many different values to use an bitmap index
to 2: I have to add an field only for this purpose and to change the query when I want to retrieve the null date rows
to 3: locks tricky to add an field to an index which is not really needed
What is the best practice for this case?
Thanks in advance
Some infos I have read:
Oracle Date Index
When does Oracle index null column values?
Edit
Our table has 300,000 records. 1,000 to 10,000 records are inserted and delete every day. 280,000 records have a null delivered_at date. It is a kind of picking buffer.
Our structure (translated to english) is:
create table orders
(
orderid VARCHAR2(6) not null,
customerid VARCHAR2(6) not null,
compartment VARCHAR2(8),
externalstorage NUMBER(1) default 0 not null,
created_at DATE not null,
last_update DATE not null,
latest_delivery DATE not null,
delivered_at DATE,
delivery_group VARCHAR2(9),
fast_order NUMBER(1) default 0 not null,
order_type NUMBER(1) default 0 not null,
produkt_group VARCHAR2(30)
)
In addition to Tony's excellent advice, there is also an option to index your column in such a way that you don't need to adjust your queries. The trick is to add a constant value to just your index.
A demonstration:
Create a table with 10,000 rows out of which only 6 contain a NULL value for the a_date column.
SQL> create table mytable (id,a_date,filler)
2 as
3 select level
4 , case when level < 9995 then date '1999-12-31' + level end
5 , lpad('*',1000,'*')
6 from dual
7 connect by level <= 10000
8 /
Table created.
First I'll show that if you just create an index on the a_date column, the index is not used when you use the predicate "where a_date is null":
SQL> create index i1 on mytable (a_date)
2 /
Index created.
SQL> exec dbms_stats.gather_table_stats(user,'mytable',cascade=>true)
PL/SQL procedure successfully completed.
SQL> set autotrace on
SQL> select id
2 , a_date
3 from mytable
4 where a_date is null
5 /
ID A_DATE
---------- -------------------
9995
9996
9997
9998
9999
10000
6 rows selected.
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=72 Card=6 Bytes=72)
1 0 TABLE ACCESS (FULL) OF 'MYTABLE' (Cost=72 Card=6 Bytes=72)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
720 consistent gets
0 physical reads
0 redo size
285 bytes sent via SQL*Net to client
234 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
6 rows processed
720 consistent gets and a full table scan.
Now change the index to include the constant 1, and repeat the test:
SQL> set autotrace off
SQL> drop index i1
2 /
Index dropped.
SQL> create index i1 on mytable (a_date,1)
2 /
Index created.
SQL> exec dbms_stats.gather_table_stats(user,'mytable',cascade=>true)
PL/SQL procedure successfully completed.
SQL> set autotrace on
SQL> select id
2 , a_date
3 from mytable
4 where a_date is null
5 /
ID A_DATE
---------- -------------------
9995
9996
9997
9998
9999
10000
6 rows selected.
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=6 Bytes=72)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'MYTABLE' (Cost=2 Card=6 Bytes=72)
2 1 INDEX (RANGE SCAN) OF 'I1' (NON-UNIQUE) (Cost=2 Card=6)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
6 consistent gets
0 physical reads
0 redo size
285 bytes sent via SQL*Net to client
234 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
6 rows processed
6 consistent gets and an index range scan.
Regards,
Rob.
"Our table has 300,000 records....
280,000 records have a null
delivered_at date. "
In other words almost the entire table satisfies a query which searches on where DELIVERED_AT is null. An index is completely inappropriate for that search. A full table scan is much the best approach.
If you have an Enterprise Edition license and you have the CPUs to spare, using a parallel query would reduce the elapsed time.
Do you mean that your queries will be like this?
select ...
from mytable
where (datecol between :from and :to
or datecol is null);
It would only be worth indexing the nulls if they were relatively few in the table - otherwise a full table scan may be the most efficient way to find them. Assuming it is worth indexing them you could create a function-based index like this:
create index mytable_fbi on mytable (case when datecol is null then 1 end);
Then change your query to:
select ...
from mytable
where (datecol between :from and :to
or case when datecol is null then 1 end = 1);
You could wrap the case in a function to make it slicker:
create or replace function isnull (p_date date) return varchar2
DETERMINISTIC
is
begin
return case when p_date is null then 'Y' end;
end;
/
create index mytable_fbi on mytable (isnull(datecol));
select ...
from mytable
where (datecol between :from and :to
or isnull(datecol) = 'Y');
I made sure the function returns NULL when the date is not null so that only the null dates are stored in the index. Also I had to declare the function as DETERMINISTIC. (I changed it to return 'Y' instead of 1 merely because to me the name "isnull" suggests it should; feel free to ignore my preference!)
Avoid the table lookup and create the index like this :
create index i1 on mytable (a_date,id) ;