I have below two tables. I wrote after insert trigger on employees table. If i insert the record in employees table it will insert the record in employee_audits table.
Both table have primary key column (id). suppose if you try to insert record ID value which does not exists in employees table and exists in employee_audits table, it shows the
error duplicate key value violates unique constraint "employee_audits_pkey" and also it is not inserting record in employees table. Both transaction failed.
But i want to insert the record in employees table.
CREATE TABLE employees(
id SERIAL PRIMARY KEY,
first_name VARCHAR(40) NOT NULL,
last_name VARCHAR(40) NOT NULL
);
CREATE TABLE employee_audits (
id SERIAL PRIMARY KEY,
last_name VARCHAR(40) NOT NULL,
changed_on TIMESTAMP(6) NOT NULL
)
The trigger function:
CREATE OR REPLACE FUNCTION log_last_name_changes()
RETURNS trigger AS
$BODY$
BEGIN
INSERT INTO employee_audits(last_name,changed_on)
VALUES(NEW.last_name,now());
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
The trigger definition:
CREATE TRIGGER last_name_changes
AFTER INSERT
ON employees
FOR EACH ROW
EXECUTE PROCEDURE log_last_name_changes();
INSERT INTO employees (first_name, last_name)
VALUES ('John', 'Doe');
SELECT * FROM EMPLOYEES
id first_name last_name
1 "John" "Doe"
SELECT * FROM EMPLOYEE_AUDITS
ID last_name CHANGED_ON
1 "Doe" "2019-12-27 17:21:13.934"
Manual insert on second table
insert into employee_audits values(2,'banu','2019-12-27 17:21:13.934')
Manual insert on first table
INSERT INTO employees (first_name, last_name)
VALUES ('David', 'Raj');
Error duplicate key value violates unique constraint "employee_audits_pkey"
is it possible to insert record in employees table?
If you have a serial column you should never provide the value for it manually. Manually providing a value for a serial will not advance the sequence behind that column, so the next time you insert without specifying the id column, the next sequence value will be taken which is 2 as the sequence was only advanced once.
So instead of:
insert into employee_audits values(2,'banu','2019-12-27 17:21:13.934')
Just use:
insert into employee_audits (last_name, changed_at)
values ('banu','2019-12-27 17:21:13.934');
This behaviour of the serial columns is one of the reasons why it's highly recommended to use identity columns with modern Postgres versions.
I need to understand how to add a statement that will delete the results of the following query:
I understand that a DELETE statement with a WHERE clause would normally be used, but because I'm SELECTING two different columns, the where clause doesn't accept the comma. I've not yet been able to figure out how to turn this into a CTE (but maybe that's overkill?) and then call it in a DELETE statement later (assuming that's even an option). Examples always have the DELETE FROM..., or WHICH statements, neither of which seem implementable under this code. Do I have to rewrite my code so it includes a WHICH statement?
SELECT field1, field2
FROM table
GROUP BY field1, field2
HAVING SUM(field3) IS NULL
Expecting to be able to institute a DELETE statement to delete the results of the query.
I believe that you could use (assuming that the table is not a WITHOUT ROWID table) :-
DELETE FROM mytable WHERE rowid IN (SELECT rowid FROM mytable GROUP BY field1, field2 HAVING SUM(field3) IS NULL);
An alternative using a CTE (where the CTE has been given the name deletions) would be :-
WITH deletions(rowid) AS (SELECT rowid
FROM mytable
GROUP BY field1, field2
HAVING SUM(field3) IS NULL
)
DELETE FROM mytable WHERE rowid IN (SELECT rowid FROM deletions);
note that mytable has been used as the table name instead of table.
Considering the comment
Primary key is field1
then :-
DELETE FROM mytable WHERE field1 IN (SELECT field1 FROM mytable GROUP BY field1, field2 HAVING SUM(field3) IS NULL);
could be used, this would then work whether or not the table is defined as a WITHOUT ROWID table, a similar change could be applied to the CTE version.
Notes
Using GROUP BY on a PRIMARY KEY, as it is UNIQUE, will result in as many groups and therefore rows, as there are rows. Effectively the query could be SELECT field1 FROM mytable WHERE field3 IS NULL and therefore the deletion could simply be DELETE FROM mytable WHERE field3 IS NULL.
If this were not the case and field1 was not the PRIMARY KEY, then the complication is that values per group that are not aggregated values are values from an arbitrarily selected row. In short you would delete 1 from a number of the rows that where grouped.
I've got two tables: tableA and tableB. I would like to insert random row from tableA to tableB and then delete this row from tableA. How can i do it? Is it possible at all? Below is my insert code but i dont know how to delete this row.
INSERT INTO tableB
SELECT * FROM tableA ORDER BY RANDOM() LIMIT 1;
You can use rowid:
DELETE from tableA
WHERE elementA = (
SELECT elementA from tableB
WHERE rowid = (SELECT MAX(rowid) FROM tableB)
)
Provided that elementA is unique in both tables, if this is executed after your INSERT statement it will find elementA from the inserted row and delete the row from TableA with that elementA.
I'm using an sqlite database, and i want to reset rowid field after deleting rows. my table doesn't have a primary key or a specified Id, i'm only using the default rowid given by sqlite.
NOM TYPE CL VAL SP TP
"test1" "test1" "1" "1" "test1" "test1"
"test2" "test2" "2" "2" "test2" "test2"
"test3" "test3" "3" "3" "test3" "test3"
When i delete one or multiple rows, i want the default rowid to reset automatically, so i have read some answers and found that we can do that by using sql_sequence table .
delete from sqlite_sequence where name='table_name'
the problem is that i can't find that table in my sqlite
[ no such table: sqlite_sequence ]
Is there any solution to my problem
A table by default will have a rowid that does not use sqlite_sequence table.
It is only when an alias for the rowid is specified (defined using column_name INTEGER PRIMARY KEY or column_name INTEGER PRIMARY KEY AUTOINCREMENT) AND that the definition of the alias includes the optional AUTOINCREMENT keyword/phrase, that the algorithm for determining the rowid utilises the sqlite_sequence table to ensure that the rowid is greater than the last used rowid (typically it will be greater anyway).
Even if you used AUTOINCREMENT and thus the sqlite_sequence table existed and you deleted the row for the table from the sqlite_sequence table. This would not reset the sequence as I believe the algorithm used uses the higher of the highest existing rowid in the table and the value obtained from the sqlite_sequence table. here's a post I made in realtion to this
There is likely very little, or no, reason to utilise the rowid as if it's something other than the means to uniquely identify a row.
However, if you really want to manage the rowid then you can specifically set this value by specifying the rowid column (noting that there is an implied UNIQUE constraint).
So you could for example use :-
INSERT INTO yourtable (rowid, NOM, TYPE, CL, VAL, SP, TP) VALUES(123,'MyName','Testx', '123', '123', 'Testx', 'Testx');
rowid will be set to 123
You could also use update a rowid e.g. :-
UPDATE yourtable SET rowid = 5 WHERE rowid = 123;
- rowid that was 123 is changed to 5 unique constraint permitting
You could obtain the highest rowid using:-
SELECT max(rowid) from yourtable;
You could even use :-
INSERT INTO yourtable (rowid, NOM, TYPE, CL, VAL, SP, TP) VALUES((SELECT max(rowid) from yourtable)+1,'MyName','maxx', '???', '???', 'Maxx', 'Maxx');
insert with the next rowid sequence.
Note the above examples are without an alias of the rowid, obviously they not rely upon the non-existant sqlite_sequence table.
Of course you could always have your own equivalent of the sqlite_sequence table, just that it CANNOT be named this nor could it's name start with sqlite. Note! this would be the only persistent way using SQLite of resetting the id, but could be a nightmare
All the above assumes that :-
and i want to reset rowid field after deleting rows
is not ....after deleting ALL rows, as if this were the case the rowid would be effectively reset (basically without AUTOINCREMENT rowid is determined according to the highes existing rowid, so if none exist then the rowid will be 1).
See example 2 below.
NOTE!!
This answer is not condoning or recommending any of the above.
Example 1
using the following :-
CREATE TABLE IF NOT EXISTS rowidtable (NOM TEXT, TYPE TEXT, CL TEXT, VAL TEXT, SP TEXT, TP TEXT);
INSERT INTO rowidtable (rowid) VALUES(10);
INSERT INTO rowidtable (rowid) VALUES(12);
INSERT INTO rowidtable (rowid, NOM, TYPE, CL, VAL, SP, TP) VALUES(123,'MyName','Testx', '123', '123', 'Testx', 'Testx');
INSERT INTO rowidtable (NOM, TYPE, CL, VAL, SP, TP) VALUES('MyName','Testx', '123', '123', 'Testx', 'Testx');
UPDATE rowidtable SET rowid = 5 WHERE rowid = 123;
SELECT max(rowid) from rowidtable;
--// Following repteated 3 times (so rowid's 125,126 and 127 used respectively)
INSERT INTO rowidtable (rowid, NOM, TYPE, CL, VAL, SP, TP) VALUES((SELECT max(rowid) from rowidtable)+1,'MyName','maxx', '???', '???', 'Maxx', 'Maxx');
select rowid,* from rowidtable;
The resultant table is :-
Rows with id's 10 and 12 were inserted first (nulls (red) for other values)
Row with id 5 was originally row with id 123
Rows with id's 125, 126 and 127 were inserted using the rowid as obtained via subquery that adds 1 to the maximum current rowid (i.e. last insert run 3 times).
Example 2
If the following were then used :-
DELETE from rowidtable
INSERT INTO rowidtable (NOM) VALUES('after mass delete');
Then the resultant table would be :-
The sqlite_sequence table is only used for tables that have a INTEGER PRIMARY KEY AUTOINCREMENT column defined.
If you are relying on the value of the internal defaultrowid for a given row in a table, then you should define a named autoincrement primary key for that table, then you can reset the counter using the sqlite_sequence table.
Thanks for all your answers i found what i was looking for and it quite simple
DELETE FROM 'table_name' WHERE col='value';
REINDEX 'table_name';
and this will rebuild the specified table index, so if all the rows are deleted the index is reset to 0, and if is still row in the table, the index will be reordred correctly.
I want to create a table with a field that is unique and limited to a certain value. Lets say that the limit is 100, the table is full, I remove a random row, and when I create a new row it has the value that was freed before.
It doesn't need to be the fastest thing in the world (the limit is quite small), I just want to implement it in a DB.
Any ideas?
Create one more column in main table, say deleted (integer, 0 or 1). When you need to delete with certain id, do not really delete it, but simply update deleted to 1:
UPDATE mytable SET deleted=1 WHERE id = <id_to_delete>
When you need to insert, find id to be reused:
SELECT id FROM mytable WHERE deleted LIMIT 1
If this query returns empty result, then use INSERT to create new id. Otherwise, simply update your row:
UPDATE mytable SET deleted=0, name='blah', ... WHERE id=<id_to_reuse>
All queries reading from your main table should have WHERE constraint with NOT deleted condition:
SELECT * FROM mytable WHERE NOT deleted
If you add index on deleted, this method should work fast even for large number of rows.
This solution does everything in a trigger, so you can just use a normal INSERT.
For the table itself, we use an autoincrementing ID column:
CREATE TABLE MyTable(ID INTEGER PRIMARY KEY, Name);
We need another table to store an ID temporarily:
CREATE TABLE moriturus(ID INTEGER PRIMARY KEY);
And the trigger:
CREATE TRIGGER MyTable_DeleteAndReorder
AFTER INSERT ON MyTable
FOR EACH ROW
WHEN (SELECT COUNT(*) FROM MyTable) > 100
BEGIN
-- first, select a random record to be deleted, and save its ID
DELETE FROM moriturus;
INSERT INTO moriturus
SELECT ID FROM MyTable
WHERE ID <> NEW.ID
ORDER BY random()
LIMIT 1;
-- then actually delete it
DELETE FROM MyTable
WHERE ID = (SELECT ID
FROM moriturus);
-- then change the just inserted record to have that ID
UPDATE MyTable
SET ID = (SELECT ID
FROM moriturus)
WHERE ID = NEW.ID;
END;