Sqlite: Insert if not exist, Update if exist - sql

I have a database with 2 tables like this:
cg_resp
id | name | email
1 | George | george#yahoo.com
id column is primary_key,autoincremented and name is unique
and
equip_info
id | description | cg_resp_id
1 | Caliper | 1
In the application form I have 2 edit boxes named edit_resp_name and edit_resp_email
If user insert a new responsible name like John with the email john#yahoo.com then during the save of form I would like to insert a new responsible into cg_resp table, get the last inserted id and update it to equip_info.cg_resp_id.
If the user maintain the name George but it's updating the email like george01#gmail.com then I would like to update the id = 1 from cg_resp with the new email address and the rest of them (equip_info.cg_resp_id and cg_resp.id) to remain the same.
I would like to maintain the original reference of cg_resp_id from table equip_info if the name of responsible is the same, so it's necessary to avoid situation like delete and insert a new one.
How can be done this in one Sqlite sql sequence?

SQLite has no built-in mechanism that would not delete the existing row.
The easiest way to do this is to put the logic into your application:
cursor = db.execute("SELECT ...")
if cursor.empty:
db.execute("INSERT ...")
else:
db.execute("UPDATE ...")
If your language allows to read the number of affected rows, it is possible to do this with two SQL commands:
db.execute("UPDATE ...")
if db.rowcount == 0:
db.execute("INSERT ...")

Use INSERT OR REPLACE which does exactly what you want : insert a new row when the name does not exists or update otherwise.

Related

What is the best SQL query to populate an existing column on table A with a similar column from table B?

Let's say I have an existing table A with a column called contact_name and a ID column id as the primary key.
All the rows in A have the name value as "NULL" right now.
Another table B has different columns, but one of which is contact_name, and another is ref_id.
Each ref_id in B corresponds to a value of id in A, and there may be multiple rows in B that share the same value for ref_id (meaning they all correspond to a single entry in A).
Let me set up an example:
Table A
id | contact_name
1 | [NULL]
2 | [NULL]
Table B
ref_id | contact_name
1 | "John"
2 | "Helen"
2 | "Alex"
Note there are theoretically other values in each table but for the sake of brevity I'm just showing the values I'm interested in using.
I want to populate contact_name in table A with the first entry of the corresponding contact_name in B, where B.(first)ref_id = A.id, without adding any rows or editing the rest of the rows in either table. That is, I want A in my example to now be:
id | contact_name
1 | "John"
2 | "Helen"
Again, note how the first contact_name value, "Helen", in B is selected, not any other subsequent one, like "Alex".
The MERGE function is made for this. Give it a try:
MERGE INTO TABLEA a USING (
SELECT b.REF_ID, b.CONTACT_NAME
FROM TABLEB) b
ON (a.ID = b.REF_ID)
WHEN MATCHED THEN
UPDATE SET
a.CONTACT_NAME = b.CONTACT_NAME
WHERE a.CONTACT_NAME IS null;
You could try using Five, a low-code development environment that lets you manage your MySQL database, and build a web application on top of it.
Whenever You can add a new column in a Table, Five will give you a prompt where you can use a query to populate that particular column.
Here are the steps:
Import your existing MySQL database into Five
Add a new column to your database.
Five will prompt you to fill in values for the new column. Five gives you four options:
A. you can leave the new column empty,
B. assigned a Default value,
C. Copy Values to an existing field
D. Write a custom query.
Refer to This Screenshot to get an idea how things look inside Five
Hope this helps!
Disclaimer: I work for Five

Postgres upsert using on conflict - multiple fields in on conflict condition

I'm trying to implement the concept of liking / disliking an item in a postgres db - when the user likes / dislikes something, I want to insert an item into my DB to represent this.
This is my schema:
id | postID | userID | type
1 2 1 like
Now if the user has already liked the item, and now they decide to dislike it - I want to update the type field, from like to dislike.
Similarly, if they've disliked something and now decided to like it, I want to perform the opposite update.
Furthermore, a user can only like / dislike something once - so if the user has liked / disliked the post previously and now decides to like / dislike it again, nothing should happen.
This means I need to implement an upsert statement in postgres, which inserts a new row, if the user has not interacted with the post previously, and updates the type field, if a row with the specified postID + userID + type already exists.
I was looking at doing this using the on conflict syntax -
INSERT INTO table_name(postID,userID,type)
VALUES(2,1,'like')
ON CONFLICT (????) DO UPDATE
SET type = 'like'
but I'm not sure what to pass into the ON CONFLICT section, since the match needs to happen on multiple fields.
I considered setting a unique index on the (postID, userID) fields - something like this:
create unique index idx_1 on table (postID, userID)
The problem is I want to use this DB in the future to store comment information, and a user is allowed to comment on the same post multiple times.
An example would be:
id | postID | userID | type
1 2 1 comment
2 2 1 comment
3 2 1 like
If you want to limit the number of updates to a row, you can use a check constraint and secondary column counting the updates.
alter table t add column num_updates int default 1 check (num_updates <= 2);
Then, if you want to prevent duplicate rows on types other than comment, you can use a filtered unique index:
create unique index unq_table_name_postid_userid
on table_name(postid, userid)
where type <> 'comment';
Then I think you can express the logic using on conflict:
INSERT INTO table_name (postID, userID, type)
VALUES(2, 1, 'like')
ON CONFLICT (postID, userID) DO UPDATE
SET type = 'like',
num_updates = excluded.num_updates + 1;
This allows only one update. You might want more refined logic, such as updating the value only if type changes:
num_update = (excluded.num_updates = num_updates)::int + 1

How to create a trigger that tracks changes to specific columns?

In a PostgreSQL database I have a table called SURVEYS which looks like this:
| ID (uuid) | name (varchar) | status (boolean) | update_at (timestamp) |
|--------------------------------------|----------------|------------------|--------------------------|
| 9bef1274-f1ee-4879-a60e-16e94e88df38 | Doom | 1 | 2019-03-26 00:00:00 |
As you can see, the table has the columns status and update_at.
My task is to create a trigger that will start the function if the user updates the value in status column to 2 and changes the value in the update_at column. In the function I would use the ID of the entry which was changed. I created such a trigger. Do you think is it correct to check column values in the trigger, or do I need to check it in the function? I am little bit confused.
CREATE TRIGGER СHECK_FOR_UPDATES_IN_SURVEYS
BEFORE UPDATE ON SURVEYS
FOR EACH ROW
WHEN
(OLD.update_at IS DISTINCT FROM NEW.update_at)
AND
(OLD.condition IS DISTINCT FROM NEW.condition AND NEW.condition = 2)
EXECUTE PROCEDURE CREATE_SURVEYS_QUESTIONS_RELATIONSHIP(NEW.id);
Your trigger looks just fine.
There is only one slight syntax problem: the whole WHEN clause has to be surrounded by parentheses.
Also, you cannot pass anything but a constant to the trigger function. But you don't have to do that at all: NEW will be available in the trigger function automatically.
So you could write it like this:
CREATE TRIGGER СHECK_FOR_UPDATES_IN_SURVEYS
BEFORE UPDATE ON SURVEYS
FOR EACH ROW
WHEN
(OLD.update_at IS DISTINCT FROM NEW.update_at
AND
OLD.condition IS DISTINCT FROM NEW.condition AND NEW.condition = 2)
EXECUTE PROCEDURE CREATE_SURVEYS_QUESTIONS_RELATIONSHIP();
It is always preferable to check conditions in the trigger definition, because that will save you unnecessary function calls.

how to insert a data into many tables

For example, I have a 3 table
Table name: Role
table Attributes: roleid (pk), rolename
role to user one to many
Table name: user
table Attributes: roleid (fk), userid(pk), trackingid(fk) username, password, email
user to tacking one to one
Table name: tracking
table Attributes: trackingid(pk) approvalstatus*, status, createdby, createdDate(yyyy-mm-dd).
*meaning of attributes
approval status - admin will approve any changes so it can be pending, approved or rejected
status is to indicated whether the change request is new user/ edit or delete user.
How do I do a insert into statement to insert a new user for approval. As, when you insert the data in the database should look like this
+----------+----------+--------------------+----------+--------+----------------+-----------+-------------+
| username | password | email | rolename | status | approvalstatus | createdby | createdDate |
+----------+----------+--------------------+----------+--------+----------------+-----------+-------------+
| harry | password | harry#yahoo.com.sg | Admin | New | Pending | Barry | 2016-09-20 |
+----------+----------+--------------------+----------+--------+----------------+-----------+-------------+
This really depends on how you're interacting with the Database.
If you're using an ORM like Entity Framework or NHibernate, this comes out of the box depending on how you map your tables.
For information on this please view:
Entity Framework
NHibernate
If you're doing this direct to SQL Server you can use the following:
Stored Procedure - In this case you'll call a single stored procedure on your DB Server, which will do three inserts for you based on your inputs.
You can perform 3 operations to the DB using a single connection.
Regardless of whether you pick a Stored Proc. or manual statement your inserts will look like:
INSERT INTO ROLE (RoleID, RoleName) VALUES (newID(), 'Your Role Name'); --If you have auto increment on your PK you can ignore inserting into RoleID. Most Systems I work with now use GUID's for ID's so this is just an example.
INSERT INTO User (RoleID, UserID, TrackingID, UserName, Password, Email) Values (....)
INSERT INTO Tracking (TrackingID, ApprovalStatus, Status, CreatedBy, CreatedDate) values (....)
Once you have an entry in your DB you can update that using:
UPDATE Tracking SET ApprovalStatus = 'whatever you want here' where id = X
IF you need to maintain history of tracking, rather than update Tracking, you need to insert a new row and always make sure when you're SELECT'ing your data, you get the latest one based on the DateTime stamp.
Your table in your question is misleading. You're joining three tables to get those results, which is maybe what you want on our output.

Keeping a column in sync with another column in Postgres

I'm wondering if it's possible to have a column always kept in sync with another column in the same table.
Let this table be an example:
+------+-----------+
| name | name_copy |
+------+-----------+
| John | John |
+------+-----------+
| Mary | Mary |
+------+-----------+
I'd like to:
Be able to INSERT into this table, using providing a value only for the name column - The name_copy column should automatically take the value I used in name
When UPDATE-ing the name column on a pre-existing row, the name_copy should automatically update to match the new & updated name_column.
Some solutions
I could do this via code but that would be terribly bad as there's no guarantee the data would always be accessible by my code (what if someone changes the data through a DB client?)
What would be a safe and reliable and easy way to tackle this in Postgres?
You can create a trigger. Simple trigger function:
create or replace function trigger_on_example()
returns trigger language plpgsql as $$
begin
new.name_copy := new.name;
return new;
end
$$;
In Postgres 12+ there is a nice alternative in the form of generated columns.
create table my_table(
id int,
name text,
name_copy text generated always as (name) stored);
Note that a generated column cannot be written to directly.
Test both solutions in db<>fiddle.
Don't put name_copy into the table. One method is to create the column and access it using a view:
create view v_table as
select t.*, name as name_copy
from t;
That said, I don't really see a use for this.