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

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.

Related

Adding column to sqlite database and distribute rows based on primary key

I have some data elements containing a timestamp and information about Item X sales related to this timestamp.
e.g.
timestamp | items X sold
------------------------
1 | 10
4 | 40
7 | 20
I store this data in an SQLite table. Now I want to add to this table. Especially if I get data about another item Y.
The item Y data might or might not have different timestamps but I want to insert this data into the existing table so that it looks like this:
timestamp | items X sold | items Y sold
------------------------------------------
1 | 10 | 5
2 | NULL | 10
4 | 40 | NULL
5 | NULL | 3
7 | 20 | NULL
Later on additional sales data (columns) must be added with the same scheme.
Is there an easy way to accomplish this with SQLite?
In the end I want to fetch data by timestamp and get an overview which items were sold at this time. Most examples consider the usecase to add a complete row (one record) or a complete column if it perfectly matches to the other columns.
Or is sqlite the wrong tool at all? And I should rather use csv or excel?
(Using pythons sqlite3 package to create and manipulate the DB)
Thanks!
Dynamically adding columns is not a good design. You could add them using
ALTER TABLE your_table ADD COLUMN the_column_name TEXT
the column, for existing rows would be populated with nulls, although you could specify a DEFAULT value and the existing rows would then be populated with that value.
e.g. the following demonstrates the above :-
DROP TABLE IF EXISTS soldv1;
CREATE TABLE IF NOT EXISTS soldv1 (timestamp INTEGER PRIAMRY KEY, items_sold_x INTEGER);
INSERT INTO soldv1 VALUES(1,10),(4,40),(7,20);
SELECT * FROM soldv1 ORDER BY timestamp;
ALTER TABLE soldv1 ADD COLUMN items_sold_y INTEGER;
UPDATE soldv1 SET items_sold_y = 5 WHERE timestamp = 1;
INSERT INTO soldv1 VALUES(2,null,10),(5,null,3);
SELECT * FROM soldv1 ORDER BY timestamp;
resulting in the first query returning :-
and the second query returning :-
However, as stated, the above is not considered a good design as the schema is dynamic.
You could alternately manage an equivalent of the above with the addition of either a new column (to also be part of the primary key) or by prefixing/suffixing the timestamp with a type.
Consider, as an example, the following :-
DROP TABLE IF EXISTS soldv2;
CREATE TABLE IF NOT EXISTS soldv2 (type TEXT, timestamp INTEGER, items_sold INTEGER, PRIMARY KEY(timestamp,type));
INSERT INTO soldv2 VALUES('x',1,10),('x',4,40),('x',7,20);
INSERT INTO soldv2 VALUES('y',1,5),('y',2,10),('y',5,3);
INSERT INTO soldv2 VALUES('z',1,15),('z',2,5),('z',9,25);
SELECT * FROM soldv2 ORDER BY timestamp;
This has replicated, data-wise, your original data and additionally added another type (column items_sold_z) without having to change the table's schema (nor having the additional complication of needing to update rather than insert as per when applying timestamp 1 items_sold_y 5).
The result from the query being :-
Or is sqlite the wrong tool at all? And I should rather use csv or excel?
SQLite is a valid tool. What you then do with the data can probably be done as easy as in excel (perhaps simpler) and probably much simpler than trying to process the data in csv format.
For example, say you wanted the total items sold per timestamp and how many types were sold then :-
SELECT timestamp, count(items_sold) AS number_of_item_types_sold, sum(items_sold) AS total_sold FROM soldv2 GROUP by timestamp ORDER BY timestamp;
would result in :-

Add timestamp to existing table

I have a SQL Server table with just 3 columns, one of which is of type varbinary. The data in this column is actually a Json document which among other properties contains information about when the data was last modified. Unfortunately the SQL table itself does not contain information about when its rows were modified.
Now when doing sorting and filtering of the data I of course don't want fetch all rows in order to find e.g. the latest 100 entries.
So my question is: does SQL Server somehow remember when a row was added/modified? I have tried adding a timestamp and this is applied to all existing rows but this is applied randomly I think, because the sorting doesn't work. I don't need a datetime or anything, I just want to be able sort the records based on when they were last modified.
Thanks
For those looking to insert a tamestamp column of type DateTime into an existing DB table, you can do this like so:
ALTER TABLE TestTable
ADD DateInserted DATETIME NOT NULL DEFAULT (GETDATE());
The existing records will automatically get the value equal to the date/time of the moment when column is added.
New records will get up-to-date value upon insertion.
SQL Server will not track historically when a row was inserted or modified so you need to rely on the JSON data to figure that out yourself. You are going to need a new column to make this efficient to query. Once you have your new column you have some options:
Loop through all your records populating the new column with the relevant value from the JSON data.
If your version of SQL Server is recent enough, you can query the JSON data directly. Populate this column using a query like this:
UPDATE MyTable
SET MyNewColumn = JSON_VALUE(JsonDataColumn, '$.Customer.DateCreated')
The downside of this method is that you need to maintain this
Make SQL Server compute the value from the JSON automatically, for example:
ALTER TABLE MyTable
ADD MyNewColumn AS JSON_VALUE(JsonDataColumn, '$.Customer.DateCreated')
And, create an index to make it efficient:
CREATE INDEX IX_MyTable_MyNewColumn
ON MyTable(MyNewColumn)
Use a new column CreatedDate and store datetime every time you make an Insert.
You could use GetDate() for inserting date in the column.
A UpdatedDate column can be used for updates.
in order to find e.g. the latest 100 entries.
Timestamp is indeed what you need.
It's ever-increasing value, it's updated automatically, so you are always able to find all last modified/inserted rows.
Here is an example:
create table dbo.test1 (id int);
insert into dbo.test1 values(1), (2), (3);
alter table dbo.test1 add ts timestamp;
update dbo.test1
set id = 10
where id = 2
select top 1 *
from dbo.test1
order by ts desc;
--id ts
--10 0x000000001FCFABD2
insert into dbo.test1 (id)
values (100);
select top 1 *
from dbo.test1
order by ts desc;
--id ts
--100 0x000000001FCFABD3
As you see, you always get the last modified/inserted row.
For your purpose just use
select top 100 *
...
order by ts desc;
Thanks. Apparently I didn't look hard enough before I posted this question. The question has been asked a couple of times before and the answer is: Nope! There is no easy solution to this.
SQL Server does not keep track of when a record was created or modified, which was somehow what I was looking for. So I will go for the next best solution, which is probably to create a datetime column, retrieve the modified date from the Json document and then update the record. Or rather, the 1,4 million records:-(

Sql timestamp for created/updated dates

In a few logging tables that are frequently written to, I'd like to be able to store a relative order so that I can union between these tables, and get the order that things actually occurred in.
DateTime2's resolution is lacking. Several rows will get the exact same date, so there is no way to tell which happened first.
Because sorting should work across several tables, sorting by Id is out.
Then I started looking at timestamp. This works for updated dates, but it does not work for created dates, because you can only have one timestamp column per table, and it automatically updates.
This is for Microsoft Sql Server 2008.
Any suggestions?
You can simulate it with another column typed as binary(8) (same as rowversion) and defaulting to ##DBTS:
create table TX (
ID int not null,
Updated rowversion not null,
Created binary(8) not null constraint DF_TX_Created DEFAULT (##DBTS)
)
go
insert into TX (ID)
values (1),(2)
go
update TX set ID = 3 where ID = 1
go
insert into TX (ID)
values (4)
go
select * from TX
Result:
ID Updated Created
----------- ------------------ ------------------
3 0x00000000000007D3 0x00000000000007D0
2 0x00000000000007D2 0x00000000000007D0
4 0x00000000000007D4 0x00000000000007D3
Notes:
The Created values will always be equal to the last rowversion value assigned, so they will "lag", in some sense, compared to Updated values.
Also, multiple inserts from a single statement will receive the same Created values, whereas Updated values will always be distinct.

using UPDATE, SELECTand INSERT INTO statements altogther in MS Access

I have an MS access project in which the tabel of transactions contains as many as 70 records per day. they are common in one parameter, the Date of today.
the normal way to start entering data is to go to teh table and copy records of yesterday then paste them as new records, then changing date from yesterday to today date.
What I need is to use SQL statements to achieve this. I can say that
step 1: use insert into to input the new records in the table.
Step 2: use update statement to change the date.
Step 3: use the select statement to select the records which will have the date to be changed.
How to combine the use of these statements altogther.
thanks
You need something on the lines of:
INSERT INTO Table (Field, Field, Field, ADate)
SELECT Field,Field,Field,Date() As ADate
FROM Table WHERE ADate = Date()-1
Or just change the default value of the date field in the table to Date(), or Now(), and only insert the other fields. Date will then be automatically filled in as today.

SQLite create a new record if it doesn't exist

Hey so I'm trying to create a new record based on one which already exists, but I'm having trouble ensuring that this record doesn't already exist. The database stores details of transactions and has a field for whether it repeats.
I'm generating the new dates using
SELECT datetime(transactions.date, '+'||repeattransactions.interval||' days')
AS 'newdate' FROM transactions, repeattransactions WHERE
transactions.repeat = repeattransactions.id
Initially I'd like to use SELECT instead of INSERT just for debugging purposes. I've tried to do something with GROUP BY or COUNT(*) but the exact logic is eluding me. So for instance I've tried
SELECT * FROM transactions, (SELECT transactions.id, payee, category, amount,
fromaccount, repeat, datetime(transactions.date,
'+'||repeattransactions.interval||' days') AS 'date' FROM transactions,
repeattransactions WHERE transactions.repeat = repeattransactions.id)
but this obviously treats the two tables as though they were joined, it doesn't append the records to the bottom of the table which would let me group.
I've been struggling for days so any help would be appreciated!
EDIT:
The docs say "The ON CONFLICT clause applies to UNIQUE and NOT NULL constraints". I can't have a unique constraint on the table by definition as I have to create the exact same record for a new transaction with just a new date. Or have I misinterpreted this?
EDIT2:
id payee repeat date
1 jifoda 7 15/09/2011
2 jifoda 7 22/09/2011 <-- allowed as date different within subset
3 grefa 1 15/09/2011 <-- allowed as only date is not-unique
4 grefa 1 15/09/2011 <-- not allowed! exactly same as id 3
SQLite has special DDL statement for duplicates. Like:
create table test(a TEXT UNIQUE ON CONFLICT IGNORE);
insert into test values (1);
insert into test values (1);
select count(*) from test;
1
It can be also ON CONFLICT REPLACE. See docs.