How to create an insert trigger based on primary key? - sql

I am stuck on a problem creating a trigger in SQL Server. I have MyTable with the following columns (simplified):
Sn - Identity, not nullable;
SomeId - int, not nullable;
miscellaneous other columns
The Sn column is nothing special, values 1,2...n.
SomeId needs to be 1000 + Sn, i.e. this is what I want the trigger to do on insert.
The problem I am having is a standard trigger fails if I don't include something for SomeId (error is that null is not allowed), if that trigger is using after insert. Maybe I am meant to use instead of insert, but I am having trouble getting that to work correct or find details about it.
The other factor here - I am not even sure if it is possible for what I am trying to do to work. I.e when a new row is being created and SQL Server generates an Sn (Identity column), can a trigger be part of that process and also compute the SomeId value (which needs the Sn value) before inserting?
If not, as a fallback I could either make the SomeId column nullable (not desirable), or always insert 0 into it (and let the trigger fire afterwards to update it), but that would be a bit grim also.

No need for a trigger. Just use a SEQUENCE to generate the ID values instead of an IDENTITY column, eg
create sequence seq_MyTable
start with 1
increment by 1
go
CREATE TABLE MyTable
(
Sn int not null primary key default (next value for seq_MyTable),
SomeID int not null unique default (next value for seq_MyTable) + 1000,
Name VARCHAR(50) NOT NULL
)
insert into MyTable(Name)
values ('A'),('B'),('C')
select * from Mytable

Related

Is it Possible to recover from a temporal table?

is it Possible to recover from a temporal table?
I defined 2 tables like this:
create table lib.x(
"ID" INTEGER GENERATED ALWAYS AS IDENTITY (
START WITH 1 INCREMENT BY 1
NO MINVALUE NO MAXVALUE
NO CYCLE NO ORDER
CACHE 20
),
char char(1),
row_start TIMESTAMP(12) NOT NULL GENERATED ALWAYS AS ROW BEGIN IMPLICITLY hidden,
row_end TIMESTAMP(12) NOT NULL GENERATED ALWAYS AS ROW END IMPLICITLY hidden,
row_id TIMESTAMP(12) GENERATED ALWAYS AS TRANSACTION START ID IMPLICITLY hidden,
PERIOD SYSTEM_TIME(row_start, row_end)
);
create table lib.x_history like lib.x;
alter TABLE lib.x
ADD VERSIONING USE HISTORY TABLE lib.x_history;
then I did this:
insert into lib.x(char) values('a'), ('b'), ('c');
delete from lib.x where id = 2;
Is it possible to restore the char 'b' with the ID 2?
Yes and no. It is not a system restore, but of course you can query the old state and use it to insert into the regular table. See the section "Querying system-period temporal data" in the Db2 docs.
You would first construct a query to search AS OF. Later, you could use it as input to an insert statement. Thus, you restore the value, but it is treated as deleting the value and inserting it as new.

Insert value into an old identity field

I have a table which had an identity column.
for many reasons we had to remove the identity from that column.
I have a system that inserts a value to that table by the old way, passes null for the identity column.
is there a simple way to define identity column to receive a value in case it is passed to it, and if a value of null is passed, to make that table to set a unique value to that field that is not found in that table (act like identity).
What i mean is, if there is value, insert it.
And if the system tries to insert null act like an identity.
Thanks in advance.
A trigger seems like a good choice for this situation, but there are a lot of ways you can handle the situation.
The example below creates an INSTEAD OF insert trigger that gets the columns that were supposed to be inserted, and generates a new value via a function if the ID column is null.
CREATE TRIGGER myTrigger ON myTable
INSTEAD OF INSERT
AS
INSERT INTO myTable (myIDcolumn, anotherColumn)
SELECT COALESCE(myIDColumn, dbo.SomeMethodToCreateID), anotherColumn
FROM inserted
How that SomeTheodToCreateID function is to work is up to you. One thing you could do is change the SELECT to combine max plus row_number:
SELECT (COALESCE(myIDColumn,
(SELECT MAX(myIDColumn) FROM myTable) + ROW_NUMBER() OVER(ORDER BY anotherColumn)
)
...
Try This
DBCC CHECKIDENT ( '[dbo].[Customers]', RESEED, 1 )

On SQL INSERT can I use the identity column for part of the insert data in another column at the same time?

CREATE TABLE Table1 :
Id int IDENTITY(1,1),
PK_Column1 nvarchar(50) Primary Key.
INSERT INTO Table1 (PK_Column1) VALUES ('Name'+Id)
Result:
Id PK_Column1
1 Name1
Is this possible? Or do I need to manage the Id column myself for this to work?
From the documentation:
After an INSERT, SELECT INTO, or bulk copy statement completes, ##IDENTITY contains the last identity value generated by the statement.
This applies to all the other identity checkers.
You should probably write a little SP to update the record immediately after your insert if this is what you need. Given that your primary_key appears to be some unusual composite of the ID and a varchar, you would also be best reviewing your data model.
It's important to note the difference with ##IDENTITY and SCOPE_IDENTITY():
##IDENTITY and SCOPE_IDENTITY return the last identity value generated in any table in the current session. However, SCOPE_IDENTITY returns the value only within the current scope; ##IDENTITY is not limited to a specific scope.

Primay Key conflicts on insertion of new records

In a database application, I want to insert, update and delete records in a table of database.
Table is as below:
In this table, Ga1_ID is Primary Key.
Suppose, I insert 5 records as show currently.
In second attempt, if I want to insert 5 other records and if any of these new records contains a primary key attribute which is already present in table it show error. Its fine.
But, when I insert new 5 records... how I can verify these new records's primary key value is not present. I mean, how to match or calculate the already present primary key attributes and then insert new records.
What is the best approach to manage this sort of situation ?
use following query in dataadapter:
da=new SqlDataAdapter("select Ga1_ID from table where Ga1_ID=#pkVal",conn);
DataSet=new DataSet();
da.fill(ds);
//pass parameter for #pkVal
da.SelectCommand.Parameters(1).Value = pkValue;
if(ds.Tables[0].Rows.Count>0) //If number of rows >0 then record exists
BEGIN
messagebox.show("Primary key present");
END
Hope its helpful.
Do not check existing records in advance, i.e. do not SELECT and then INSERT. A better (and pretty common) approach is to try to INSERT and handle exceptions, in particular, catch a primary key violation if any and handle it.
Do the insert in a try/catch block, with different handling in case of a primary key violation exception and other sql exception types.
If there was no exception, then job's done, record was inserted.
If you caught a primary key violation exception, then handle it appropriately (your post does not specify what you want to do in this case, and it's completely up to you)
If you want to perform 5 inserts at once and want to make sure they all succeed or else roll back if any of them failed, then do the inserts within a transaction.
you can do a lookup first before inserting.
IF EXISTS (SELECT * FROM tableName WHERE GA1_id=#newId)
BEGIN
UPDATE tableName SET Ga1_docid = #newdocID, GA1_fieldNAme = #newName, Ga1_fieldValue = #newVal where GA1_id=#newId
END
ELSE
BEGIN
INSERT INTO tableName(GA1_ID, Ga1_docid, GA1_fieldNAme Ga1_fieldValue) VALUES (value1,val2,value3,value4)
END
If you're using SQL Server 2012, use a sequence object - CREATE SEQUENCE.
This way you can get the next value using NEXT VALUE FOR.
With an older SQL Server version, you need to create the primary key field as an IDENTITY field and use the SCOPE_IDENTITY function to get the last identity value and then increment it manually.
Normally, you would like to have a surrogate key wich is generally an identity column that will automatically increment when you are inserting rows so that you don't have to care about knowing which id already exists.
However, if you have to manually insert the id there's a few alternatives for that and knowing wich SQL database you are using would help, but in most SQL implementations, you should be able to do something like:
IF NOT EXISTS
IF NOT EXISTS(
SELECT 1
FROM your_table
WHERE Ga1_ID = 1
)
INSERT INTO ...
SELECT WHERE NOT EXISTS
INSERT INTO your_table (col_1, col_2)
SELECT col_1, col_2
FROM (
SELECT 1 AS col_1, 2 AS col_2
UNION ALL
SELECT 3, 4
) q
WHERE NOT EXISTS (
SELECT 1
FROM your_table
WHERE col_1 = q.col_1
)
For MS SQL Server, you can also look at the MERGE statement and for MySQL, you can use the INSERT IGNORE statement.

Custom sort in SQL Server

I have a table where the results are sorted using an "ORDER" column, eg:
Doc_Id Doc_Value Doc_Order
1 aaa 1
12 xxx 5
2 bbb 12
3 ccc 24
My issue is to initially set up this order column as efficiently and reusably as possible.
My initial take was to set up a scalar function that could be used as a default value when a new entry is added to the table:
ALTER FUNCTION [dbo].[Documents_Initial_Order]
( )
RETURNS int
AS
BEGIN
RETURN (SELECT ISNULL(MAX(DOC_ORDER),0) + 1 FROM dbo.Documents)
When a user wants to permute 2 documents, I can then easily switch the 2 orders.
It works nicely, but I now have a second table I need to set up the same way, and I am quite sure there is a nicer way to do it. Any idea?
Based on your comment, I think you have a very workable solution. You could make it a little more userfriendly by specifying it as a default:
alter table documents
add constraint constraint_name
default (dbo.documents_initial_order()) for doc_order
As an alternative, you could create an update trigger that copies the identity field to the doc_order field after an insert:
create trigger Doc_Trigger
on Documents
for insert
as
update d
set d.doc_order = d.doc_id
from Documents d
inner join inserted i on i.doc_id = d.doc_id
Example defining doc_id as an identity column:
create table Documents (
doc_id int identity primary key,
doc_order int,
doc_value ntext
)
It sounds like you want an identity column that you can then override once it gets it initial value. One solution would be to have two columns, once call "InitialOrder", that is an auto-increment identity column, and then a second column called doc_order that initially is set to the same value as the InitialOrder field (perhaps even as part of the insert trigger or a stored procedure if you are doing inserts that way), but give the user the ability to edit that column.
It does require an extra few bytes per record, but solves your problem, and if its of any value at all, you would have both the inital document order and the user-reset order available.
Also, I am not sure if your doc_order needs to be unique or not, but if not, you can then sort return values by doc_order and InitialOrder to ensure a consistent return sequence.
If there is no need to have any control over what that DOC_ORDER value might be, try using an identity column.