SQL: How to update or insert if doesn't exist? - sql

I have a SQL insert below, which works fine, however I would like it to check if DATE=xxxx, NAME =xxxx and JOB = xxx and update HOURS if they exist otherwise insert new row. IS this possible with SQL?
"INSERT INTO TABLE (NAME, DATE, JOB, HOURS) VALUES ('BOB', '12/01/01', 'PM','30');
Trying the below OR REPLACE with the same results, a new line is added each time.
add_time = conn.prepareStatement("INSERT OR REPLACE INTO RESOURCE (NAME, DATE, JOB, HOURS) VALUES ('"+name+"', '" + date + "', '"+job+"','"+hours+"');");
For example:
if the below was in the DB, and John wanted to update his hours, it would check name, date, job were the same as the values trying to insert and if they are update HOURS only. Otherwise if none of them existed together ( John may have hours logged against another DATE or JOB) insert a new row.
Also others will also log their hours and different roles in the same DB as below.
John | 12/12/2012 | Cleaner | 20
John | 12/12/2012 | ceo | 10
Jim | 12/10/2011 | Cleaner | 5

You can use REPLACE INTO like this:
REPLACE INTO mytable (NAME, DATE, JOB, HOURS)
VALUES ('BOB', '12/01/01', 'PM','30')
But, you must create unique index for this to work:
CREATE UNIQUE INDEX myindex
ON mytable(NAME, DATE, JOB)
Note, that you should use such combination of fields for unique index that will determine if two rows are considered the same and should be replaced rather than inserted.
SQLFiddle Demo.
Note that if you comment out unique index creation, it stops working correctly.

I think this has been asked here before for sqlite:
INSERT IF NOT EXISTS ELSE UPDATE?
seems like they have a syntax for that:
INSERT OR REPLACE INTO TABLE (NAME, DATE, JOB, HOURS) VALUES ('BOB', '12/01/01', 'PM','30');

Related

Writing a Trigger to find and fill the age of a patient whenever a patient record is inserted into patients table

In this question, I have to add value of age with the help of trigger.
please help me implement this question.
create or replace trigger ia_patient
after insert on patient
for each row
enable
declare
age_value number(10);
begin
DBMS_OUTPUT.PUT_LINE('Please Insert Age');
update patient set age=&age_value where ??;
end;
I am not sure what I should write after "where"
Unless it is a homework question, it really doesn't make much sense as "age" depends on current date.
Anyway, see if this helps.
SQL> create table patient
2 (id number primary key,
3 date_of_birth date,
4 age number
5 );
Table created.
Trigger: calculate age as months between sysdate and date of birth divided by 12 (as there are 12 months in a year); certainly, this is just an approximate value, but - as far as I understood - your task isn't to correctly calculate age, but to learn how to use triggers.
SQL> create or replace trigger ia_patient
2 before insert on patient
3 for each row
4 begin
5 :new.age := round(months_between(sysdate, :new.date_of_birth) / 12);
6 end;
7 /
Trigger created.
Testing:
SQL> insert into patient (id, date_of_birth) values (2, date '2000-10-05');
1 row created.
SQL> select * from patient;
ID DATE_OF_BI AGE
---------- ---------- ----------
2 2000-10-05 20
SQL>
This is written as a frame challenge to the question.
Don't use a trigger; if you do it'll insert the age now and then tomorrow, or in 6 months, or a year the person will have a birthday and your table will be incorrect. This should be something you calculate in a query at the time you want the data and could be put into a view.
CREATE TABLE people (
id INTEGER
GENERATED AS IDENTITY
CONSTRAINT people__id__pk PRIMARY KEY,
name VARCHAR2(50)
NOT NULL,
date_of_birth DATE
NOT NULL
);
Then you can create a view to calculate the ages:
CREATE VIEW people_ages ( id, name, date_of_birth, age ) AS
SELECT id,
name,
date_of_birth,
FLOOR( MONTHS_BETWEEN( SYSDATE, date_of_birth ) / 12 )
FROM people;
Which, if you have the sample data:
INSERT INTO people ( name, date_of_birth )
SELECT 'Alice', DATE '1970-01-01' FROM DUAL UNION ALL
SELECT 'Bob', DATE '2020-10-21' FROM DUAL UNION ALL
SELECT 'Carol', DATE '2000-10-21' FROM DUAL;
Then the output from the view would be:
SELECT * FROM people_ages;
ID | NAME | DATE_OF_BIRTH | AGE
-: | :---- | :------------------ | --:
1 | Alice | 1970-01-01 00:00:00 | 50
2 | Bob | 2020-10-21 00:00:00 | 0
3 | Carol | 2000-10-21 00:00:00 | 20
db<>fiddle here
Your trigger has serious defects. Despite being a totally inappropriate use of a trigger I'll go over them.
Attribute: Age. Lets just say others have sufficiently covered it. But i will return to it.
DBMS_OUTPUT: This is not output when issued. The pl/sql engine does not even output this at all. It builds an internal buffer which can later be read back and processed by the client after the pl/sql has completed. However, there is no requirement that the client do so. Typically used for debugging purposes and not available in production system.
&age_value: This is an attempt at an interactive processing. Pl/sql is not and cannot do interactive processing. This is called a substitution variable and is totally the responsibly of the client. The client sees the &... and prompts for a value. It then physically alters the statement to contain that value. For example if you reply 42 to the prompt what is sent to the compiler is age=42. The compiler actually never sees &age_value.
Update: you CAN NOT do this; it will throw the exception "ORA-04091: table ... is mutating, trigger/function may not see it". Within a trigger you cannot issue DML on the table that caused the trigger to fire. Moreover it is completely unnecessary.
where ??: Since you cannot have the update statement this is moot.
In a trigger you refer to table columns through the pesudo rows :new and/or :old. So assuming you need to stay with a trigger you rewrite as (following assumes a date_of_birth column (dob) name being inserted):
create or replace trigger ia_patient
after insert on patient
for each row
enable
begin
:new.age := trunc(months_between(sysdate, :new.dob)/12);
end;
However the problem being age is now a static value. Which will need regular updates to keep current. A much better approach just let Oracle do the calculation when needed with a view as suggested by #MT0 and abandon the trigger altogether.

I have a table where I need to update or insert depending on field paramaters

I have spent many hours researching this problem and trying various solutions but I never quite find a suitable solution for my specific problem. I am new to SQL and some of the examples are confusing as well.
So here is my dilemma. I have a equipment table that tracks oil changes for specific units in a database. The table looks like this:
**id UnitID Posted_On Date_Completed Note OverDueBy**
1 BT-109F 2019-02-04 2019-02-14 Hrs Overdue 23
1 BT-108G 2020-01-17 2020-01-22 Days Overdue 12
1 BT-122K 2020-01-02 2020-01-16 Days Overdue 12
1 BT-109F 2019-02-04 Days Overdue 3
The example records above need to be created or updated by the query. The date completed is entered manually by the technician when he has completed the oil change.
What I want the query to do is, Check to see if a specific Unit has a record where the 'Date_Completed' field is empty, and if so update the 'OverDueBy' field to reflect the new value. If all the records for the specified Unit have the 'Date_Completed' fields filled in, then the query should create a new record will all fields filled in except for the 'Date_Completed' field.
Can anyone help me construct such a query?
Thanks
Clan
First create a unique partial index for the column UnitID:
CREATE UNIQUE INDEX idx_unit ON tablename(UnitID)
WHERE Date_Completed IS NULL;
so that only 1 row with Date_Completed=null is allowed for each UnitID.
So a statement like this:
INSERT INTO tablename(id, UnitID, Posted_On, Date_Completed, Note, OverDueBy)
VALUES (?, 'BT-109F', ?, null, ?, ?)
ON CONFLICT(UnitID) WHERE Date_Completed IS NULL DO UPDATE
SET OverDueBy = ?;
will insert the new values only if there is no row already for UnitID='BT-109F' with null in Date_Completed.
But if there is such a row then it will update the column OverDueBy.
I'm not sure what values you want to insert or what will be the updated value so replace the ? with the appropriate values.
Firstly I would use a view rather than a table to store any calculated data - it reduces storage overheads and will update the calculation every time the view is opened.
If you're using SQLite you should be able to get the overdue by subtracting the Posted_On from its function to return today's date something like date('now') or julianday('now') - read up on and test the functions to ensure it does what you want.
So along the lines of:-
create view MyView as select *, julianday('now') - julianday(Posted_On) as OverDueBy from ClansTable where Date_Completed is null;
If you want to store a snapshot you can always create a table from a view in any case:-
create table MyStoredOverduesOn4thFeb as select * from MyView;
You can find your units that have all Date_Completed and create a single new record like so:-
Create table CompletedUnits as select id, UnitID, max(posted_on) as latest_posted_on, '' as Date_Completed from ClansTable group by id, UnitID having count(*) = count(Date_Complete);
Test this SQL and see if you can get it working - note I've created a text field for the date. Apparently there is no date/datetime data type as such:-
https://www.sqlitetutorial.net/sqlite-date/
Hope this helps,
Phil
I think you need something like this:
MERGE INTO EQUIPMENT A
USING (SELECT * FROM EQUIPMENT B WHERE DATE_COMPLETED IS NULL) C
ON (A.UNITID=C.UNITID)
WHEN MATCHED THEN UPDATE SET A.OVERDUEBY="new value"
WHEN NOT MATCHED THEN INSERT (A.id,A.UnitID,A.Posted_On,A.Date_Completed,A.Note,A.OverDueBy)
VALUES (C.id,C.UnitID,C.Posted_On,NULL,C.Note,C.OverDueBy)
Not sure where new values from update will come from. It's not clear in your question. But something like this could work.

Get back the id of each insert in SQL Server

Let's say we want to insert two users and I want to know the userId of each record I inserted.
Example:
Db:
User.lookup database with these columns:
UserId(PK, identity) | Username
Setup, insert two users:
declare #users table (uniqueId INT, name nvarchar(100));
insert into #users (0, 'TestUser')--Two users with the same name, they'll get a different userid in the db
insert into #users (1, 'TestUser')--Uniqueid is just an autonumber I use to tell the difference between them.
Insert statement:
insert into user.lookup (userName)
output inserted.userid
select name from #users;
This will return two usersIds, example 1 & 2. But how do I know which of the two users got which userId?
I can differentiate them in code with their 'uniqueid' I pass but I don't know how to return it.
Don't just output the id. You can include other columns:
insert into user.lookup (userName)
output inserted.*
select name from #users;
Here is a db<>fiddle.
You can't correlate the inserted rows with the database-assigned IDs, at least not without inserting an alternate key as well. INSERT ... OUTPUT will not let you output a row that wasn't actually inserted, so the column that correlates the un-keyed rows with the new key values has to be actually inserted.
So the options are:
To use a SEQUENCE instead of IDENTITY and and either assign IDs to the table variable before insert, or assign IDs to the entities on the client, eg by calling sp_sequence_get_range.
Use MERGE instead of INSERT. This is what Entity Framework Core does. See eg The Case of Entity Framework Core’s Odd SQL
As Gordon explained, one can output more than 1 column.
But just to put my 2 cents in, such insert doesn't really need an intermediate table variable.
create table lookup (
lookupId int identity primary key,
userName nvarchar(100),
createdOn datetime2 not null
default sysdatetime()
)
GO
✓
insert into lookup (userName) values
('TestUser1')
,('TestUser2')
;
GO
2 rows affected
insert into lookup (userName)
output inserted.lookupId, inserted.userName
values
('Testuser3'),
('Testuser3')
GO
lookupId | userName
-------: | :--------
3 | Testuser3
4 | Testuser3
select lookupId, userName
--, convert(varchar,createdOn) as createdOn
from lookup
order by lookupId
GO
lookupId | userName
-------: | :--------
1 | TestUser1
2 | TestUser2
3 | Testuser3
4 | Testuser3
db<>fiddle here

How to insert row in a table every time I insert a new row in the main table?

I have a Vb.net app that is connected to an Access 2010* Database, I have a table with personal information of many students and another table with multiple true/false fields for every course the student has succed.
The structure is something like this
Table students
|Id_student | Name | Phone |
Table finishedCourses
| Id_stutent | chemistry | physics | maths |
How can I add a new row into finishedCourses table every time that I insert a new row into students table.
I don't know how add the rows with the same id in both tables.
I expect something like this
Table students
Id_student | Name | Phone
1234456 | abc | 12432534645
Table finishedCourses
Id_stutent | chemistry | physics | maths
1234456 | false | false | false
The default values for Courses are `False'. Initial status of each course is incomplete.
I tried to undestand what you want, you want to insert a initial values in finishedCourses when you insert a student information in table students, am I right?
I am not familiar with the Access database, I realized that the Access database may not have the trigger function, otherwise you can use the trigger to implement your requirement.
And in this problem, you can just write two insert SQLs to complete this with the same studentId, like below:
insert into students(id_101, 'Bob', '88089901');
insert into finishedCourses(id_101, false, false, false...);
If you are using SQL server then you can use trigger. The sample code is as below, I have no idea about MS-Access
CREATE TRIGGER trgAfterInsert ON [dbo].[students]
FOR INSERT
AS
DECLARE #Id_stutent int;
SELECT #Id_stutent=i.Id_student FROM inserted i;
INSERT INTO finishedCourses
(Id_stutent,chemistry,physics,maths)
VALUES(#Id_stutent ,false , false , false ,false );
GO

INSERT into table wherever update_time is latest

I have a table that has data about session. I want to insert hardcoded values for every new session row added at the latest time.
for example, given this table with 3 session rows information added at
2/10/2019 5:15 PM
I want to insert server1, and business values into the 3 rows (row 1-3) at the latest UDPATE_TIME, which in this case is
2/10/2019 5:15 PM
I use this query to get the latest update time:
$update_time = query "SELECT FORMAT(MAX([UPDATE_TIME]), 'M/d/yyyy h:mm tt') AS UPDATE_TIME FROM table"| Select -ExpandProperty UPDATE_TIME;
and i use this insert query to insert the values:
query "INSERT INTO table (SERVERNAME, Business_Name) VALUES ('server1', 'business')"
however, as you can see its inserting on a new row (row4), and instead i want to insert in rows that are already there 1-3, so that it becomes like this:
pseudo-code of query i am looking for:
query "INSERT INTO table (SERVERNAME, Business_Name) VALUES ('server1', 'business') wherever $update_time
I think you want an update. Something like this:
update [table]
set business_name = #business_name
where update_time = (select max(t2.update_time) from [table] t2);
Doing an update after the inserts seems dangerous -- different threads could be inserting data at the same time. However this answers the question that you did ask.