How to auto adjust ID value (primary key) in oracle? - java

Right now I have created a sequence and a trigger to auto increment the ID value like this:
CREATE OR REPLACE TRIGGER "RTH"."TBL_USER_TRIGGER"
BEFORE INSERT ON TBL_USER
FOR EACH ROW
BEGIN
SELECT TBL_USER_SEQ.nextval
INTO :new.USR_ID
FROM dual;
END;
ALTER TRIGGER "RTH"."TBL_USER_TRIGGER" ENABLE;
Let say I have three rows:
User ID FIRSTNAME LASTNAME
====================================
1 John smith
2 James smith
3 Pat smith
When I delete the first row(1) I want the ID values to auto correct itself to correct numbers so that second row ID values becomes 1 and third row ID values becomes 2
Is it possible in oracle? or do I have do it through code as I am using Java to insert records into table when user submits a form.
Java DAO class:
public class RegistrationDAO {
public void insert(User user) {
try {
Connection con = DBConnection.getConnection();
String query = "insert into TBL_USER(USR_FIRST_NAME,USR_LST_NAME,USR_PRIMARY_EMAIL,USR_PASSWORD) values(?,?,?,?)";
PreparedStatement pst = con.prepareStatement(query);
pst.setString(1, user.getFirstName());
pst.setString(2, user.getLastName());
pst.setString(3, user.getEmail());
pst.setString(4, user.getPassword());
pst.executeUpdate();
} catch (Exception e) {
System.out.println("####Record insertion error in Registration DAO####");
System.out.println(e);
}
}
}

The simple answer is: "No, you don't want to do that." The purpose of an id is to uniquely identify each row. A sequential id also has the feature that it provides insertion order. It is not intended to change over time. Row 1 is Row 1 is Row 1.
If you want ordering, then declare the id to be the primary key and use a query such as this:
select t.*, row_number() over (order by usr_id) as seqnum
from t;

Ideally, you should not be changing USER ID values. But still if there is requirement. Then below are the steps.
Create a procedure to reset the sequence TBL_USER_SEQ. This is needed as you are resetting the USER ID values. So after resetting, whenever you insert new record, Sequence should start with proper value ( i.e. MAX USER_ID value of the table). So that there will be no gap in USER_ID
Write Delete Trigger on table which will adjust USER_ID values.
Procedure to reset sequence
CREATE OR REPLACE PROCEDURE reset_sequence_p (p_seq IN VARCHAR2, p_new_seq NUMBER)
AS
l_value NUMBER;
BEGIN
-- Select the next value of the sequence
EXECUTE IMMEDIATE 'SELECT ' || p_seq || '.NEXTVAL FROM DUAL' INTO l_value;
-- Alter the sequnce to increment by the difference
EXECUTE IMMEDIATE 'ALTER SEQUENCE ' || p_seq || ' INCREMENT BY ' || ( p_new_seq - l_value ) ;
-- Increment to next value after diference
EXECUTE IMMEDIATE 'SELECT ' || p_seq || '.NEXTVAL FROM DUAL' INTO l_value;
-- Set the increment back to 1
EXECUTE IMMEDIATE 'ALTER SEQUENCE ' || p_seq || ' INCREMENT BY ' || 1 ;
END reset_sequence_p;
Delete Trigger
CREATE OR REPLACE TRIGGER "RTH"."TBL_USER_TRIG_DEL"
BEFORE DELETE
ON TBL_USER
FOR EACH ROW
DECLARE
v_new_user_id_seq NUMBER;
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
-- Update USER_ID value for all remaining records greater than USER_ID that got deleted.
UPDATE TBL_USER
SET USER_ID = USER_ID - 1
WHERE USER_ID > :old.USER_ID;
-- Retrieve max USER_ID available in table.
SELECT MAX (user_id) INTO v_new_user_id_seq FROM TBL_USER;
-- Call procedure to reset sequence
reset_sequence_p ('TBL_USER_SEQ', v_new_user_id_seq);
COMMIT;
END;
NOTE : Make sure that Primary key on table is disabled before execution of Delete statement.
I hope this helps.

Related

How to change value in mysql based on another value in the same database?

I am trying to change one of the value in my table in database based on an another value in the same database but different table. The first table is called orders and the second is called buy supply. What I want to do is I want to change the value of sumquantity that is in 'buysupply' table by subtracting a value from the column named quantity_ordered from 'orders' table. I tried writing some query but it is not working, it keeps popping up error. If you know of a solution, do let me know. The code is all below as well
private void DispatchButtonActionPerformed(java.awt.event.ActionEvent evt) {
String type = txttype.getSelectedItem().toString();
String name = txtname.getText();
String quantity = txtquantity.getText();
String dispatch_row = txtdispatch.getText();
String statusDispatched = "Dispatched";
try {
Class.forName("com.mysql.jdbc.Driver");
con1 = DriverManager.getConnection("jdbc:mysql://localhost/restock", "root", "password");
// Focus on this
String template = "UPDATE orders SET status = '%s' WHERE id = %s";
String template2 = "UPDATE buysupply SET sumquantity = sumquantity - %s WHERE id = %s";
String quantity_ordered = "quantity_ordered FROM orders";
pst = con1.prepareStatement(String.format(template, statusDispatched, dispatch_row));
pst.executeUpdate();
pst1 = con1.prepareStatement(String.format(template2, quantity_ordered , dispatch_row));
pst1.executeUpdate();
// Look on top
JOptionPane.showMessageDialog(null, "Item has been dispatched");
// To update the newly recorded data to the table
table_update();
// Set the textfields to empty upon button click
txttype.setSelectedIndex(-1);
txtname.setText("");
txtquantity.setText("");
txtdispatch.setText("");
txttype.requestFocus();
} catch (ClassNotFoundException | SQLException ex) {
JOptionPane.showMessageDialog(null, "Quantity or Dispatch field is not an integer, Please try again.");
Logger.getLogger(mainpage.class.getName()).log(Level.SEVERE, null, ex);
}
}
// This code is in another class file
try {
Class.forName("com.mysql.jdbc.Driver");
con1 = DriverManager.getConnection("jdbc:mysql://localhost/restock", "root", "password");
String template = "SELECT SUM(quantity) as sumquantity FROM buysupply WHERE itemtype IN ('Plastic gloves', 'Rubber gloves')";
PreparedStatement pst = con1.prepareStatement(template);
ResultSet rs = pst.executeQuery();
if (rs.next()) {
glovesum = rs.getString("sumquantity");
} else {
System.out.print("Query didn't return any results");
}
} catch (ClassNotFoundException | SQLException ex) {
Logger.getLogger(stock.class.getName()).log(Level.SEVERE, null, ex);
}
As far I understand (and please read until the end) if it is a classic Invoice order database with a list of product into "buysupply" table and "order" table for the ordering client list of product.
The first point, several people into the comment point is the missing link data between both table. I assume reading your peace of code that the link is made by a ID but that not clear, so I offer a solution base on a link between those columns:
orders.itemtype = buysupply.itemtype
If it on an other element, please change the information into the SQL query below.
I assume also the column orders.status as to change the values from what I call 'Waiting' value to 'Dispatched' value.
So here the data before into "buysupply" table:
id, itemtype, quantity
1 , mask , 704
2 , clothed, 101
3 , N95, 18
Here the data before into "order" table:
id, itemtype, quantity_orderred, status,
1,mask, 1 , Dispatched
2,clothed,3,Waiting
The SQL to update both value (the orders.status and buysupply.quantity ) should be something like that suppose the order.id to update is 2:
update orders,buysupply
set orders.status='Dispatched',
buysupply.quantity = buysupply.quantity - orders.quantity_orderred
where
orders.itemtype = buysupply.itemtype
AND
orders.status = 'Waiting'
AND
orders.id = '2'
AFTER:
So here the data after into "buysupply" table:
id, itemtype, quantity
1 , mask , 704
2 , clothed, 98
3 , N95, 18
Here the data before into "order" table:
id, itemtype, quantity_orderred, status,
1,mask, 1 , Dispatched
2,clothed,3,Dispatched
The update could apply on several tables and columns, you should just indicate the column table name with each column to avoid confusion.
That could be the first step to let you improve the code for the sum part, that, I afraid no understand at all.
Then I find a partial information explaining that the sum_quantity is a computed calcul from a sum of the value, so you do not want to change the quantity, my bad.
So you can create a temporary table with this kind of SQL, temporary table is detroy at the connection close:
CREATE TEMPORARY TABLE IF NOT EXISTS TMPsumquantity AS
SELECT SUM(quantity) as sumquantity FROM buysupply WHERE itemtype IN ('Plastic gloves', 'Rubber gloves')
That could create a column with the information you want, BUT, it's not my recommendation as far I understand ;-)
I will create a new column to store the sum value into the table "buysupply", to say the "the quantity in stock avalaible at the moment this order will be Dispatched is that for this element" so the result of you sum value
Before "buysupply":
id, itemtype, quantity, quantity_avalaible
1 , mask , 704, 704
2 , clothed, 101, 101
3 , N95, 18, 18
Before "order":
id, itemtype, quantity_orderred, status, quantity_avalaible
1,mask, 1 , Dispatched
2,clothed,3, Waiting
So the SQL to create this column is complex, base on an inner-join between the same table
UPDATE buysupply b1
INNER JOIN (
SELECT SUM(quantity) as sumquantity, id
FROM buysupply
where buysupply.itemtype IN ('clothed', 'N95')
) b2 ON true
SET b1.quantity_avalaible = b2.sumquantity
So the new table "buysupply" with the colum "quantity_avalaible" containing the sum of the value of the colum quantity for N95 and clothed values :
id, itemtype, quantity, quantity_avalaible
1 , mask , 704, 116
2 , clothed, 101, 116
3 , N95, 18, 116
So then you can use the first SQL proposal to update quantity_avalaible depending the value of "orders.quantity_orderred"
Last point, I have a partial view on the data structure and the bussiness logic, it could be usefull to store a negative value into the column "orders.quantity_orderred" so the SQL SUM could add and substract values with the same call to the SUM function
Best

Executing multiple procedure calls at once in jdbc

FYI, None of the solutions mentioned in this answer have worked for me. I intend to execute multiple procedure call in one sql query.
The Mysql code is:
SET #SYSTEM_ID = (SELECT `id` FROM `users_admin` WHERE `username`='my_username');
SET #PAYMENT_MODE = 0;
CALL payment_mode_add(#SYSTEM_ID, 'TEST', TRUE, #PAYMENT_MODE);
CALL payment_add(#SYSTEM_ID, #PAYMENT_MODE,
'receipt/00',1000.50,#TEMP_ID);
The way it's supposed to work is, Procedure "payment_mode_add" sets out an output parameter which is supposed to be used as an input parameter by the procedure "payment_add".I know that executing multiple queries at once is not possible in Java, but the method i intend to use here works well in languages like PHP. Definition for payment_mode_add is:
# -- PAYMENT-MODE ADD
DELIMITER //
DROP PROCEDURE IF EXISTS `payment_mode_add` //
# -- remove above
CREATE PROCEDURE `payment_mode_add`(IN _author INT, IN _name VARCHAR(20), IN _active BOOLEAN, OUT _id INT)
BEGIN
# -- declare
IF NOT EXISTS (SELECT `id` FROM `users_admin` WHERE `id`=_author AND `active`=TRUE) THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Invalid Author'
ELSEIF EXISTS (SELECT `id` FROM `gym_form_hhq` WHERE `name`=_name) THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '\'Payment Mode\' already exists'
ELSE
SET _active = IFNULL(_active,FALSE)
INSERT INTO `payment_mode`(`name`, `active`, `author`)
VALUES ( _name , _active , _author )
SET _id = LAST_INSERT_ID()
SELECT * FROM `payment_mode` WHERE `id`=_id
END IF
END //
DELIMITER ;
Definition for "payment_add":
# -- PAYMENT ADD
DELIMITER //
DROP PROCEDURE IF EXISTS `payment_add` //
# -- remove above
CREATE PROCEDURE `payment_add`(IN _author INT, IN _mode INT, IN _receipt VARCHAR(50), IN _amount FLOAT, OUT _id INT)
BEGIN
# -- declare
IF NOT EXISTS (SELECT `id` FROM `users_admin` WHERE `id`=_author AND `active`=TRUE) THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Invalid Author'
ELSEIF NOT EXISTS (SELECT `id` FROM `payment_mode` WHERE `id`=_mode AND `active`=TRUE) THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Invalid Payment Mode'
ELSE
SET _receipt = IFNULL(_receipt, NOW())
SET _amount = IFNULL(_amount, 0)
INSERT INTO `payment`(`mode`, `reciept`, `amount`, `author`)
VALUES (_mode , _receipt , _amount , _author)
SET _id = LAST_INSERT_ID()
SELECT * FROM `payment` WHERE `id`=_id
END IF
END
This is just a basic example for some more complex problems that i'm facing in JDBC. Is there any way that i can execute all those 4 queries at once though JDBC or any other method that can give me the output i intend to achieve?
Since this question was getting many upvotes and i had found a solution to it, i'm answering it.
So all you have to do is write a method that takes in an array of string sql queries as its parameter, executes all the queries(in the array) one by one, and returns the last ResultSet. Example:
public ResultSet runMutltipleQueries(String [] arrayOfQueries) throws SQLException{
ResultSet rs = null;
Statement stmnt= con.createStatement();
boolean rsReturned=false;
for(int i=0; i<arrayOfQueries.length;i++){
rsReturned = stmnt.execute(arrayOfQueries[i]);
}
if(rsReturned){
rs= stmnt.getResultSet();
}
return rs;
}
And while calling the method,
queries= new String [] {"SET #SYSTEM_ID = (SELECT `id` FROM `users_admin` WHERE `username`='"+model.getUserName()+"');",
"SET #TRANSACTION_ID = (select `user_data`.`transaction` from `user_data` where id= "+idText.getText()+");",
"SET #FREEZE_ID = NULL;",
// set payment id to 0 for now, update it in pay operation.
"SET #PAYMENT_ID = NULL;",
"CALL user_freeze_add(#SYSTEM_ID, #TRANSACTION_ID, '"+FreezeStartDate.getValue()+"', '"+FreezeStartDate.getValue().plusDays(Integer.parseInt(freezeAvailabletext.getText()))+"', #PAYMENT_ID, #FREEZE_ID);"
};
ResultSet rs= DatabaseHandler.getInstance().runMutltipleQueries(queries);

Updating rows of updatable view with ResultSet.updateRow function

I have updatable view on PostgreSQL server.
Update query works fine when I execute it from pgAnmin3 console, but when I try to update this view with ResultSet.updateRow() method, I get the following error:
org.postgresql.util.PSQLException: No primary key found for table
I guess I can't specify primary key for view.
Can I specify key columns for ResultSet.updateRow() method in my client application? Or can I specify a WHERE clause for ResultSet.updateRow() method?
Here are my tables
CREATE TABLE fin.t_year
(
id serial NOT NULL,
date_begin date NOT NULL,
date_end date NOT NULL,
year_name character varying(128),
CONSTRAINT "PK_year" PRIMARY KEY (id)
)
WITH (
OIDS=FALSE
);
CREATE TABLE fin.t_period
(
id serial NOT NULL,
id_year integer NOT NULL,
per_begin date NOT NULL,
per_end date NOT NULL,
per_name character varying(256),
CONSTRAINT "PK_period" PRIMARY KEY (id),
CONSTRAINT "FK_period_year" FOREIGN KEY (id_year)
REFERENCES fin.t_year (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
WITH (
OIDS=FALSE
);
CREATE VIEW fin.vi_period AS
SELECT per.id,
per.per_begin AS "Begin",
per.per_end AS "End",
per.per_name AS "Name",
y.year_name AS "Year"
FROM fin.t_period per
JOIN fin.t_year y ON y.id = per.id_year;
CREATE OR REPLACE FUNCTION fin.tgfn_vi_period_update()
RETURNS trigger AS
$BODY$
DECLARE
id_row INTEGER;
id_curr INTEGER;
result RECORD;
BEGIN
id_curr = NEW.id;
-- Replace text identifier with integer primary key
IF NEW."Year" IS NOT NULL THEN
SELECT id INTO id_row
FROM fin.t_year
WHERE year_name = NEW."Year";
UPDATE fin.t_period SET id_year = id_row
WHERE id = id_curr;
END IF;
IF NEW."Begin" IS NOT NULL THEN
UPDATE fin.t_period SET per_begin = NEW."Begin"
WHERE id = id_curr;
END IF;
IF NEW."End" IS NOT NULL THEN
UPDATE fin.t_period SET per_end = NEW."End"
WHERE id = id_curr;
END IF;
IF NEW."Name" IS NOT NULL THEN
UPDATE fin.t_period SET per_name = NEW."Name"
WHERE id = id_curr;
END IF;
SELECT * INTO result FROM fin.vi_period WHERE id = id_curr;
RETURN result;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
This insert statement works fine
UPDATE fin.vi_period SET "Year" = 'new_year_name' WHERE id = 10;
But the problem with this java code
statement = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet rs = statement.ExecuteQuery("SELECT * FROM fin.vi_period;");
rs.absolute(pos + 1);
rs.updateString("new_year_name");
rs.updateRow();
The problem is that you are querying a view, and that view doesn't have a primary key (I am not sure if that is even possible with PostgreSQL, but most database don't support that). The JDBC driver requires a primary key to be able to make the result set updatable.
In other words: you cannot update this view through the result set. You either need to use an explicit UPDATE statement, or do this directly on the underlying table, not through the view.

MySql database table having Xml String. How to Import that data into another mysql Table

I have one Person named table's. this table's column "outsourcedData" contain below xml as an String :
<person>
<educations total="2">
<education>
<school-name>Delhi University</school-name>
<degree>Master of Science (MSc)</degree>
<field-of-study>Banking and Financial Support Services</field-of-study>
<start-date>
<year>2009</year>
</start-date>
<end-date>
<year>2013</year>
</end-date>
</education>
<education>
<school-name>American University</school-name>
<degree>Bachelor of Arts (BA)</degree>
<field-of-study>Business Administration and Management, General</field-of-study>
</education>
</educations>
</person>
There are lots of similar rows available into this table. Is there is any way so I can load these data parse and insert into education table.
There are lots of row I am having in my database. But now I want to import this data into new table Education which I newly created in database corresponding fields with xml.(SchoolName,degree......).
In Mysql Database what is the best way to migrate this database.
I am stuck in this place. Please help. help
create table person (id int,outersource varchar(1024));
insert into person values(1,'<?xml version="1.0" encoding="UTF-8" standalone="yes"?><person><educations total="2"><education><school-name>Delhi University</school-name><degree>Master of Science (MSc)</degree><field-of-study>Banking and Financial Support Services</field-of-study><start-date><year>2009</year></start-date><end-date><year>2013</year></end-date></education><education><school-name>American University</school-name><degree>Bachelor of Arts (BA)</degree><field-of-study>Business Administration and Management, General</field-of-study></education></educations></person>');
create table education( schoolName varchar(255), degree varchar(255),start_year datetime, end_year datetime);
Any Store Procedure we can do this ?
Originally problem was that. I have three tables. first table where xml string data exist and two new table for this xml string parsing. Parent table contain school name + userid unique combinate. during parsing xml if any node contain same school name + user id than data saved into child table.
this child table having referece of this parent table. use below stored procedure.
CREATE DEFINER=`teneqs`#`localhost` PROCEDURE `xxx`()
begin
declare v_row_index int unsigned default 0;
declare v_row_count int unsigned;
declare v_xpath_row varchar(255);
declare userId int;
declare userEduInfoId int;
declare p_xml text;
declare p_xpath_row varchar(255) default '//educations[1]/education';
declare done int;
DECLARE outsourcedUserDataCursor CURSOR FOR select user_id, data from source_table where sourceType='LINKED_IN' order by user_id;
OPEN outsourcedUserDataCursor;
outsourcedUserDataCursor_loop:
LOOP FETCH outsourcedUserDataCursor INTO userId,p_xml;
Set v_row_index := 0;
-- SET done := 0;
select userId,p_xml;
-- calculate the number of row elements.
set v_row_count := extractValue(
p_xml
, concat(
'count('
, p_xpath_row
, ')'
)
);
select v_row_count as "Education Count" ;
IF v_row_count > 0 THEN
-- loop through all the row elements
while v_row_index < v_row_count do
set v_row_index := v_row_index + 1;
set v_xpath_row := concat(
p_xpath_row
, '['
, v_row_index
, ']'
);
select v_row_index, v_xpath_row;
begin
DECLARE userEduInfoCursor CURSOR FOR select id from parent where user_id= userId and school_name=extractValue(p_xml,concat(v_xpath_row,'/school-name[1]')) limit 1;
-- DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
select "Exist into Edu Info table: ",userId,extractValue(p_xml,concat(v_xpath_row,'/school-name[1]')) as 'SchoolName';
OPEN userEduInfoCursor;
-- IF done = 1 THEN
-- select done "Done";
-- END IF;
select 'userEduInfoCursor will be open';
LOOP
FETCH userEduInfoCursor INTO userEduInfoId;
select userEduInfoId;
IF userEduInfoId is NOT NULL then
select "in side if, userid is not null";
-- Insert UserEducationInfo
insert into child(
a
,b
,c
,d
,e
,f
,g
,h
,i
,j
) values(
userID
,userEduInfoId
,extractValue(p_xml,concat(v_xpath_row,'/school-name[1]'))
,'degree'
,'fieldOfStudy'
,'January'
,'2001'
,'December'
,'2014'
,'description');
ELSE
select "in side else";
close userEduInfoCursor;
-- User Info Inserted.
insert into parent (
school_name
,user_id,
creationDate,
lastmodified)
values (
extractValue(p_xml,concat(v_xpath_row,'/school-name[1]'))
,userId
,now()
,now()
);
OPEN userEduInfoCursor;
FETCH userEduInfoCursor INTO userEduInfoId;
-- Detaild Information Entered
insert into child(
a
,b
,c
,d
,e
,f
,g
,h
,i
,j
) values(
userID
,userEduInfoId
,extractValue(p_xml,concat(v_xpath_row,'/school-name[1]'))
,'degree'
,'fieldOfStudy'
,'January'
,'2001'
,'December'
,'2014'
,'description');
END IF;
END Loop;
close userEduInfoCursor;
end;
-- check userid & school name already exist into info table -> insert into details table.
end while;
END IF;
end LOOP outsourcedUserDataCursor_loop;
close outsourcedUserDataCursor;
end
I tired with this store procedure but there one issue with this. Only one single record insert into Parent table remaing other records inserted into another child table.
Please correct me where i did mistake.

Deciding addition or updation

I need to add a record if already a record with the primary key doesn't exist; otherwise existing record is to be updated. For this I am querying the db with the primary key. If no record exist, I am adding; otherwise updating. I am coding this in java using raw JDBC.
Is there a better way to do this?
insert … select … where not exist
INSERT INTO ... VALUES ... ON duplicate KEY UPDATE id = id
REPLACE INTO ... SELECT ... FROM ...
The most soft way to do this is to use special query INSERT ... ON DUPLICATE KEY UPDATE query in My Sql. It is much more effective than check is conflict exist on the application side.
Code snippet for example:
PreparedStatement statement = null;
try {
statement = connection.prepareStatement(
"INSERT INTO table (a,b,c) VALUES (?,?,?) ON DUPLICATE KEY UPDATE c=?;"
);
int paramIndex = 1;
statement.setInt(parameterIndex++, primaryKeyValue);
statement.setInt(parameterIndex++, secondValue);
statement.setInt(parameterIndex++, thirdValue);
statement.setInt(parameterIndex++, thirdValue);
int updatedCount = statement.executeUpdate();
} finally {
statement.close();
}
Another way would be REPLACE INTO which takes the same syntax as INSERT but removes the old entry when the primary key already exists before inserting.
The simplest and the most general way is to count the record before insert/update.
Pseudo Code:
SELECT COUNT(*) as recordCount FROM mytable WHERE keyField = ?
if (recordCount > 0) {
UPDATE mytable SET value1=? WHERE keyField = ?
} else {
INSERT INTO mytable (keyField, value1) VALUES (?, ?)
}

Categories

Resources