how can I reference another column inside postgres insert query? - sql

I have the following table:
CREATE TABLE tab (
id SERIAL PRIMARY KEY,
code TEXT NOT NULL,
data TEXT
)
In some cases, I'd like to insert a new row ensuring that the code column is generated by the id column. In other cases the code is provided by the user.
For example:
INSERT INTO tab(code, data) VALUES ('code: ' || id::TEXT, 'my data');
The expected result is something like:
id
code
data
1
code: 1
abc
2
code: 2
xyz
INSERT INTO tab(code, data) VALUES ('user.provided.code', 'my data');
The expected result is something like:
id
code
data
1
code: 1
abc
2
code: 2
xyz
3
user.provided.code
xyz
Is it possibile in one statement?

It sounds like you want to default the coder to something based on the id. Unfortunately, this doesn't work in Postgres:
create table tab (
id integer primary key generated always as identity,
code text not null default ('code '||id::text),
data text
);
One option is a single statement that does both an insert and update:
with i as (
insert into tab (code, data)
values ('', 'my data'
returning *
)
update tab
set code = 'code: ' || id::TEXT
where tab.id in (select i.id from i);
Another is to use a trigger that assigns the value.

Use INSERT INTO .. SELECT as follows:
INSERT INTO tab(code, data)
select 'code: ' || id::TEXT, 'my data' from tab;
Note: In newly added data(above insert), you are missing to add id column data or I think it is auto generated.

Related

Is there a way to insert the same row values in a temp table? SQL Server

I have a temp table #t that has a column already called ID with about 75 values. I've inserted another column called status and I want all of the values in the 'status' column to equal "A". Is there a way I can do this without having to manually insert A for each row?
Would want it to look like this but for all 75 rows
|ID| |Status|
----------------
|24| | A |
Not sure if I understand your question correctly but you could do it with something like
update #t set Status = 'A'
You can do that by setting the default value for status column.
Create table #test(id int, status varchar(1) default 'A');
insert into #test (id) values (1),(2),(3);
Select * From #test;
id status
1 A
2 A
3 A
If your table is already created you may set the default value as the following:
ALTER TABLE #test ADD CONSTRAINT df_val DEFAULT 'A' FOR status;
See a demo from db<>fiddle.

SQL to catch incremental entries from a view

I have a historic table that won't be updated with new inserts, and a have a view that everyday will be update with new inserts. So I need to know if my SQL is correct.
This SQL needs to get all entries that inside in FATO_Proposta_Planilha table (Table 1)
and to add the entries not similar that are in the FATO_Proposta_View table (Table 2).
So, this SQL must have all entries from Table 1 more all entries from Table 2 that are not repeated in the Table 1. Can you give a opinion about this SQL, please?
SELECT vw.[DescPac] [PA]
,vw.[DescRegional] [Regional]
,vw.[DescSuperintendencia] [Superintendencia]
,vw.[NUM_CPF_CNPJ] [Documento_Numero]
,pla.[Nome] [Nome]
,pla.[Produto] [Produto]
,pla.[Modalidade] [Modalidade]
,vw.[NUM_CONTRATO_CREDITO] [Contrato]
,vw.[DESC_FINALIDADE_OPCRED] [Finalidade]
,vw.[DATA_OPERACAO] [Data_operacao]
,pla.[Data_mov_entrada] [Data_mov_entrada]
,vw.[DATA_VENC_OPCRED] [Data_vencimento]
,vw.[VALOR_CONTRATO_OPCRED] [Valor_contrato]
,pla.[Processo_Lecon] [Processo_Lecon]
,CASE WHEN ISNULL(pla.Origem, '') = ''
THEN 'Esteira Convencional'
ELSE pla.Origem
END [Origem]
FROM Proposta_View vw
LEFT JOIN FATO_Proposta_Planilha pla
ON vw.NUM_CONTRATO_CREDITO = pla.Contrato
UNION
SELECT [PA] [PA]
,[Regional] [Regional]
,[Superintendencia] [Superintendencia]
,[Documento_Numero] [Documento_Numero]
,[Nome] [Nome]
,[Produto] [Produto]
,[Modalidade] [Modalidade]
,[Contrato] [Contrato]
,[Finalidade] [Finalidade]
,[Data_operacao] [Data_operacao]
,[Data_mov_entrada] [Data_mov_entrada]
,[Data_vencimento] [Data_vencimento]
,[Valor_contrato] [Valor_contrato]
,[Processo_Lecon] [Processo_Lecon]
,CASE WHEN ISNULL(Origem, '') = ''
THEN 'Esteira Convencional'
ELSE Origem
END [Origem]
If you are only inserting rows through the view you can add an extra column with a DEFAULT value to distinguish the old rows from the new ones.
For example if you have a table t as:
create table t (a int primary key not null);
insert into t (a) values (123), (456);
You can add the extra column as:
alter table t add is_new int default 1;
update t set is_new = 0;
create view v as select a from t;
Then each insert through the view won't see that new column and will insert with value 1.
insert into v (a) values (789), (444);
Then it's easy to find the new rows:
select * from t where is_new = 1;
Result:
a is_new
---- ------
444 1
789 1
Se running example at db<>fiddle.

Not able to insert a row in a table which has auto incremented primary key

I have a table reportFilters which has the following column names:
The reportFilterId is auto increment. I want to insert a row in the table with the script below:
IF OBJECT_ID(N'ReportFilters', N'U') IS NOT NULL
BEGIN
IF NOT EXISTS (SELECT * FROM [ReportFilters]
WHERE ReportId IN (SELECT ReportId FROM [Reports] WHERE ReportType = 'Operational Insights Command Staff Dashboard') )
BEGIN
INSERT INTO [ReportFilters] Values(1, 'SelectView', 'Select Views', 13, 'Views','Views', 'SelectView', 'a', 'b', 'c' );
END
END
GO
But I am getting the following error:
Column name or number of supplied values does not match table definition.
Can I please get help on this ? Thanks in advance.
I think the problem is on inserted columns can't match with inserted data because that will instead by your table column order which is ReportFilterId instead of ReportId
So that there are 11 columns in your table but your statement only provides 10 columns.
I would use explicitly specify for inserted columns (inserted columns start from ReportId except your PK ReportFilterId column)
INSERT INTO [ReportFilters] (ReportId,ReportFilterName,ReportFilterTitle....)
Values (1, 'SelectView', 'Select Views', 13, 'Views','Views', 'SelectView', 'a', 'b', 'c' );

While updating table1, how do I INSERT to table2 for every change in table 1?

I have a MEMBER table and NOTIFICATION table. On client side, I list all of the records in MEMBER table and there is a points column and this is shown as text input. So after I change the values for some members, I can click save button and this will update the records in my MEMBER table that's all right,
But the thing I want to accomplish is for every record whose points value has changed I want to INSERT a record in my notifications table.
I couldn't think of anything, how can I approach to this problem?
For notifications I made 3 tables by following the article in here
Use the output clause instead of trigger, they are bad.
You need the condition "where data_old <> data_new" case if you updated a column with the same value, SQL Server marked it as changed, even if the value hasn't changed
create table #example (id int identity(1,1) not null, data nvarchar(max));
insert into #example (data) values ('value 1'),('value 2'), ('value 3');
create table #audit (id int, data_old nvarchar(max), data_new nvarchar(max), [When] datetime not null default (getdate()));
insert into #audit (id, data_old, data_new)
select id, data_old, data_new
from (
update #example
set data = 'value changed'
output inserted.id, deleted.data as data_old, inserted.data as data_new
where id = 2
)changed (id, data_old, data_new)
where data_old <> data_new
select * from #audit
will result with this in #audit :
You have described what a trigger does.
create trigger trig_member_insert on members after update
as
begin
insert into notifications ( . . . )
select . . ., i.points as new_points u.points as old_points -- what you want to insert
from inserted i join
updated u
on i.member_id = u.member_id
where u.points <> i.points
end;
Storing something called "points" as a string seems like a very poor choice. It sounds like a number.

INSERT SELECT in Firebird

I'm new to firebird and I have verious issues. I want to insert various lines into a table selected from another table.
Here's the code:
/*CREATE GENERATOR POS; */
SET GENERATOR POS TO 1;
SET TERM ^;
create trigger BAS_pkassign
for MATERIAL
active before insert position 66
EXECUTE BLOCK
AS
declare posid bigint;
select gen_id(POS, 1)
from RDB$DATABASE
into :posid;
BEGIN
END
SET TERM ; ^
INSERT INTO MATERIAL ( /*ID */ LOCATION, POSID, ARTID, ARTIDCONT, QUANTITY )
SELECT 1000, ':posid', 309, BAS_ART.ID, 1
FROM BAS_ART
WHERE BAS_ART.ARTCATEGORY LIKE '%MyWord%'
The ID should autoincrement from 66 on. The posid should autoincrement from 1 on.
Actually it is not inserting anything.
I'm using Firebird Maestro and have just opened the SQL Script Editor (which doesnt throw any error message on executing the script).
Can anybody help me?
Thanks!
Additional information:
The trigger should autoincrement the column "ID" - but I dont know how exactly I can change it so it works.. The ':posid' throws an error using it :posid but like this theres no error (I guess its interpretated as a string). But how do I use it right?
I dont get errors when I execute it. The table structure is easy. I have 2 tables:
1.
Material (
ID (INTEGER),
Location (INTEGER),
POSID (INTEGER),
ARTID (INTEGER),
ARTIDCONT (INTEGER),
QUANTITY (INTEGER),
OTHERCOLUMN (INTEGER))
and the 2. other table
BAS_ART (ID (INTEGER), ARTCATEGORY (VARCHAR255))
-> I want to insert all entries from the table BAS_ART which contain "MyWord" in the column ARTCATEGORY into the MATERIAL table.
I don't understand why you need the trigger at all.
This problem:
I want to insert all entries from the table BAS_ART which contain "MyWord" into the MATERIAL table
Can be solved with a single insert ... select statement.
insert into material (id, location, posid, artid, quantity)
select next value for seq_mat_id, 1000, next value for seq_pos, id, 1
from bas_art
where artcategory = 'My Word';
This assumes that there is a second sequence (aka "generator") that is named seq_mat_id that provides the new id for the column material.id
For most of my answer I will assume a very simple table:
CREATE TABLE MyTable (
ID BIGINT PRIMARY KEY,
SomeValue VARCHAR(255),
posid INTEGER
)
Auto-increment identifier
Firebird (up to version 2.5) does not have an identity column type (this will be added in Firebird 3), instead you need to use a sequence (aka generator) and a trigger to get this.
Sequence
First you need to create a sequence using CREATE SEQUENCE:
CREATE SEQUENCE seqMyTable
A sequence is atomic which means interleaving transactions/connections will not get duplicate values, it is also outside transaction control, which means that a ROLLBACK will not revert to the previous value. In most uses a sequences should always increase, so the value reset you do at the start of your question is wrong for almost all purposes; for example another connection could reset the sequence as well midway in your execution leaving you with unintended duplicates of POSID.
Trigger
To generate a value for an auto-increment identifier, you need to use a BEFORE INSERT TRIGGER that assigns a generated value to the - in this example - ID column.
CREATE TRIGGER trgMyTableAutoIncrement FOR MyTable
ACTIVE BEFORE INSERT POSITION 0
AS
BEGIN
NEW.ID = NEXT VALUE FOR seqMyTable;
END
In this example I always assign a generated value, other examples assign a generated value only when the ID is NULL.
Getting the value
To get the generated value you can use the RETURNING-clause of the INSERT-statement:
INSERT INTO MyTable (SomeValue) VALUES ('abc') RETURNING ID
INSERT INTO ... SELECT
Using INSERT INTO ... SELECT you can select rows from one table and insert them into others. The reason it doesn't work for you is because you are trying to assign the string value ':pos' to a column of type INTEGER, and that is not allowed.
Assuming I have another table MyOtherTable with a similar structure as MyTable I can transfer values using:
INSERT INTO MyTable (SomeValue)
SELECT SomeOtherValue
FROM MyOtherTable
Using INSERT INTO ... SELECT it is not possible to obtain the generated values unless only a single row was inserted.
Guesswork with regard to POSID
It is not clear to me what POSID is supposed to be, and what values it should have. It looks like you want to have an increasing value starting at 1 for a single INSERT INTO ... SELECT. In versions of Firebird up to 2.5 that is not possible in this way (in Firebird 3 you would be able to use ROW_NUMBER() for this).
If my guess is right, then you will need to use an EXECUTE BLOCK (or a stored procedure) to assign and increase the value for every row to be inserted.
The execute block would be something like:
EXECUTE BLOCK
AS
DECLARE posid INTEGER = 1;
DECLARE someothervalue VARCHAR(255);
BEGIN
FOR SELECT SomeOtherValue FROM MyOtherTable INTO :someothervalue DO
BEGIN
INSERT INTO MyTable (SomeValue, posid) VALUES (:someothervalue, :posid);
posid = posid + 1;
END
END
Without an ORDER BY with the SELECT the value of posid is essentially meaningless, because there is no guaranteed order.