Oracle update one table in relation to entering data into another table - sql

the scenario is simple
CREATE TABLE one
(ID NUMBER CONSTRAINT
pk_id PRIMARY KEY,
Number_of_relations INTEGER)
CREATE TABLE two
(mock_id NUMBER,
CONSTRAINT fk_id FOREIGN KEY (mock_id) REFERENCES one(ID)
text VARCHAR)
is it possible to update Number_of_relations every time you enter a new row into table two. so if i enter ID in table one as '1', at the moment Number_of_relations is at '0', but if i add ('1', 'hello') and ('1', 'helloagain') into table two, now id '1' has two texts to it, but i want the number_of_relations to update (automatically if possible) in table one to '2'. is it possible?, thanks in advance.

This trigger does it:
create or replace trigger ins_two after insert on two
for each row
update one set number_of_relations = number_of_relations + 1
where one.id = :new.mock_id
Test:
insert into two values (1, 'hello');
insert into two values (1, 'hello again');
insert into two values (2, 'hello');
select * from one
ID NUMBER_OF_RELATIONS
---------- -------------------
1 2
2 1
This is example for INSERT action, you can add also sections for DELETE and UPDATE.
In "DELETE" case number_of_relations has to be decremented,
in "UPDATE" - it depends what column was updated, but logic is similar.
Triggers documentation and examples.

Related

How can I insert a row that references another postgres table via foreign key, and creates the foreign row too if it doesn't exist?

In Postgres, is there a way to atomically insert a row into a table, where one column references another table, and we look up to see if the desired row exists in the referenced table and inserts it as well if it is not?
For example, say we have a US states table and a cities table which references the states table:
CREATE TABLE states (
state_id serial primary key,
name text
);
CREATE TABLE cities (
city_id serial,
name text,
state_id int references states(state_id)
);
When I want to add the city of Austin, Texas, I want to be able to see whether Texas exists in the states table, and if so use its state_id in the new row I'm inserting in the cities table. If Texas doesn't exist in the states table, I want to create it and then use its id in the cities table.
I tried this query, but I got an error saying
ERROR: WITH clause containing a data-modifying statement must be at the top level
LINE 2: WITH inserted AS (
^
WITH state_id AS (
WITH inserted AS (
INSERT INTO states(name)
VALUES ('Texas')
ON CONFLICT DO NOTHING
RETURNING state_id),
already_there AS (
SELECT state_id FROM states
WHERE name='Texas')
SELECT * FROM inserted
UNION
SELECT * FROM already_there)
INSERT INTO cities(name, state_id)
VALUES
('Austin', (SELECT state_id FROM state_id));
Am I overlooking a simple solution?
Here is one option:
with inserted as (
insert into states(name) values ('Texas')
on conflict do nothing
returning state_id
)
insert into cities(name, state_id)
values (
'Dallas',
coalesce(
(select state_id from inserted),
(select state_id from states where name = 'Texas')
)
);
The idea is to attempt to insert in a CTE, and then, in the main insert, check if a value was inserted, else select it.
For this to work properly, you need a unique constraint on states(name):
create table states (
state_id serial primary key,
name text unique
);
Demo on DB Fiddlde
You can force the insert statement to return a value:
WITH inserted AS (
INSERT INTO states (name)
VALUES ('Texas')
ON CONFLICT (name) DO UPDATE SET name = EXCLUDED.NAME
RETURNING state_id
)
. . .
The DO UPDATE SET forces the INSERT to return something.
I notice that you don't have a unique constraint, so you also need that:
ALTER TABLE states ADD CONSTRAINT unq_state_name
UNIQUE (name);
Otherwise the ON CONFLICT doesn't have anything to work with.

INSERT INTO with default values for a single column

I have a problem to insert the data into 1 table with 1 column
Name: user_id
Column: id
I am trying to add 1 line in this column with this query:
INSERT INTO user_id (id) VALUES ()
The problem is the above is invalid, I want the id take the last value id +1
This is not a syntax problem because this query works:
INSERT INTO user_id (id) VALUES (4)
So, I do not really know how to solve this problem.
Assuming the id column is defined as serial or identity you can specify a column list and set the column value to default:
insert into user_id (id) values (default);
This also works if you have more columns, e.g:
insert into users (id, firstname, lastname)
values (default, 'Arthur', 'Dent');
Or you can leave out the column list completely and request the default value(s) for all columns:
insert into user_id default values;
SQL supports the default values statement.
So this will work:
create table t (id serial primary key);
insert into t
default values;
The syntax is described in the documentation.

How to automatically set the value of a not primary key column with the value of the primary key in an insert query?

I am working on an INSERT query that insert a new row in a table named VulnerabilityAlertDocument
This table have 3 fields:
Id: that is an auto increment int and it is my PRIMARY KEY
VulnerabilityAlertId: this field is an int and must have the same value of **Id column
SourceId: is a varchar(50) and contains some text.
Now my problem is how to do that the VulnerabilityAlertId have the same value of the *auto increment Id value for this new record.
If I do something like:
INSERT INTO VulnerabilityAlertDocument
( [VulnerabilityAlertId], [SourceId] )
VALUES
(4, 'TEST');
it create a new record in the table in whic the Id column value is not specify because it is auto increment, and the VulnerabilityAlertId value is 4
I need that in this query, the VulnerabilityAlertId value is automatically setted with the value of the Id value of the new row
What can I do to do it?
As I understand your question is it like you want the VulnerabilityAlertId value automatically setted with the value of the Id column of the new row always?
If yes then you should alter the table definition and add VulnerabilityAlertId column as a persisted computed column as:
alter table VulnerabilityAlertDocument drop column VulnerabilityAlertId;
alter table VulnerabilityAlertDocument add VulnerabilityAlertId as id persisted;
by doing this there's no need to specify the values for VulnerabilityAlertId column as it will be computed every time a new row is inserted.
Hope this helps!!
Here's the way I'd skin this cat...
I would amend my table definition (apologies, can't think of a better field name right now):
CREATE TABLE your_table (
Id int identity(1,1) NOT NULL
, SourceId varchar(50) NULL
, ActualVulnerabilityAlertId int NULL
, VulnerabilityAlertId As Coalesce(ActualVulnerabilityAlertId, Id)
);
Essentially I have changed VulnerabilityAlertId to be a calculated field and added a new field to hold the "raw" data.
You can then populate the table like so:
INSERT INTO your_table (SourceId)
VALUES ('TEST 1');
INSERT INTO your_table (SourceId)
VALUES ('TEST 2');
INSERT INTO your_table (SourceId)
VALUES ('TEST 3');
INSERT INTO your_table (SourceId, ActualVulnerabilityAlertId)
VALUES ('TEST 4', 12345); -- Non-"default" value!
If you need to update a value then you need to refer to the ActualVulnerabilityAlertId field:
UPDATE your_table
SET ActualVulnerabilityAlertId = 937
WHERE SourceId = 'TEST 2';
Results:
SELECT Id
, SourceId
, ActualVulnerabilityAlertId
, VulnerabilityAlertId
FROM your_table
ORDER
BY Id;
Id SourceId VulnerabilityAlertId ActualVulnerabilityAlertId
-- -------- -------------------- --------------------------
1 TEST 1 1 NULL
2 TEST 2 937 937
3 TEST 3 3 NULL
4 TEST 4 12345 12345
There are 2 ways..
If you can split this into multiple statements, use ##Identity after inserting the record to update the recently inserted row.
or
2. make use of below built-in sql server function.
IDENT_CURRENT('table_name') + 1
If you want to populate VulnerabilityID with the newly created identity .
.then you have to ##Idenity to retrieve the latest identity generated and then send it to insert statement of VulnerabilityiD
For ex:
declare #vulid int
insert into tablename(params) --> Normal insert statement that generates identity
now, get that latest identity created by using
set #vulid=(select ##Identity) -> newly generated identity is stored in this varaibale now
Insert this into table now..
updatetablename set vulnerabilityid=#vulid;
This will ensure that latest generated id is inserted as Vulnerabilityid
Hope this helps..

SQLite Insert or Replace Where

i have a table with 3 columns, ID, Description and Key where ID is not a primary key!
What i want is to insert or update/replace a current record.
Example:
decryptionKeys
ID Description Key
999 Birthday 24.12.1988
I tried this but it won't work:
INSERT OR REPLACE INTO decryptionKeys VALUES ("999","Birthday","25.12.1988") WHERE ID="999" AND Description="Birthday"
leave thr where clause
INSERT OR REPLACE INTO decryptionKeys VALUES ("999","Birthday","25.12.1988")

SQL can I have a "conditionally unique" constraint on a table?

I've had this come up a couple times in my career, and none of my local peers seems to be able to answer it. Say I have a table that has a "Description" field which is a candidate key, except that sometimes a user will stop halfway through the process. So for maybe 25% of the records this value is null, but for all that are not NULL, it must be unique.
Another example might be a table which must maintain multiple "versions" of a record, and a bit value indicates which one is the "active" one. So the "candidate key" is always populated, but there may be three versions that are identical (with 0 in the active bit) and only one that is active (1 in the active bit).
I have alternate methods to solve these problems (in the first case, enforce the rule code, either in the stored procedure or business layer, and in the second, populate an archive table with a trigger and UNION the tables when I need a history). I don't want alternatives (unless there are demonstrably better solutions), I'm just wondering if any flavor of SQL can express "conditional uniqueness" in this way. I'm using MS SQL, so if there's a way to do it in that, great. I'm mostly just academically interested in the problem.
If you are using SQL Server 2008 a Index filter would maybe your solution:
http://msdn.microsoft.com/en-us/library/ms188783.aspx
This is how I enforce a Unique Index with multiple NULL values
CREATE UNIQUE INDEX [IDX_Blah] ON [tblBlah] ([MyCol]) WHERE [MyCol] IS NOT NULL
In the case of descriptions which are not yet completed, I wouldn't have those in the same table as the finalized descriptions. The final table would then have a unique index or primary key on the description.
In the case of the active/inactive, again I might have separate tables as you did with an "archive" or "history" table, but another possible way to do it in MS SQL Server at least is through the use of an indexed view:
CREATE TABLE Test_Conditionally_Unique
(
my_id INT NOT NULL,
active BIT NOT NULL DEFAULT 0
)
GO
CREATE VIEW dbo.Test_Conditionally_Unique_View
WITH SCHEMABINDING
AS
SELECT
my_id
FROM
dbo.Test_Conditionally_Unique
WHERE
active = 1
GO
CREATE UNIQUE CLUSTERED INDEX IDX1 ON Test_Conditionally_Unique_View (my_id)
GO
INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
VALUES (1, 0)
INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
VALUES (1, 0)
INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
VALUES (1, 0)
INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
VALUES (1, 1)
INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
VALUES (2, 0)
INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
VALUES (2, 1)
INSERT INTO dbo.Test_Conditionally_Unique (my_id, active)
VALUES (2, 1) -- This insert will fail
You could use this same method for the NULL/Valued descriptions as well.
Thanks for the comments, the initial version of this answer was wrong.
Here's a trick using a computed column that effectively allows a nullable unique constraint in SQL Server:
create table NullAndUnique
(
id int identity,
name varchar(50),
uniqueName as case
when name is null then cast(id as varchar(51))
else name + '_' end,
unique(uniqueName)
)
insert into NullAndUnique default values
insert into NullAndUnique default values -- Works
insert into NullAndUnique default values -- not accidentally :)
insert into NullAndUnique (name) values ('Joel')
insert into NullAndUnique (name) values ('Joel') -- Boom!
It basically uses the id when the name is null. The + '_' is to avoid cases where name might be numeric, like 1, which could collide with the id.
I'm not entirely aware of your intended use or your tables, but you could try using a one to one relationship. Split out this "sometimes" unique column into a new table, create the UNIQUE index on that column in the new table and FK back to the original table using the original tables PK. Only have a row in this new table when the "unique" data is supposed to exist.
OLD tables:
TableA
ID pk
Col1 sometimes unique
Col...
NEW tables:
TableA
ID
Col...
TableB
ID PK, FK to TableA.ID
Col1 unique index
Oracle does. A fully null key is not indexed by a Btree in index in Oracle, and Oracle uses Btree indexes to enforce unique constraints.
Assuming one wished to version ID_COLUMN based on the ACTIVE_FLAG being set to 1:
CREATE UNIQUE INDEX idx_versioning_id ON mytable
(CASE active_flag WHEN 0 THEN NULL ELSE active_flag END,
CASE active_flag WHEN 0 THEN NULL ELSE id_column END);