SQL - date column automatically changing each time I update table - sql

I have a date column in my table. When I perform an update query on the rows, each time the date gets refreshed to the current date. I have set date's default value to CURRENT_TIMESTAMP but why is this happening each time?
UPDATE
My create query:
CREATE TABLE `ACCOUNTS` (
`id` bigint(7) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(30) DEFAULT NULL,
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`status` varchar(1) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1234567 DEFAULT CHARSET=latin1

It's probably something to do with date being a keyword. try changing it to some_date. Was CURRENT_TIMESTAMP intentional?

When you set the default value to CURRENT_TIMESTAMP, sql will insert the current time stamp at the time of creating new record only and will never update it unless you update it yourself. Refreshing will not update the timestamp

In case you use a MariaDB, this documentation page https://mariadb.com/kb/en/library/timestamp/ may have some surprising information for you:
"The timestamp field is generally used to define at which moment in time a row was added or updated and by default will automatically be assigned the current datetime when a record is inserted or updated. The automatic properties only apply to the first TIMESTAMP in the record; subsequent TIMESTAMP columns will not be changed."
Hope this bit helps the next developer who runs into this fantastic feature...

Related

postgresql option to both insert new row despite "conflict" or update existing row

If I have a postgresql table like:
CREATE TABLE IF NOT EXISTS public.time_series
(id serial PRIMARY KEY,
variable_id integer NOT NULL,
datetime timestamp with time zone NOT NULL,
value real NOT NULL,
edit_datetime timestamp with time zone NOT NULL DEFAULT now())
Im not having any constrains here except the primary key. For now..
And i want to have the option to either UPDATE OR INSERT new data where it is essentially the same variable for the same datetime. So that I can choose to either overwrite or just have a new version.. How do a proceede to do that?
Without constrains and if I want to INSERT some data; I do some WHERE NOT EXISTS where i make sure that the new data does not correlate with a row that has the same variable_id, datetime, and value. And it works just fine.
Now if the case is that I want to UPDATE if it exists and else INSERT I make a CONSTRAINT like:
ALTER TABLE public.time_series
ADD CONSTRAINT unique_comp UNIQUE (variable_id, datetime)
And updates the row if there is a conflict and INSERT if there is non.
And it works just fine..
But, again, what if I for some variables wants pervious "versions" where i can see them based on edit_datetime and want some other variables to be overwritten with the new data? Currently one way rules out the other one.
BR

Postgres - range for 'time without time zone' and exclude constraint

I have the following table:
create table booking (
identifier integer not null primary key,
room uuid not null,
start_time time without time zone not null,
end_time time without time zone not null
);
I want to create an exclude constraint to enforce that there are no overlapping appointments for the same room.
I tried the following:
alter table booking add constraint overlapping_times
exclude using gist
(
cast(room as text) with =,
period(start_time, end_time) with &&)
);
This has two problems:
Casting room to text is not enough, it gives: ERROR: data type text has no default operator class for access method "gist". I know in v10 there is btree_gist, but I am using v9.5 and v9.6, so I have to manually cast the uuid to a text afaik.
period(...) is wrong, but I have no idea how to construct a range of time without time zone type.
After installing btree_gist, you can do the following:
create type timerange as range (subtype = time);
alter table booking add constraint overlapping_times
exclude using gist
(
(room::text) with =,
timerange(start_time, end_time) with &&
);
If you want an expression in the constraint you need to put that into parentheses. So either (room::text) or (cast(room as text))

Ensure SQLite table only has one row

How can I enforce a table to have only one row? Below is what I tried. The UPDATE trigger might work, however, the CREATE trigger definitely will not. For the CREATE, I would like to use SET, however, SET is not supported by SQLite.
CREATE TABLE IF NOT EXISTS `config` (
`id` TINYINT NOT NULL DEFAULT 0,
`subdomain` VARCHAR(45) NOT NULL,
`timezone` CHAR(3) NOT NULL,
`timeout` TINYINT NOT NULL,
`offline` TINYINT NOT NULL,
`hash_config` CHAR(32) NOT NULL,
`hash_points` CHAR(32) NOT NULL,
PRIMARY KEY (`id`));
INSERT INTO config(id,subdomain,timezone,timeout,offline,hash_config,hash_points) VALUES(0,'subdomain','UTC',5,0,'hash_config','hash_points');
CREATE TRIGGER `config_insert_zero`
BEFORE INSERT ON `config`
FOR EACH ROW
BEGIN
-- SET NEW.id=0;
NEW.id=OLD.id;
END;
CREATE TRIGGER `config_update_zero`
BEFORE UPDATE ON `config`
FOR EACH ROW
BEGIN
-- SET NEW.id=0;
NEW.id=OLD.id;
END;
In the general case, to limit the number of rows in a table, you have to prevent any further insert.
In SQLite, this is done with RAISE():
CREATE TRIGGER config_no_insert
BEFORE INSERT ON config
WHEN (SELECT COUNT(*) FROM config) >= 1 -- limit here
BEGIN
SELECT RAISE(FAIL, 'only one row!');
END;
However, if the limit is one, you could instead simply constrain the primary key to a fixed value:
CREATE TABLE config (
id INTEGER PRIMARY KEY CHECK (id = 0),
[...]
);
One idea you may want to consider is to make it appear like the table has only one row. In reality, you keep all previous rows because it's quite possible you will one day want to maintain a history of all past values.
Since there is only one row, there really is no need for an ID column, the purpose of which is to uniquely differentiate each row from all the others. You do need, however, a timestamp which will be used to identify the "one row" which will be the latest row written to the table.
CREATE TABLE `config_history` (
`created` timestamp default current_timestamp,
`subdomain` VARCHAR(45) NOT NULL,
`timezone` CHAR(3) NOT NULL,
`timeout` TINYINT NOT NULL,
`offline` TINYINT NOT NULL,
`hash_config` CHAR(32) NOT NULL,
`hash_points` CHAR(32) NOT NULL,
PRIMARY KEY (`created`)
);
Since you are normally interested in only the last row written (the latest version), the query selects the row with the latest creation date:
select ch.created effective_date, ch.subdomain, ch.timezone, ch.timeout,
ch.offline, ch.hash_config, ch.hash_points
from config_history ch
where ch.created =(
select max( created )
from config_history );
Put a create view config as in front of this query and you have a view that selects only one row, the latest, from the table. Any query against the view returns the one row:
select *
from config;
An instead of trigger on the view can convert Updates to Inserts -- you don't actually want to change a value, just write a new row with the new values. This then becomes the new "current" row.
Now you have what appears to be a table with only one row but you also maintain a complete history of all the past changes ever made to that row.

How do I create an updated "date" field using SQLITE 3?

I am writing a program to help me keep track of my day to day life, and I want one of the fields to be a "date" field that will automatically update. What specifically do I do in SQLITE 3? Something like....
create table day_to_day(
date field
miles_ran INTEGER
food_eaten TEXT
)
How about:
CREATE TABLE day_to_day(
id INTEGER PRIMARY KEY AUTOINCREMENT,
t TIMESTAMP DEFAULT CURRENT_TIMESTAMP
miles_ran INTEGER
food_eaten TEXT
);
which would give you a column called t with the type TIMESTAMP, as a alternative you could also use this:
CREATE TABLE day_to_day(
id INTEGER PRIMARY KEY AUTOINCREMENT,
t DATE DEFAULT (datetime('now','localtime')),
miles_ran INTEGER
food_eaten TEXT
);
Maybe you can use this :
CREATE TABLE table_test (
...
date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
or
CREATE TABLE table_test (
...
date DATE DEFAULT (datetime('now','localtime')),
);
This is a good reference : sqlite database default time value 'now'
You can read what the docs have to say about it: SQLite datatypes, scroll to section 1.2.
The gist of it is, you can either use TEXT, REAL, or INTEGER, and then use the corresponding Date/Time function to access it.

MySQL create time and update time timestamp

I am creating some tables where I want to store the time when a record was created and when it was last updated. I thought I could have two timestamp fields where one would have the value CURRENT_TIMESTAMP and the other would have CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP. But I guess I can't do this because you can have only 1 timestamp field with a default value in a table?
How would you recommend I get and store the two times? Thanks!
A good way to create fields like 'created' and 'updated' is
CREATE TABLE `mytable` (
`id` INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
`created` TIMESTAMP DEFAULT '0000-00-00 00:00:00',
`updated` TIMESTAMP DEFAULT now() ON UPDATE now(),
`myfield` VARCHAR(255)
);
And its necessary to enter nulls into both columns during "insert":
INSERT INTO mytable (created,updated,myfield) VALUES (null,null,'blablabla');
And now, in all updates, the 'updated' field will have a new value with actual date.
UPDATE mytable SET myfield='blablablablu' WHERE myfield='blablabla';
Source : http://gusiev.com/2009/04/update-and-create-timestamps-with-mysql
As of MYSQL version 5.6.5 you can do this using DEFAULT and ON UPDATE. No triggers are needed.
ts_create TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
ts_update TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
You can have two columns of type timestamp in one table.
The following works for MySQL 5.0
create table t
(
id integer,
created_at timestamp default current_timestamp,
updated_at timestamp
);
I think you are confusing this with SQL Server (where timestamp is not really a "time stamp" and there is indeed a limit on a single "timestamp" column)
Edit: But you will need a trigger to update the update_at column each time the row is changed.
As far as I know, there's no workaround for that restriction. You'll need to manually set (at least) one of the timestamps, the easiest way is just add updated = NOW() to the UPDATE-query.
You'll need two columns: CREATE_TIME and UPDATE_TIME.
You might want to add CREATE_USER and UPDATE_USER.
Perhaps you'd want to have a 1:many relationship with name of column changed, old and new values.
It's all part of change data capture. You could have CDC tables that are updated using triggers.
I would leave the current timestamp the way you suggested and fill in the created_at field with current date on insert.