SQL Server - Create auto-incrementing unique key - primary-key

Is there a way in SQL Server to create a table with a primary key that auto-increments itself? I've been looking at the "UniqueIdentifier" type, but this doesn't seem to do what I expect.
Currently, I have something like this:
CREATE TABLE [dbo].[MyTable](
[Date] [datetime] NOT NULL,
[MyField1] [nchar](50) NOT NULL,
[MyField2] [nvarchar](max) NULL,
[Key] [uniqueidentifier] NOT NULL
) ON [PRIMARY]
Basically, I want KEY to start at 1 and just increment itself for each record.

You're looking for the IDENTITY property.
From the documentation:
Creates an identity column in a table.
This property is used with the CREATE
TABLE and ALTER TABLE Transact-SQL
statements.
IDENTITY [ (seed , increment) ]
seed
Is the value that is used for the very
first row loaded into the table.
increment
Is the incremental value that is added
to the identity value of the previous
row that was loaded.
You must specify both the seed and
increment or neither. If neither is
specified, the default is (1,1).
Btw, all of this is also easily achieved from Sql Server's mgmt UI. Just right click on your table and select design. Then select the proper column and set the IDENTITY property.

Define your primary key in the table create statement like this:
[Key] [int] IDENTITY(1,1) NOT NULL

Related

SQL Server Prevent Update on Column (datetime2 column value set by database on insert)

I have a table Values with 3 columns:
CREATE TABLE [dbo].[Values]
(
[Id] [uniqueidentifier] NOT NULL,
[Value] [nvarchar](150) NOT NULL,
[CreatedOnUtc] [datatime2](7) NOT NULL
)
I want SQL Server to set the value of CreatedOnUtc to UTC-Now whenever a new entry is created, and not allow an external command to set this value.
Is this possible?
This is sort of two questions. For the first:
CREATE TABLE [dbo].[Values] (
[Id] [uniqueidentifier] NOT NULL,
[Value] [nvarchar](150) NOT NULL,
[CreatedOnUtc] [datetime2](7) NOT NULL DEFAULT SYSUTCDATETIME()
);
The canonical way to prevent changes to the column is to use a trigger that prevents the value from being updated or inserted.
Note that Values is a really bad name for a table because it is a SQL keyword and SQL Server reserved word. Choose identifiers that do not need to be escaped.
There are other ways. For instance, you could turn off DML access to the table. Then create a view without CreatedOnUtc and only allow inserts and updates through the view.

Insert into column without having IDENTITY

I have a table
CREATE TABLE [misc]
(
[misc_id] [int] NOT NULL,
[misc_group] [nvarchar](255) NOT NULL,
[misc_desc] [nvarchar](255) NOT NULL
)
where misc_id [int] not null should have been IDENTITY (1,1) but is not and now I'm having issues
With a simple form that insert into this table but since misc_id is looking for a number that a user would not know unless they have access to the database.
I know a option would be to create another column make it IDENTITY(1,1) and copy that data.
Is there another way I will be able to get around this?
INSERT INTO misc (misc_group, misc_desc)
VALUES ('#misc_group#', '#misc_desc#')
I have SQL Server 2012
You should re-create your table with the desired identity column. The following statements will get you close. SQL Server will automatically adjust the table's identity field to MAX(misc_id) + 1 as you're migrating data.
You'll obviously need to stop trying to insert misc_id with new records. You'll want to retrieve the SCOPE_IDENTITY() column after inserting records.
-- Note: I'd recommend having SSMS generate your base create statement so you know you didn't miss anything. You'll have to export the indexes and foreign keys as well. Add them after populating data to improve performance and reduce fragmentation.
CREATE TABLE [misc_new]
(
[misc_id] [int] NOT NULL IDENTITY(1,1),
[misc_group] [nvarchar](255) NOT NULL,
[misc_desc] [nvarchar](255) NOT NULL
-- Todo: Don't forget primary key but can be added later (not recommended).
)
GO
SET IDENTITY_INSERT misc_new ON;
INSERT INTO misc_new
(
[misc_id],
[misc_group],
[misc_desc]
)
SELECT
[misc_id],
[misc_group],
[misc_desc]
FROM misc
ORDER BY misc_id;
SET IDENTITY_INSERT misc_new OFF;
GO
EXEC sp_rename 'misc', 'misc_old';
EXEC sp_rename 'misc_new', 'misc';
GO
If altering the table is not an option, you can try having a different table with the latest [misc_id] value inserted, so whenever you insert a new record into the table, you retrieve this value, add 1, and use it as your new Id. Just don't forget to update the table after.
Changing a int column to an identity can cause problems because by default you cannot insert a value into an identity column without use the set identity_insert command on. So if you have existing code that inserts a value into the identity column it will fail. However its much easier to allow SQL Server to insert values(that is change it to an identity column) so I would change misc_id into an identity column and make sure that there are no programs inserting values into misc_id.
In MSSQL 2012 you can use SEQUENCE objects:
CREATE SEQUENCE [dbo].[TestSequence]
AS [BIGINT]
START WITH 1
INCREMENT BY 1
GO
Change 1 in START WITH 1 with MAX value for [misc_id] + 1.
Usage:
INSERT INTO misc (misc_id, misc_group, misc_desc)
VALUES (NEXT VALUE FOR TestSequence, '#misc_group#','#misc_desc#')

Get value of PRIMARY KEY during SELECT in ORACLE

For a specific task I need to store the identity of a row in a tabel to access it later. Most of these tables do NOT have a numeric ID and the primary key sometimes consists of multiple fields. VARCHAR & INT combined.
Background info:
The participating tables have a trigger storing delete, update and insert events in a general 'sync' tabel (Oracle v11). Every 15 minutes a script is then launched to update corresponding tables in a remote database (SQL Server 2012).
One solution I came up with was to use multiple columns in this 'sync' table, 3 INT columns and 3 VARCHAR columns. A table with 2 VARCHAR columns would then use 2 VARCHAR columns in this 'sync' table.
A better/nicer solution would be to 'select' the value of the primary key and store this in this table.
Example:
CREATE TABLE [dbo].[Workers](
[company] [nvarchar](50) NOT NULL,
[number] [int] NOT NULL,
[name] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Workers] PRIMARY KEY CLUSTERED ( [company] ASC, [number] ASC )
)
// Fails:
SELECT [PK_Workers], [name] FROM [dbo].[Workers]
UPDATE [dbo].[Workers] SET [name]='new name' WHERE [PK_Workers]=#PKWorkers
// Bad (?) but works:
SELECT ([company] + CAST([number] AS NVARCHAR)) PK, [name] FROM [dbo].[Workers];
UPDATE [dbo].[Workers] SET [name]='newname' WHERE ([company] + CAST([number] AS NVARCHAR))=#PK
The [PK_Workers] fails in these queries. Is there another way to get this value without manually combining and casting the index?
Or is there some other way to do this that I don't know?
for each table create a function returning a concatenated primary key. create a function based index on this function too. then use this function in SELECT and WHERE clauses

Insert only modified values and column names into a table

I have a sql server 2012 database. In which i have a changeLog table that contains
TableName, ColumnName, FromValue and ToValue columns. Which will be used to keep track of modified columns and data.
So if any update occur through application then only modified columns should insert into this table with its new and old value.
Can anyone help me in this.
For Example:
If the procedure updates all columns of property table (propertyName, address)
then if user update propertyName (but update also contains address column but with no data change) then only propertyName and its data will be inserted into ChangeLog table not address column and its data because address data does not contains any data change.
IF there is no other auditing requirement at all - you would not be thinking about Auditing in any way without this - then OK, go for it. However this is a very limited use of Auditing: User X changed this field at time Y. Generally this is interesting as part of a wider question: what did user X do? What happened to that customer data in the database to end up the way it is now?
Questions like that are harder to answer if you have the data structure you propose and would be quite onerous to reconstruct. My usual approach would be as follows. Starting from a base table like so (this from one of my current projects):
CREATE TABLE [de].[Generation](
[Id] [int] IDENTITY(1,1) NOT NULL,
[LocalTime] [datetime] NOT NULL,
[EntityId] [int] NOT NULL,
[Generation] [decimal](18, 4) NOT NULL,
[UpdatedAt] [datetime] NOT NULL CONSTRAINT [DF_Generation_UpdatedAt] DEFAULT (getdate()),
CONSTRAINT [PK_Generation] PRIMARY KEY CLUSTERED
(
[Id] ASC
)
(I've excluded FK definitions as they aren't relevant here.)
First create an Audit table for this table:
CREATE TABLE [de].[GenerationAudit](
[AuditId] int identity(1, 1) not null,
[Id] [int] NOT NULL,
[LocalTimeOld] [datetime] NULL,
[EntityIdOld] [int] NULL,
[GenerationOld] [decimal](18, 4) null,
[UpdatedAtOld] [datetime] null,
[LocalTimeNew] [datetime] null,
[EntityIdNew] [int] null,
[GenerationNew] [decimal](18, 4) null,
[UpdatedAtNew] [datetime] NOT NULL CONSTRAINT [DF_GenerationAudit_UpdatedAt] DEFAULT (getdate()),
[UpdatedBy] varchar(60) not null
CONSTRAINT [PK_GenerationAudit] PRIMARY KEY CLUSTERED
(
[AuditId] ASC
)
This table has an *Old and a *New version of each column that can't change. The Id, being an IDENTITY PK, can't change so no need for an old/new. I've also added an UpdatedBy column. It also has a new AuditId IDENTITY PK.
Next create three triggers on the base table: one for INSERT, one for UPDATE and one for DELETE. In the Insert trigger, insert a row into the Audit table with the New columns selected from the inserted table and the Old values as null. In the UPDATE one, the Oldvalues come from the deleted and the new from the inserted. In the DELETE trigger, old from from deleted and the new are all null.
The UPDATE trigger would look like this:
CREATE TRIGGER GenerationAuditUpdate
ON de.Generation
AFTER UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
insert into de.GenerationAudit (Id, LocalTimeOld, EntityIdOld, GenerationOld, UpdatedAtOld,
LocalTimeNew, EntityIdNew, GenerationNew, UpdatedAtNew,
UpdatedBy)
select isnull(i.Id, d.Id), d.LocalTime, d.EntityId, d.Generation, d.UpdatedAt,
i.LocalTime, i.EntityId, d.Generation, getdate(),
SYSTEM_USER)
from inserted i
full outer join deleted d on d.Id = i.Id;
END
GO
You then have a full before/after picture of each change (and it'll be faster than seperating out diffs column by column). You can create views over the Audit table to get entries where the Old value is different to the new, and include the base table Id (which you will also need in your structures!), the user who did it, and the time they did it (UpdatedAtNew).
That's my version of Auditing and it's mine!

Triggering a timestamp update

For every INSERT, how do I populate my DateStamp field with the current datetime?
I've created an error output table for my SSIS task:
Here's the table:
CREATE TABLE [dbo].[gbs_CRMErrorOutput](
[ID] [uniqueidentifier] NULL,
[ErrorCode] [nvarchar](50) NULL,
[ErrorColumn] [nvarchar](500) NULL,
[CrmErrorMessage] [nvarchar](max) NULL,
[targetid] [uniqueidentifier] NULL,
[subordinateid] [uniqueidentifier] NULL,
[DateStamp] [datetime] NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
Please note that I do not have an auto-increment or any key in the table.
I'm also wondering what would be a best practice for this?
Here is an example of using not null with a default. In your real table you may want to name your default constraint. If you define the constraint inline like this it will still be named but it will be automatically assigned.
CREATE TABLE #MyTable
(
MyID INT IDENTITY NOT NULL,
SomeValue VARCHAR(10),
DateCreated DATETIME NOT NULL DEFAULT GETDATE()
)
INSERT #MyTable(SomeValue)
VALUES ('Value1')
--This next line just waits for 1 second.
--This will demonstrate multiple inserts at different times so you can the values change
WAITFOR DELAY '00:0:01'
INSERT #MyTable(SomeValue)
VALUES ('Value2')
SELECT *
FROM #MyTable
DROP TABLE #MyTable
Two good options:
1) Create a DEFAULT CONSTRAINT on your table with GETDATE() specified for your column (good example here). Within SSIS, do not map any value to that column - leave it as Ignore. Make sure that Keep Nulls is not checked. Note that you might have to fiddle with the settings of your OLE DB Destination - uncheck Identity Insert if there's a problem. I've also seen cases where the column had to allow NULLs - that only affects certain scenarios.
2) Add a Derived Column transformation to your data flow, setting it up to add a new column to the flow. I usually use the System::StartTime variable here, so that all records inserted during a single ETL run will share the same inserted date, but you could just as easily use the SSIS function GETDATE().
Map the new column you just created to your OLE DB Destination.