How do I add a delete statement to this code? - sqlite

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.

Related

Oracle 11g Triggers

I have create a table person(id, name ,samenamecount).The samenamecount attribute can be null but for each row can store the row count for same names.I am achieving this by calling a stored procedure inside a after insert trigger.Below is my code.
create or replace procedure automatic(s in person.name%type)
AS
BEGIN
update person set samenamecount=(select count(*) from person where name=s) where name=s;
END;
create or replace trigger inserttrigger
after insert
on person
for each row
declare
begin
automatic(:new.name);
end;
On inserting a row it is giving error like
table ABCD.PERSON is mutating, trigger/function may not see it.
Can somebody help me to figure out this?
If you have the table:
CREATE TABLE person (
id NUMBER
GENERATED ALWAYS AS IDENTITY
CONSTRAINT person__id__pk PRIMARY KEY,
name VARCHAR2(20)
NOT NULL
);
Then rather than creating a trigger, instead, you could use a view:
CREATE VIEW person_view (
id,
name,
samenamecount
) AS
SELECT id,
name,
COUNT(*) OVER (PARTITION BY name)
FROM person;
You can use the trigger:
CREATE TRIGGER inserttrigger
AFTER INSERT ON person
BEGIN
MERGE INTO person dst
USING (
SELECT ROWID AS rid,
COUNT(*) OVER (PARTITION BY name) AS cnt
FROM person
) src
ON (src.rid = dst.ROWID)
WHEN MATCHED THEN
UPDATE SET samenamecount = src.cnt;
END;
/
fiddle
If you want to make it more efficient then you could use a compound trigger and collate the names that are being inserted and only update the matching rows.

Reset rowid field after deleting rows

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.

How to "update" the _id column in SQLite Database Browser

The _id column in my database is an INTEGER PRIMARY KEY, so it is an auto-incrementing column.
The problem is that now I deleted a row, and the column didn't update the auto-incrementing number.
Is there a way to make the _id column update, so there wouldn't be holes in the sequence?
Thank you very much in advance.
No. This is not how it is intended to be used. Don't mess with the primary key! There will be gapes. The id is just a unique identifier.
If you need a rank then you can do that
select t.*, #rank := #rank + 1 as gapless_rank
from your_table t
cross join (select #rank := 0) r
order by id
To get the nth ID from the table, use a query like this:
SELECT _id
FROM MyTable
ORDER BY _id
LIMIT 1
OFFSET n-1

SQL ORACLE INSERT INTO - How to check existence before

I've the dump of a table of a database and I have to transfer the rows on a table with the same structure on a new database.
On the new table there are already some rows so I've to insert the new values only if the primary key is not already present.
As database I'm using Oracle 11.6.
...
INSERT INTO TABLE1 (KEY, FIELD1, FIELD2) VALUES ('1111111','aaa','xxx');
INSERT INTO TABLE1 (KEY, FIELD1, FIELD2) VALUES ('2222222','bbb','yyy');
INSERT INTO TABLE1 (KEY, FIELD1, FIELD2) VALUES ('3333333','ccc','zzz');
...
If the key '2222222' is already in the database how can I avoid to have an error from the execution of the query?
Thank you
You can do this for each record:
MERGE INTO TABLE1 mt
USING (
SELECT '2222222' as KEY,
'bbb' as FIELD1,
'yyy' as FIELD2
FROM dual
) t on (mt.key = t.key)
WHEN NOT MATCHED THEN
INSERT (KEY, FIELD1, FIELD2)
VALUES (t.KEY, t.FIELD1, t.FIELD2);
However, it might help to first insert everything in a temporary table TABLE1_TEMP.
then run only one merge statement:
MERGE INTO TABLE1 mt
USING (
SELECT KEY, -- more interesting
FIELD1, -- to merge all this
FIELD2 -- at once instead of
FROM TABLE1_TEMP -- just one record
) t on (mt.key = t.key)
WHEN NOT MATCHED THEN
INSERT (KEY, FIELD1, FIELD2)
VALUES (t.KEY, t.FIELD1, t.FIELD2);
Then drop TABLE1_TEMP

Is it possible to use WHERE clause in same query as PARTITION BY?

I need to write SQL that keeps only the minimum 5 records per each identifiable record in a table. For this, I use partition by and delete all records where the value returned is greater than 5. When I attempt to use the WHERE clause in the same query as the partition by statement, I get the error "Ordered Analytical Functions not allowed in WHERE Clause". So, in order to get it to work, I have to use three subqueries. My SQL looks ilke this:
delete mydb.mytable where (field1,field2) in
(
select field1,field2 from
(
select field1,field2,
Rank() over
(
partition BY field1
order by field1,field2
) n
from mydb.mytable
) x
where n > 5
)
The innermost subquery just returns the raw data. Since I can't use WHERE there, I wrapped it with a subquery, the purpose of which is to 1) use WHERE to get records greater than 5 in rank and 2) select only field1 and field2. The reason why I select only those two fields is so that I can use the IN statement for deleting those records in the outermost query.
It works, but it appears a bit cumbersome. I'd like to consolidate the inner two subqueries into a single subquery. Is this possible?
Sounds like you need to use the QUALIFY clause which is the HAVING clause for Window Aggregate functions. Below is my take on what you are trying to accomplish.
Please do not run this SQL directly against your production data without first testing it.
/* Physical Delete */
DELETE TGT
FROM MyDB.MyTable TGT
INNER JOIN
(SELECT Field1
, Field2
FROM MyDB.MyTable
QUALIFY ROW_NUMBER() (PARTITION BY Field1, ORDER BY Field1,2)
> 5
) SRC
ON TGT.Field1 = SRC.Field1
AND TGT.Field2 = SRC.Fileld2
/* Logical Delete */
UPDATE TGT
FROM MyDB.MyTable TGT
,
(SELECT Field1
, Field2
FROM MyDB.MyTable
QUALIFY ROW_NUMBER() (PARTITION BY Field1, ORDER BY Field1,2)
> 5
) SRC
SET Deleted = 'Y'
/* RecordExpireDate = Date - 1 */
WHERE TGT.Field1 = SRC.Field1
AND TGT.Field2 = SRC.Fileld2

Resources