When using Supabase, is there a clean / simple way in which I can delete all associated records together with an item? Say I have Posts and a Post can have many Comments. When I delete the Post I want to delete the Comments as well.
Something like dependend: :destroy in Rails, basically.
const { data, error } = await supabase
.from('posts')
.delete()
.match({ id: 123 })
.options({ destroyDependent: true }) // set some option that tells Supabase to delete all associated records as well.
Yes, there is!
The magic happens not when you delete the data, but when you first create your comments table. It's also not a Supabase feature, but rather a postgres feature.
When you create a foreign key constraint, you can set a delete cascade option to tell the table to delete any related data.
For example, you can create a comments table like this:
create table if not exists public.comments (
id uuid not null primary key DEFAULT uuid_generate_v4 (),
post_id uuid references public.posts on delete cascade not null,
user_id uuid references public.users on delete cascade not null,
created_at timestamp with time zone default timezone('utc' :: text, now()) not null,
text varchar(320) not null
);
Notice the delete cascade keyword on post_id and user_id definition. Adding these will delete the comment entry if the related post or user is deleted.
Currently, there is no way of creating a column with delete cascade option in Supabase UI, so you would have to create such table using SQL.
Also, if you already have a table and would like to add this delete cascade option, you would have to remove the foreign key constraints and re-add it with delete cascade option. You can find out more about how to add delete cascade to an existing table here, but if your app is not in production, it might be easier to just delete the table and re-create it from scratch!
Edited
If you scroll a bit more to see the answer by #Justus Blümer, you can see how you can alter an existing table to add delete cascade!
Based on Tyler's tip here's how to change an existing key constraint to allow for a cascading deletion.
List all your foreign key constraints by executing this in the SQL editor in Supabase.
From there, identify the key that you need to change. Insert the name of the table with the items that you'd like to delete based on the deletion of a parent item in another table:
SELECT con.*
FROM pg_catalog.pg_constraint con
INNER JOIN pg_catalog.pg_class rel
ON rel.oid = con.conrelid
INNER JOIN pg_catalog.pg_namespace nsp
ON nsp.oid = connamespace
WHERE 1=1
AND rel.relname = 'YOUR_CHILD_TABLE';
Change the appropriate foreign key constraint (here: child_parent_id_fkey to support cascading deletes:
ALTER TABLE public.YOUR_CHILD_TABLE
DROP CONSTRAINT child_parent_id_fkey,
ADD CONSTRAINT child_parent_id_fkey
FOREIGN KEY (parent_id)
REFERENCES YOUR_PARENT_TABLE(id)
ON DELETE CASCADE;
This is what worked for me if you already have a table and need to alter it. in REFERENCES you need to mention 'public' before table. See example to understand
alter table
public.child drop constraint child_parent_id_fkey,
add
constraint child_parent_id_fkey
foreign key (parent_id)
references public.parent(id) on delete cascade;
I've searched around but didn't find if it's possible.
I've this MySQL query:
INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8)
Field id has a "unique index", so there can't be two of them. Now if the same id is already present in the database, I'd like to update it. But do I really have to specify all these field again, like:
INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8)
ON DUPLICATE KEY UPDATE a=2,b=3,c=4,d=5,e=6,f=7,g=8
Or:
INSERT INTO table (id,a,b,c,d,e,f,g) VALUES (1,2,3,4,5,6,7,8)
ON DUPLICATE KEY UPDATE a=VALUES(a),b=VALUES(b),c=VALUES(c),d=VALUES(d),e=VALUES(e),f=VALUES(f),g=VALUES(g)
I've specified everything already in the insert...
A extra note, I'd like to use the work around to get the ID to!
id=LAST_INSERT_ID(id)
I hope somebody can tell me what the most efficient way is.
The UPDATE statement is given so that older fields can be updated to new value. If your older values are the same as your new ones, why would you need to update it in any case?
For eg. if your columns a to g are already set as 2 to 8; there would be no need to re-update it.
Alternatively, you can use:
INSERT INTO table (id,a,b,c,d,e,f,g)
VALUES (1,2,3,4,5,6,7,8)
ON DUPLICATE KEY
UPDATE a=a, b=b, c=c, d=d, e=e, f=f, g=g;
To get the id from LAST_INSERT_ID; you need to specify the backend app you're using for the same.
For LuaSQL, a conn:getlastautoid() fetches the value.
There is a MySQL specific extension to SQL that may be what you want - REPLACE INTO
However it does not work quite the same as 'ON DUPLICATE UPDATE'
It deletes the old row that clashes with the new row and then inserts the new row. So long as you don't have a primary key on the table that would be fine, but if you do, then if any other table references that primary key
You can't reference the values in the old rows so you can't do an equivalent of
INSERT INTO mytable (id, a, b, c) values ( 1, 2, 3, 4)
ON DUPLICATE KEY UPDATE
id=1, a=2, b=3, c=c + 1;
I'd like to use the work around to get the ID to!
That should work — last_insert_id() should have the correct value so long as your primary key is auto-incrementing.
However as I said, if you actually use that primary key in other tables, REPLACE INTO probably won't be acceptable to you, as it deletes the old row that clashed via the unique key.
Someone else suggested before you can reduce some typing by doing:
INSERT INTO `tableName` (`a`,`b`,`c`) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE `a`=VALUES(`a`), `b`=VALUES(`b`), `c`=VALUES(`c`);
There is no other way, I have to specify everything twice. First for the insert, second in the update case.
Here is a solution to your problem:
I've tried to solve problem like yours & I want to suggest to test from simple aspect.
Follow these steps: Learn from simple solution.
Step 1: Create a table schema using this SQL Query:
CREATE TABLE IF NOT EXISTS `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(30) NOT NULL,
`password` varchar(32) NOT NULL,
`status` tinyint(1) DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `no_duplicate` (`username`,`password`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;
Step 2: Create an index of two columns to prevent duplicate data using following SQL Query:
ALTER TABLE `user` ADD INDEX no_duplicate (`username`, `password`);
or, Create an index of two column from GUI as follows:
Step 3: Update if exist, insert if not using following queries:
INSERT INTO `user`(`username`, `password`) VALUES ('ersks','Nepal') ON DUPLICATE KEY UPDATE `username`='master',`password`='Nepal';
INSERT INTO `user`(`username`, `password`) VALUES ('master','Nepal') ON DUPLICATE KEY UPDATE `username`='ersks',`password`='Nepal';
Just in case you are able to utilize a scripting language to prepare your SQL queries, you could reuse field=value pairs by using SET instead of (a,b,c) VALUES(a,b,c).
An example with PHP:
$pairs = "a=$a,b=$b,c=$c";
$query = "INSERT INTO $table SET $pairs ON DUPLICATE KEY UPDATE $pairs";
Example table:
CREATE TABLE IF NOT EXISTS `tester` (
`a` int(11) NOT NULL,
`b` varchar(50) NOT NULL,
`c` text NOT NULL,
UNIQUE KEY `a` (`a`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;
I know it's late, but i hope someone will be helped of this answer
INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6)
ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);
You can read the tutorial below here :
https://mariadb.com/kb/en/library/insert-on-duplicate-key-update/
http://www.mysqltutorial.org/mysql-insert-or-update-on-duplicate-key-update/
You may want to consider using REPLACE INTO syntax, but be warned, upon duplicate PRIMARY / UNIQUE key, it DELETES the row and INSERTS a new one.
You won't need to re-specify all the fields. However, you should consider the possible performance reduction (depends on your table design).
Caveats:
If you have AUTO_INCREMENT primary key, it will be given a new one
Indexes will probably need to be updated
With MySQL v8.0.19 and above you can do this:
mysql doc
INSERT INTO mytable(fielda, fieldb, fieldc)
VALUES("2022-01-01", 97, "hello")
AS NEW(newfielda, newfieldb, newfieldc)
ON DUPLICATE KEY UPDATE
fielda=newfielda,
fieldb=newfieldb,
fieldc=newfieldc;
SIDENOTE: Also if you want a conditional in the on duplicate key update part there is a twist in MySQL. If you update fielda as the first argument and include it inside the IF clause for fieldb it will already be updated to the new value! Move it to the end or alike. Let's say fielda is a date like in the example and you want to update only if the date is newer than the previous:
INSERT INTO mytable(fielda, fieldb)
VALUES("2022-01-01", 97)
AS NEW(newfielda, newfieldb, newfieldc)
ON DUPLICATE KEY UPDATE
fielda=IF(fielda<STR_TO_DATE(newfielda,'%Y-%m-%d %H:%i:%s'),newfielda,fielda),
fieldb=IF(fielda<STR_TO_DATE(newfielda,'%Y-%m-%d %H:%i:%s'),newfieldb,fieldb);
in this case fieldb would never be updated because of the <! you need to move the update of fielda below it or check with <= or =...!
INSERT INTO mytable(fielda, fieldb)
VALUES("2022-01-01", 97)
AS NEW(newfielda, newfieldb, newfieldc)
ON DUPLICATE KEY UPDATE
fielda=IF(fielda<STR_TO_DATE(newfielda,'%Y-%m-%d %H:%i:%s'),newfielda,fielda),
fieldb=IF(fielda=STR_TO_DATE(newfielda,'%Y-%m-%d %H:%i:%s'),newfieldb,fieldb);
This works as expected with using = since fielda is already updated to its new value before reaching the if clause of fieldb... Personally i like <= the most in such a case if you ever rearrange the statement...
you can use insert ignore for such case, it will ignore if it gets duplicate records
INSERT IGNORE
... ; -- without ON DUPLICATE KEY
Right now I am coding in PHP & HTML
I need to make a selection from the checkbox group
and it goes like this:
The value in the dropdown menu is retrieved from the value stored in the database in table subject. So the checkbox options will appears as (for example) Mathematics, Algebra, Calculus, English, Linguistic etc. -- I have no problem with displaying this one.
Table subject
subject_id subject_name
========== ============
1 Mathematics
2 Algebra
3 Calculus
4 English
5 Linguistic
So after that, a student can choose their subject and it will be stored in another table named student_subject where it stores the student_id and the subject_id (to store what subjects did a student chose)
Example:
table student_subject
student_id subject_id
========== ===========
1 1
1 2
1 3
2 1
2 3
Now, the student comes again to add another subject to his profile. The same checkbox option will be displayed as mentioned earlier in (1) but I would love to disabled/remove/not display the already selected option from being selectable again to avoid redundancy.
I really have no idea on how to create this function. I know there are a lot of experienced programmers and developera here so I wish you could show me the right way. Your guidance is highly appreciated.
You should not remove/disabled/not display the existing subjects.
First, you should check for existing rows for student_subject table and render it in HTML checkbox as selected.
Second, while student makes changes for subject selection in edit mode, check with the database rows with student and subject ids in a loop.
If submitted subject id for the student is not available in the database, insert that value to the table.
If submitted subject id for the student is available in the database, do nothing with it.
Check for the removed subject from submitted values with the database, and delete from the database.
If you want to disable existing subjects, use readonly to the checkbox if the subject is assigned to the student.
This is just an idea:
On your #2 where a table will store student_id and subject_id, I think it's best that whenever you store value to subject_id- you just serialize the value by storing it on an php array in that way you can iterate the whole array if the field was checked or not.
Json_encode is also good since it will be a string and same as serialize
Example:
Student_id Subject_ID
999-0518 a:{this is the serialize value };
Then when you query the result it will be serialize, then use unserialize function and do a loop to iterate the results, same goes when you json_encode; just perform json_decode() function and perform loop.
You can use a query which only picks up subjects which ID isn't in student_subject.
It would look something like:
SELECT * FROM `subject`
WHERE `subject_id` NOT IN (
SELECT `subject_id` FROM `student_subject` WHERE `student_id` = '1'
)
(you can ommit the inner where clause if you don't want this to be user specific)
and then build the <select> element based on the array returned from it.
I am working in Web Api & MVC using angularjs for CURD operations,
In my DB I have a table "Accounts" it has a column with Name "ID" which will insert as
1 for first record and 2 for second record etc...
This column values will increment as per the last record in table,
this process should be in client side only.
Thanks in advance
If I understand your question correctly, you want to send next ID from db to client, create a new record on client with that ID and update it in db.
1) All you need to do is to create a empty record in db and send it's id/primary key to the client. This approach has a potential problem that, if the client stops/chooses to cancel the operation, there will be lot of empty records created.
2) Otherwise, you can fetch the last record from client using select max(id) from table and then use id+1 for the new record. The problem with this approach is that when multiple clients try to update db at the same time, all of them will/may have the same id.
3) In order to overcome the problem of (2), you can use locking mechanisms, but thats not how you do it. Its not worth it.
In my opinion, in most of the cases, client doesn't need to know the id of the going to be created record. Once the record is created, you can send it from db.
Once you're within the table, do everything in there. It's faster an easier.
UPDATE table1 SET column = (SELECT DISTINCT MAX(id) FROM Accounts WHERE user = ? GROUP BY id DESC LIMIT 1 ) WHERE user = ?