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

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

Related

How to add missing values of file on COPY command

I have a table with a column that comes from a file, although I'm certain that for the other column the value is missing on the file.
Here's the table:
create table if not exists user(
id varchar(36) primary key,
relevance varchar(3) not null,
constraint relevance_check check (relevance in ('ONE', 'TWO'))
);
The command I want to populated the table with:
copy user(id') from '/home/users_ids.txt';
The problem is that my column relevance is not null, and I'd like to set default values on the relevance column when copying, but I'm not sure if that's possible.
I cant set a default value on the tables because I need to import data from many files and each one would have a different value on the relevance field.
Can I achieve what I want using the copy command or there's another approach for this?
You can set the column's default value just for a while, e.g.:
alter table "user" alter relevance set default 'ONE';
copy "user"(id) from '/home/users_one.txt';
alter table "user" alter relevance set default 'TWO';
copy "user"(id) from '/home/users_two.txt';
alter table "user" alter relevance drop default;
The solution is simple and efficient when you are sure that the import takes place in only one session at a time but is not safe if you intend to use it simultaneously in more than one session. A safer alternative, in this case, could be to use a temporary table, e.g .:
create temp table ids(id text);
copy ids(id) from '/home/users_one.txt';
insert into "user"
select id, 'ONE'
from ids;

ALTER TABLE migrations does not update the generated code

I'm writing a migration file which alters a table, adding a new column, but the generated code isn't updated, so I can't insert new records into the table with value to the new column.
Example:
// BankAccount.sq file
CREATE TABLE bank_account (
id INTEGER PRIMARY KEY,
bank_code TEXT NOT NULL,
account_number INTEGER NOT NULL,
account_digit INTEGER NOT NULL
);
selectALL:
SELECT * FROM bank_account;
insert:
INSERT INTO bank_account VALUES (?,?,?,?);
// 1.sqm file
ALTER TABLE bank_account ADD COLUMN bank_name TEXT;
It seems the generated code is not updated after that column is added. For instance, the insert function isn't updated to receive the new added column to the table, and the internal generated Kotlin code isn't updated with the new column.
Is there any way to bypass this issue?
the CREATE TABLE is always the fresh version of your schema, so you need to add the column in the CREATE TABLE as well. If you want to have migration files be the source of truth you need to enable deriveSchemaFromMigrations as outlined here: https://cashapp.github.io/sqldelight/jvm_mysql/#migration-schema

SQL Server Insert with no specified columns

I have a table with an auto-generated ID column (and that's all!)
CREATE TABLE [dbo].[EmailGroup](
[EmailGroupGuid] [uniqueidentifier] NOT NULL
CONSTRAINT [PK_EmailGroup] PRIMARY KEY CLUSTERED ([EmailGroupGuid] ASC)
) ON [PRIMARY]
ALTER TABLE [dbo].[EmailGroup]
ADD CONSTRAINT [DF_EmailGroup_EmailGroupGuid] DEFAULT (newsequentialid()) FOR [EmailGroupGuid]
I want to INSERT into this table and extract the generated ID. but, I can't work out if it's possible. It seems to complain about the lack of values/columns.
DECLARE #Id TABLE (Id UNIQUEIDENTIFIER)
INSERT INTO EmailGroup
OUTPUT inserted.EmailGroupID INTO #Id
Is there any way to do this? I mean I could add a dummy column to the table and easily do this:
INSERT INTO EmailGroup (Dummy)
OUTPUT inserted.EmailGroupID INTO #Id
VALUES (1)
however I don't really want to.
I could also specify my own ID and insert that, but again, I don't really want to.
Though I'm not sure why would you need such a table, the answer to your question is to use the keyword DEFAULT:
INSERT INTO EmailGroup (EmailGroupGuid)
OUTPUT inserted.EmailGroupGuid INTO #Id
VALUES(DEFAULT);
Another option is to use DEFAULT VALUES, as shown in Pawan Kumar's answer.
The key difference between these two options is that specifying the columns list and using the keyword default gives you more control.
It doesn't seem much when the table have a single column, but if you will add columns to the table, and want to insert specific values to them, using default values will no longer be a valid option.
From Microsoft Docs on INSERT (Transact-SQL):
DEFAULT
Forces the Database Engine to load the default value defined for a column.
If a default does not exist for the column and the column allows null values, NULL is inserted.
For a column defined with the timestamp data type, the next timestamp value is inserted.
DEFAULT is not valid for an identity column.
DEFAULT VALUES
Forces the new row to contain the default values defined for each column.
So as you can see, default is column based, while default values is row based.
Please use this.
CREATE TABLE [dbo].[EmailGroup]
(
[EmailGroupGuid] [uniqueidentifier] NOT NULL CONSTRAINT [PK_EmailGroup] PRIMARY KEY CLUSTERED ([EmailGroupGuid] ASC)
) ON [PRIMARY]
ALTER TABLE [dbo].[EmailGroup]
ADD CONSTRAINT [DF_EmailGroup_EmailGroupGuid] DEFAULT (newsequentialid()) FOR [EmailGroupGuid]
DECLARE #Id TABLE (Id UNIQUEIDENTIFIER)
INSERT INTO EmailGroup
OUTPUT inserted.EmailGroupGuid INTO #Id DEFAULT VALUES
SELECT * FROM #Id
last 3 OUTPUTs from my Laptop
--92832040-7D52-E811-B049-68F728AE8695
--2B6ADC5F-7D52-E811-B049-68F728AE8695
--0140AF66-7D52-E811-B049-68F728AE8695

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.

Why is SQL server throwing this error: Cannot insert the value NULL into column 'id'?

I'm using the following query:
INSERT INTO role (name, created) VALUES ('Content Coordinator', GETDATE()), ('Content Viewer', GETDATE())
However, I'm not specifying the primary key (which is id). So my questions is, why is sql server coming back with this error:
Msg 515, Level 16, State 2, Line 1
Cannot insert the value NULL into column 'id', table 'CMT_DEV.dbo.role'; column does not allow nulls. INSERT fails.
The statement has been terminated.
I'm assuming that id is supposed to be an incrementing value.
You need to set this, or else if you have a non-nullable column, with no default value, if you provide no value it will error.
To set up auto-increment in SQL Server Management Studio:
Open your table in Design
Select your column and go to Column Properties
Under Indentity Specification, set (Is Identity)=Yes and Indentity Increment=1
use IDENTITY(1,1) while creating the table
eg
CREATE TABLE SAMPLE(
[Id] [int] IDENTITY(1,1) NOT NULL,
[Status] [smallint] NOT NULL,
CONSTRAINT [PK_SAMPLE] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
)
If the id column has no default value, but has NOT NULL constraint, then you have to provide a value yourself
INSERT INTO dbo.role (id, name, created) VALUES ('something', 'Content Coordinator', GETDATE()), ('Content Viewer', GETDATE())
Encountered the same issue. This is something to do with your table creation. When you created table you have not indicate 'ID' column to be Auto Increment hence you get this error. By making the column Primary Key it cannot be null or contain duplicates hence without Auto Increment pretty obvious to throw column does not allow nulls. INSERT fails.
There are two ways you could fix this issue.
1). via MS SQL Server Management Studio
Got to MS SQL Server Management Studio
Locate your table and right click and select Design
Locate your column and go to Column Properties
Under Indentity Specification: set (Is Identity)=Yes and Indentity
Increment=1
2). via ALTER SQLs
ALTER TABLE table DROP COLUMN id; // drop the existing ID
ALTER TABLE table ADD id int IDENTITY(1, 1) NOT NULL; // add new column ID with auto-increment
ALTER TABLE table ADD CONSTRAINT PK_ident_test PRIMARY KEY CLUSTERED (id); // make it primary key
You either need to specify an ID in the insert, or you need to configure the id column in the database to have Identity Specification = Yes.
As id is PK it MUST be unique and not null.
If you do not mention any field in the fields list for insert it'll be supposed to be null or default value.
Set identity (i.e. autoincrement) for this field if you do not want to set it manualy every time.
You need to set autoincrement property of id column to true when you create the table or you can alter your existing table to do this.
you didn't give a value for id. Try this :
INSERT INTO role (id, name, created) VALUES ('example1','Content Coordinator', GETDATE()), ('example2', 'Content Viewer', GETDATE())
Or you can set the auto increment on id field, if you need the id value added automatically.
I had a similar problem and upon looking into it, it was simply a field in the actual table missing id (id was empty/null) - meaning when you try to make the id field the primary key it will result in error because the table contains a row with null value for the primary key.
This could be the fix if you see a temp table associated with the error. I was using SQL Server Management Studio.
WARNING! Make sure the target table is locked when using this method
(As per #OnurOmer's comment)
if you can't or don't want to set the autoincrement property of the id, you can set value for the id for each row like this:
INSERT INTO role (id, name, created)
SELECT
(select max(id) from role) + ROW_NUMBER() OVER (ORDER BY name)
, name
, created
FROM (
VALUES
('Content Coordinator', GETDATE())
, ('Content Viewer', GETDATE())
) AS x(name, created)
RULE: You cannot IGNORE those colums that do not allow null values, when inserting new data.
Your Case
You're trying to insert values, while ignoring the id column, which does not allow nulls. Obviously this won't work.
Gladly for you the "Identity Specification" seems to automatically fill the not nullable id values for you (see selected answer), when you later execute the insert query.
My Case
The problem (while using SSMS): I was having this error when trying to add a new non-nullable column to an already existing table with data. The error I'd got was:
Cannot insert the value NULL into column 'id_foreign', table 'MyDataBase.dbo.Tmp_ThisTable'; column does not allow nulls. INSERT fails.
The statement has been terminated.
The solution:
I created the column I needed id_foreign, allowing nulls.
I edited/inserted all the required values for id_foreign.
Once the values where in place, I went back and unchecked the "Allow Nulls" checkbox. Now the error was gone.