Loop violating Primary Key Value - function

I have a part of my function mentioned below.
When I execute the function I get an error saying primary key violation for alarm_id
SELECT COUNT(DISTINCT exception_sub_type) INTO v_count FROM mdas.alarm_configuration;
IF v_count > 0 THEN
FOR i IN 1..v_count
LOOP
SELECT ((MAX(alarm_id))+1) INTO v_alarm_id FROM mdas.alarm_configuration;
RAISE NOTICE 'ALARM ID (%)',v_alarm_id;
INSERT INTO mdas.alarm_configuration
(alarm_id,
exception_sub_type,
exception_type,
from_range,
groups,
priority,
sub_group,
to_range,
org_unit_id,
status)
SELECT DISTINCT ON(exception_sub_type) v_alarm_id,
exception_sub_type,
exception_type,
from_range,
v_group_name,
priority,
v_subgroup_name,
to_range,
v_org_unit_id,
status
FROM mdas.alarm_configuration
WHERE groups = v_group_name AND mdas.alarm_configuration.org_unit_id = v_org_unit_id;
END LOOP;
END IF;
Note: This is only a part of the function. The function has three arguments v_org_unit_id, v_group_name and v_subgroup_name. My primary key is on alarm_id, which I want to increment by 1 and insert based on the MAX which is available in the table.

In your environment, when more than two client call the function at the same time, they will see the same alarm_id, so raise unique violated.
for exp :
SESSION A :
select max(id) into v_id from test;
insert into test values (v_id);
.. not end else.
SESSION B :
select max(id) into v_id from test; -- this v_id will same as session a.
insert into test values (v_id);
and then session A commit;
SESSION B will raise error.
ERROR: duplicate key value violates unique constraint "test_pkey"
DETAIL: Key (id)=(?) already exists.
You can use PostgreSQL serial deal this problem.
LIKE :
digoal=# create table test (id serial primary key, other_cols type ...);
CREATE TABLE
and not need to use max(id), you only need type other columns's values.
like
insert into test (other_cols,...) values (...);

Related

How to insert if not exists with selecting from same table?

I have my table schema in H2 db as follows:
create table if not exists Test ( id bigint not null,name varchar(255), primary key (id) );
alter table Test add constraint if not exists Test_NAME UNIQUE (name);
I want to insert a value for the name attribute as 'Default' if it does not exist in the table by selecting the latest id value from the table and increment it by one.
Example:
Do not insert if an entry for name = Default already exists.
ID | Name
1 | Default
Insert if an entry for name = Default does not exists.
ID | Name
1 | ABC
2 | XYZ
For the id column, find the max id and increment it by one. In this case, insert id=3 and name=Default.
My query is as follows:
INSERT INTO Test (id , name)
SELECT max(id) + 1, 'Default' from Test
WHERE NOT EXISTS (SELECT * FROM Test where name='Default');
However, it gives me an error saying:
NULL not allowed for column "ID"; SQL statement
as it applies the where condition on the inner select statement.
I also tried:
MERGE INTO Test KEY(name) VALUES (SELECT MAX(id) + 1 from Test, 'Default');
It gives an error because, merge tries to update with the new values.
If it finds 'Default', it will update the row with new id causing primary key violation.
Is there a better way to do this? How can I make the query work?
You are massively overcomplicating this. Define the id field as auto increment and place a unique index on the name field. The unique index prevents duplicate names to be inserted, while the auto increment increases the value of the id field by 1 (by default) if the insert is successful.
I updated id to auto increment and the following query work flawlessly
INSERT INTO Test (name) select * from (select 'Default') as tmp WHERE NOT EXISTS (SELECT name from Test where name='Default');
when you run your query first time, no record found in table so, it give error 'null' there, so if you add IFNULL() function there as below
INSERT INTO Test (id , name)
SELECT **IFNULL**(max(id),0) + 1, 'Default'
FROM Test
WHERE NOT EXISTS (SELECT * FROM Test where name='Default');

Script/workflow insert not exist and auto increment

I create a script/workflow exportation/importation from 2 system.
I have Table1 {id, name, description}
I want to create a script (not a procedure). I could (I didnt succed) adding procedure into my workflow. (create and delete at the end)
id is auto increment
I cant change the table
I can be sure that between the time I start execution of my script and the end, there will not be an insertion of one of my items into the database.
The script insert {name,description} but I want to NOT insert if the element (name or name and description) is there.
BASE QUERY :
INSERT INTO TABLE1 (name,description) VALUES ('itemX','this is item X')
BASE Script :
Use database1;
BEGIN;
SELECT * FROM TABLE1 ;
SELECT * FROM TABLE3 ;
INSERT INTO TABLE1 (name,description) VALUES ('itemX','this is item X');
set #idTable1 = LAST_INSERT_ID();
INSERT INTO TABLE3 (idTable1,idTable2) VALUES (#idTable1,1);
INSERT INTO TABLE3 (idTable1,idTable2) VALUES (#idTable1,2);
SELECT * FROM TABLE1 ;
SELECT * FROM TABLE3 ;
ROLLBACK;
I want to protect the multiple insertion on TABLE1. But without changing the table.
Maybe I did it wrong
I tried IF but not working outside procedure.
I tried IGNORE (valid only if id is the same, but never the same, its
auto increment)
I tried WHEN
I tried ON DUPLICATE KEY
Because of #idTable1, I will need change the " set #idTable1 = LAST_INSERT_ID();" if I doesnt have if else. But if my item is the only one with the same "name", I can get this instead of last_insert_id.
I opted for creating procedure before my "BEGIN" and removed them at the end of the script.
Just create the table with name as primary key, then be sure that you take care of the key capitalization (uppercase or lowercase) to avoid duplicates.
CREATE TABLE TABLE1(
id INT AUTO_INCREMENT,
name CHAR(30),
description CHAR(100),
PRIMARY KEY (name)
)
create unique constraint on name field if possible.
Otherwise, create trigger before insert in order to ignore duplicate insertion.
Trigger for checking duplicate on two fields a and b:
delimiter //
drop trigger if exists aborting_trigger //
create trigger aborting_trigger before insert on t
for each row
begin
set #found := false;
select true into #found from t where a=new.a and b=new.b;
if #found then
signal sqlstate '45000' set message_text = 'duplicate insert';
end if;
end //
delimiter ;
The trigger here provides feature similar to unique constraint. After creation you should use INSERT IGNORE or INSERT ...ON DUPLICATE KEY UPDATE

Mysql: most efficient way to get the ID from an attempted insertion?

I'm inserting some_data (a unique key column), and then using the resulting user_id (the primary key auto-increment column) in a separate statement (not shown)
INSERT IGNORE INTO users (some_data) VALUES ('test');
SELECT LAST_INSERT_ID(); <--- I do stuff with this.
But, of course, if some_data already exists (happens very frequently), LAST_INSERT_ID() returns 0. What is the best way to get the user_id based on the unique key some_data, in this case? Of course I can do a separate WHERE query, but not sure that is the most efficient.
From http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
INSERT INTO users ( id, some_col ) VALUES (n,some_val)
ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id), some_col=some_val;
Not an ignore but might do the job?
Edit:
To be clear, this will update some_col with some_val and then set the LAST_INSERT_ID to return the id of the duplicate row.
It could just as well be this if you didn't want to update any data on the duplicate but just set the LAST_INSERT_ID() call to give you what you want:
INSERT INTO users ( user_name ) VALUES ( 'bobloblaw' )
ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID( id );
Edit 2:
Use a proc to do the work and get back the id
DELIMITER $$
CREATE PROCEDURE insert_test( val1 varchar(10), val2 varchar(10) )
BEGIN
INSERT INTO test.test_table ( col1, col2 ) SELECT val1, val2 FROM ( select 1 ) as a
WHERE NOT EXISTS (
select 1 from test.test_table t where t.col1 = val1
);
SELECT id FROM test.test_table where col1 = val1;
END $$
DELIMITER ;

Accidentally deleted row from table with primary key

If you "accidentally" (Be gentle, it's been a tough morning) delete a row from a table that has a primary key, is it possible to re-insert the record with it's previous key, even if the table auto increments the primary key?
TSQL I've got a really weird environment - very restricted in some ways and sadly open in others. Won't allow insert of already used PK values, had to recreate the whole table...
/****
create protoTable w/ same structure as your mainTable that has the data you are trying to fix in this example the fieldname of the primary key is FldPK
assumption here is that your primary key is getting auto incremented
get a list of the fields in the mainTable that are NOT NULL.
in this example those fields are <not null fields>
get a list of all fields in the mainTable
in this example <all fields>, rather than spell out the fields. DO NOT INCLUDE the primary key field name
***/
declare #x int, #y int, #iLast int
select #iLast = (select MAX( FldPK ) from mainTable)
set #x = 1
while #x <= #iLast
begin
select #y = (select COUNT(*) from mainTable where FldPK = #x)
if #y = 1
begin
insert into protoTable(<all fields>)
select <all fields> from mainTable where FldPK = #x
end
else
begin
insert into protoTable (<not null fields> )values('N','xyz'+convert(varchar,#x)) /*or whatever values are valid to fulfill not null*/
/* this is where you keep one or more of the missing rows to update later with the lost data */
if #x <> 126
begin
delete protoTable where FldPK = #x
end
end
set #x=#x+1
end
Then rename the mainTable to backup and rename protoTable to mainTable
hth
The AUTO_INCREMENT feature will assign a value if one is not provided in the INSERT or REPLACE statement. Otherwise, if you supply a value and it does not conflict with the existing keys, it will be accepted as-is.

mysql triggers simulating assertions

Let’s consider table
Video(
IDvideo(PK),
Date,
Description,
User
)
with mysql I have no way of writing assertions.
Is it possible to simulate the following assertion using one or more triggers ?
create assertion asser1
check (0 =
( select count(*)
from Video
where Date >= DATE_SUB(current_date(),INTERVAL 1 YEAR )
&& Date<=current_date()
group by User
having count(*) > 200
)
)
how should I write that trigger?
Well, the problem is that MySQL doesn't have an equivalent of a STOP ACTION command. So basically, the work arounds are quite dirty:
One way is that you can violate a constraint inside the trigger to bubble an error and cancel the insert:
CREATE TABLE stop_action (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(35),
UNIQUE KEY (id, name)
);
INSERT INTO stop_action (1, 'Assert Failure');
Then, in the trigger, just try to:
INSERT INTO stop_action (1, 'Assert Failure');
The benefit of that, is that the error that's returned will be a duplicate key error, and the text will include "Assert Failure".
So then your trigger would become:
delimiter |
CREATE TRIGGER asser1_before BEFORE INSERT ON test1
FOR EACH ROW BEGIN
SELECT count(*) INTO test FROM (select count(*)
from Video
where Date >= DATE_SUB(current_date(),INTERVAL 1 YEAR )
&& Date<=current_date()
group by User
having count(*) > 200);
IF test != 0 THEN
INSERT INTO stop_action (1, 'Assert Failure');
END IF;
END;
|
delimiter ;
Now, you'd need to do this before UPDATE as well, otherwise you could update the date into an invalid state. But otherwise, that should at least get you started...