How can I add a column and populate it with a number after sorting a table? - sql

I have this table:
CREATE TABLE [dbo].[Phrase]
(
[PhraseId] [uniqueidentifier] NOT NULL,
[PhraseNum] [int] NULL
[English] [nvarchar](250) NOT NULL,
PRIMARY KEY CLUSTERED ([PhraseId] ASC)
) ON [PRIMARY]
GO
What I would like to do is to sort the table and populate the column PhraseNum with a number where the first row of the sorted table is 1 and each row after that has a value one larger than the one before.

Alan, if data in PhraseNum is not important (as I could see in your post), you can drop that column and add as an identity column
ALTER TABLE Phrase drop column PhraseNum ;
ALTER TABLE Phrase Add PhraseNum int identity(1,1) not null;
The numbering of PhraseNum will be done by the cluster index sorting criteria, so by PhraseId
But it is safe to test on a test database first

In SQL Server, you would do this with an identity column:
CREATE TABLE [dbo].[Phrase](
[PhraseId] [uniqueidentifier] NOT NULL PRIMARY KEY,
[PhraseNum] int identity(1, 1) NOT NULL
[English] [nvarchar](250) NOT NULL
);
Having a unique identify as a (clustered) primary key is a really, really bad idea. Why? New values are not ordered. That means that the data has to be re-ordered for each insert -- causing fragmentation.
You should use the PhraseNum column as the primary key.

Related

Create an auto incrementing alpha numeric primary key in SQL Server Management Studio

I have a Student table in SQL Server database which is as follows:
CREATE TABLE [dbo].[Student] (
[Id] INT NOT NULL IDENTITY,
[Name] NVARCHAR (50) NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
I want the Id property to be alpha-numeric and auto-increment itself for a new entry. I want Id to be S<number> and then S<number+1> and so on.
I tried to solve this problem as a two-step process:
(i) I first tried to make the Id an auto-incrementing property by doing this:
Then I pressed "Update":
And then I updated again and it led me to this table:
CREATE TABLE [dbo].[Student] (
[Id] INT NOT NULL IDENTITY,
[Name] NVARCHAR (50) NOT NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
);
I do not think Id is an auto-incrementing value yet. How can I make it both auto-incrementing and alpha-numeric from the following interface:
It seems that you don't really want a fully auto-incrementing alphanumeric column A001,A002...B001, you just want a regular integer column with a prefix of S. For this you can use a simple computed column
ALTER TABLE Student
ADD MyId AS CONCAT('S', Id);

SQL Server CHECK Constraint on value from a column not referenced explicitly

Similar question has been asked here: constraint check against values of foreign key , and here Having data from another table put in into check constraint , but it's not exactly what I mean (or whether there's any other way than a function).
My question is. Instead of referencing composite FOREIGN KEY, and then checking for the value of a referenced column via CHECK constraint, can I reference surrogate PK from Table A in Table B and check against a value of the column in Table A in a row identified uniquely by a surrogate PK from Table A called e.g. [test_table_a_id]
In other words. Can I have a CHECK CONSTRAINT in Table B without referencing specific column explicitly, but instead referencing id of a row and looking up the value in Table A. (Hope I am not being too consusing with the problem description)
-- ===============================
-- ---------- TABLE A ------------
CREATE TABLE [r_Test_Table_A](
[test_table_a_id] [int] IDENTITY(1,1)
CONSTRAINT [PK_r_test_table_a] PRIMARY KEY CLUSTERED ([test_table_a_id] ASC)
, [a_attribute_1] [nvarchar] (50) NOT NULL
, [a_attribute_2] [nvarchar] (50) NOT NULL
, CONSTRAINT [ak_r_Test_Table_A_a_attribute1_a_attribute_2]
UNIQUE ([a_attribute_1], [a_attribute_2])
)
-- ===============================
-- ---------- TABLE B ------------
CREATE TABLE [r_Test_Table_B](
[test_table_b_id] [int] IDENTITY(1,1)
CONSTRAINT [PK_r_test_table_b] PRIMARY KEY CLUSTERED ([test_table_b_id] ASC)
, [b_attribute_1] [nvarchar] (50) NOT NULL
, [a_attribute_1] [nvarchar] (50) NOT NULL
CONSTRAINT [ch_Test_Table_A_a_attribute_1] CHECK ([a_attribute_1] = 'Cookie')
, [a_attribute_2] [nvarchar] (50) NOT NULL
, CONSTRAINT [fk_comp_r_Test_Table_B_r_Test_Table_A]
FOREIGN KEY ([a_attribute_1], [a_attribute_2])
REFERENCES [r_Test_Table_A]([a_attribute_1], [a_attribute_2])
)

Composite Keys and Referential Integrity in T-SQL

Is it possible, in T-SQL, to have a relationship table with a composite key composed of 1 column defining Table Type and another column defining the Id of a row from a table referenced in the Table Type column?
For a shared-email address example:Three different user tables (UserA, UserB, UserC)One UserType Table (UserType)One Email Table (EmailAddress)One Email-User Relationship Table (EmailRelationship)The EmailRelationship Table contains three columns, EmailId, UserTypeId and UserId
Can I have a relationship from each User table to the EmailRelationship table (or some other way?) to maintain referential integrity?
I've tried making all three columns in the EmailRelationship table into primary keys, I've tried making only UserTypeId and UserId primary.
CREATE TABLE [dbo].[UserType](
[Id] [int] IDENTITY(1,1) NOT NULL ,
[Type] [varchar](50) NOT NULL)
insert into [dbo].[UserType]
([Type])
values
('A'),('B'),('C')
CREATE TABLE [dbo].[UserA](
[Id] [int] IDENTITY(1,1) NOT NULL,
[UserTypeId] [int] NOT NULL,
[Name] [varchar](50) NOT NULL)
insert into [dbo].[UserA]
(UserTypeId,Name)
values
(1,'UserA')
CREATE TABLE [dbo].[UserB](
[Id] [int] IDENTITY(1,1) NOT NULL,
[UserTypeId] [int] NOT NULL,
[Name] [varchar](50) NOT NULL)
insert into [dbo].[UserB]
(UserTypeId,Name)
values
(2,'UserB')
CREATE TABLE [dbo].[UserC](
[Id] [int] IDENTITY(1,1) NOT NULL,
[UserTypeId] [int] NOT NULL,
[Name] [varchar](50) NOT NULL)
insert into [dbo].[UserC]
(UserTypeId,Name)
values
(3,'UserC')
CREATE TABLE [dbo].[Email](
[Id] [int] IDENTITY(1,1) NOT NULL,
[EmailAddress] [varchar](50) NOT NULL)
insert into [dbo].[email]
(EmailAddress)
values
('SharedEmail#SharedEmail.com')
CREATE TABLE [dbo].[EmailRelationship](
[EmailId] [int] NOT NULL,
[UserTypeId] [int] NOT NULL,
[UserId] [int] NOT NULL)
insert into [dbo].[EmailRelationship]
(EmailId, UserTypeId, UserId)
values
(1,1,1),(1,2,1),(1,3,1)
No there isn't, a foreign key can refer to one table, and one table only, I can think of three ways you could approach this.
The first is to have 3 columns, one for each user table, each column with a foreign key, and a check constraint to check that at one, and only one of the values is not null
CREATE TABLE dbo.EmailRelationship
(
EmailId INT NOT NULL,
UserTypeId INT NOT NULL,
UserAId INT NULL,
UserBId INT NULL,
UserCId INT NULL,
CONSTRAINT FK_EmailRelationship__UserAID FOREIGN KEY (UserAId)
REFERENCES dbo.UserA (Id),
CONSTRAINT FK_EmailRelationship__UserBID FOREIGN KEY (UserBId)
REFERENCES dbo.UserB (Id),
CONSTRAINT FK_EmailRelationship__UserCID FOREIGN KEY (UserCId)
REFERENCES dbo.UserC (Id),
CONSTRAINT CK_EmailRelationship__ValidUserId CHECK
(CASE WHEN UserTypeID = 1 AND UserAId IS NOT NULL AND ISNULL(UserBId, UserCId) IS NULL THEN 1
WHEN UserTypeID = 2 AND UserBId IS NOT NULL AND ISNULL(UserAId, UserCId) IS NULL THEN 1
WHEN UserTypeID = 3 AND UserCId IS NOT NULL AND ISNULL(UserAId, UserBId) IS NULL THEN 1
ELSE 0
END = 1)
);
Then as a quick example trying to insert a UserAId with a user Type ID of 2 gives you an error:
INSERT EmailRelationship (EmailID, UserTypeID, UserAId)
VALUES (1, 1, 1);
The INSERT statement conflicted with the CHECK constraint "CK_EmailRelationship__ValidUserId".
The second approach is to just have a single user table, and store user type against it, along with any other common attributes
CREATE TABLE dbo.[User]
(
Id INT IDENTITY(1, 1) NOT NULL,
UserTypeID INT NOT NULL,
Name VARCHAR(50) NOT NULL,
CONSTRAINT PK_User__UserID PRIMARY KEY (Id),
CONSTRAINT FK_User__UserTypeID FOREIGN KEY (UserTypeID) REFERENCES dbo.UserType (UserTypeID),
CONSTRAINT UQ_User__Id_UserTypeID UNIQUE (Id, UserTypeID)
);
-- NOTE THE UNIQUE CONSTRAINT, THIS WILL BE USED LATER
Then you can just use a normal foreign key constraint on your email relationship table:
CREATE TABLE dbo.EmailRelationship
(
EmailId INT NOT NULL,
UserId INT NOT NULL,
CONSTRAINT PK_EmailRelationship PRIMARY KEY (EmailID),
CONSTRAINT FK_EmailRelationship__EmailId
FOREIGN KEY (EmailID) REFERENCES dbo.Email (Id),
CONSTRAINT FK_EmailRelationship__UserId
FOREIGN KEY (UserId) REFERENCES dbo.[User] (Id)
);
It is then no longer necessary to store UserTypeId against the email relationship because you can join back to User to get this.
Then, if for whatever reason you do need specific tables for different user types (this is not unheard of), you can create these tables, and enforce referential integrity to the user table:
CREATE TABLE dbo.UserA
(
UserID INT NOT NULL,
UserTypeID AS 1 PERSISTED,
SomeOtherCol VARCHAR(50),
CONSTRAINT PK_UserA__UserID PRIMARY KEY (UserID),
CONSTRAINT FK_UserA__UserID_UserTypeID FOREIGN KEY (UserID, UserTypeID)
REFERENCES dbo.[User] (Id, UserTypeID)
);
The foreign key from UserID and the computed column UserTypeID back to the User table, ensures that you can only enter users in this table where the UserTypeID is 1.
A third option is just to have a separate junction table for each User table:
CREATE TABLE dbo.UserAEmailRelationship
(
EmailId INT NOT NULL,
UserAId INT NOT NULL,
CONSTRAINT PK_UserAEmailRelationship PRIMARY KEY (EmailId, UserAId),
CONSTRAINT FK_UserAEmailRelationship__EmailId FOREIGN KEY (EmailId)
REFERENCES dbo.Email (Id),
CONSTRAINT FK_UserAEmailRelationship__UserAId FOREIGN KEY (UserAId)
REFERENCES dbo.UserA (Id)
);
CREATE TABLE dbo.UserBEmailRelationship
(
EmailId INT NOT NULL,
UserBId INT NOT NULL,
CONSTRAINT PK_UserBEmailRelationship PRIMARY KEY (EmailId, UserBId),
CONSTRAINT FK_UserBEmailRelationship__EmailId FOREIGN KEY (EmailId)
REFERENCES dbo.Email (Id),
CONSTRAINT FK_UserBEmailRelationship__UserBId FOREIGN KEY (UserBId)
REFERENCES dbo.UserB (Id)
);
Each approach has it's merits and drawbacks, so you would need to assess what is best for your scenario.
No it does not work that way. You cannot use a column value as a dynamic reference to different tables.
In general the data design is flawed.
Thanks to #GarethD I created a CHECK constraint that called a scalar-function that would enforce referential integrity (only upon insert, refer to caveat below):
Using my above example:
alter FUNCTION [dbo].[UserTableConstraint](#Id int, #UserTypeId int)
RETURNS int
AS
BEGIN
IF EXISTS (SELECT Id From [dbo].[UserA] WHERE Id = #Id and UserTypeId = #UserTypeId)
return 1
ELSE IF EXISTS (SELECT Id From [dbo].[UserB] WHERE Id = #Id and UserTypeId = #UserTypeId)
return 1
ELSE IF EXISTS (SELECT Id From [dbo].[UserC] WHERE Id = #Id and UserTypeId = #UserTypeId)
return 1
return 0
end;
alter table [dbo].[emailrelationship]
--drop constraint CK_UserType
with CHECK add constraint CK_UserType
CHECK([dbo].[UserTableConstraint](UserId,UserTypeId) = 1)
I am sure there is a not insignificant overhead to a Scalar-function call from within a CONSTRAINT. If the above becomes prohibitive I will report back here, though the tables in question will not have to deal with a large volume of INSERTs.
If there are any other reasons to not do the above, I would like to hear them. Thanks!
Update:
I've tested INSERT and UPDATE with 100k rows (SQL Server 2014, 2.1ghz quadcore w/ 8gb ram):
INSERT takes 2 seconds with out the CONSTRAINT
and 3 seconds with the CHECK CONSTRAINT
Turning on IO and TIME STATISTICS causes the INSERT tests to run in:
1.7 seconds with out the CONSTRAINT
and 10 seconds with the CHECK CONSTRAINT
I left the STATISTICS on for the UPDATE 100k rows test:
just over 1sec with out the CONSTRAINT
and 1.5sec with the CHECK CONSTRAINT
My referenced tables (UserA, UserB, UserC from my example) only contain around 10k rows each, so anybody else looking to implement the above may want to run some additional testing, especially if your referenced tables contain millions of rows.
Caveat:
The above solution may not be suitable for most uses, as the only time referential integrity is checked is during the CHECK CONSTRAINT upon INSERT. Any other operations or modifications of the data needs to take that into account. For example, using the above, if an Email is deleted any related EmailRelationship entries will be pointing to invalid data.

How to insert a duplicate value in primary key column?

I have a table that has primary key on first column, but when I see the data available of this table, I find that records are repeated include primary key data. I want to know how can it be possible? Does primary column has repeated data in SQL Server 2008?
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[DemoTbl](
[ProcedureId] [int] NOT NULL,
[ProcedureName] [nvarchar](100) NOT NULL,
[VersionNo] [char](5) NULL,
[PublishDate] [datetime] NULL,
[PublishUser] [varchar](50) NULL,
[SpecialtyId] [int] NOT NULL,
[ProcedureNumber] [varchar](20) NULL,
[PowerpointName] [nvarchar](100) NULL,
[Duration] [int] NOT NULL,
[LanguageId] [int] NOT NULL,
[TierId] [smallint] NOT NULL,
[PrintPdf] [bit] NULL,
[PresentationModeId] [tinyint] NULL,
CONSTRAINT [pk_DemoTbl] PRIMARY KEY CLUSTERED
(
[ProcedureId] ASC,
[LanguageId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [dbo].[DemoTbl] WITH CHECK ADD FOREIGN KEY([PresentationModeId])
REFERENCES [dbo].[DemoTbl] ([PresentationModeId])
GO
ALTER TABLE [dbo].[DemoTbl] WITH CHECK ADD CONSTRAINT [FK_DemoTbl_Specialty] FOREIGN KEY([SpecialtyId])
REFERENCES [dbo].[Specialty] ([SpecialtyId])
GO
ALTER TABLE [dbo].[DemoTbl] CHECK CONSTRAINT [FK_DemoTbl_Specialty]
GO
ALTER TABLE [dbo].[DemoTbl] WITH CHECK ADD CONSTRAINT [FK_DemoTbl_TierMaster] FOREIGN KEY([TierId])
REFERENCES [dbo].[TierMaster] ([TierId])
GO
ALTER TABLE [dbo].[DemoTbl] CHECK CONSTRAINT [FK_DemoTbl_TierMaster]
GO
ALTER TABLE [dbo].[DemoTbl] ADD CONSTRAINT [DF_DemoTbl_CreationDate] DEFAULT (getdate()) FOR [CreationDate]
GO
ALTER TABLE [dbo].[DemoTbl] ADD CONSTRAINT [DF_DemoTbl_ModifiedDate] DEFAULT (getdate()) FOR [ModifiedDate]
GO
ALTER TABLE [dbo].[DemoTbl] ADD CONSTRAINT [DF_DemoTbl_IsActive] DEFAULT ((1)) FOR [IsActive]
GO
ALTER TABLE [dbo].[DemoTbl] ADD CONSTRAINT [DF_DemoTbl_LanguageId] DEFAULT ((1)) FOR [LanguageId]
GO
ALTER TABLE [dbo].[DemoTbl] ADD CONSTRAINT [DF_DemoTbl_ManageContent] DEFAULT ('false') FOR [ManageContent]
GO
ALTER TABLE [dbo].[DemoTbl] ADD CONSTRAINT [DF__Procedure__Print__1B14C01D] DEFAULT ((1)) FOR [PrintPdf]
GO
No, it is not possible in SQL Server to insert records that have duplicate values in the Primary Key.
It is not possible to have more than one row in the same table with the same primary key (PK).
If you are doing a SELECT and getting more than one row with the same PK could be because:
That table actually does not have a PK, maybe it was not properly created. You can check this by doing the following (it should return the PK column(s)):
SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'TableName' AND COLUMN_KEY='PRI';
The SELECT statement is not correct. Maybe you are selecting from two or more tables and you are not relating them correctly.
To clarify, you can do the following:
1- Create a new table:
CREATE TABLE IF NOT EXISTS `Customer` (
`id` int(4),
`name` varchar(20),
`surname` varchar(40),
`phone` int(9),
PRIMARY KEY (`id`)
) ENGINE=INNODB;
Now you have a new table for storing customers identified by an ID.
2- Let's add some customers:
INSERT INTO Customer VALUES(111, 'John', 'White', 123456789),
(222, 'Bianca', 'Williams', 987654321),
(333, 'Mikel', 'Peterson', 421345642),
(444, 'Jolene', 'Gomez', 948113552);
3- If you try to insert a customer with an existing PK (id) you will get an error:
INSERT INTO Customer VALUES (222, 'Paul', 'Brown', 123412345);
4- In order to check the final table, you can do the following select:
SELECT * FROM Customer;
No it is not possible to have duplicate primary keys if the primary key constraint has been set to True.
Further to prevent duplicate primary keys set the Identity Specification to True
If this table is an intermediate table in a many to many connection the original columns are foreign keys and able to accept many instances of the same prodId(the whole point of the exercise). If someone then slams a PK on that column then no more duplicates can be added but if you query it the table will still return the original stuff in this guys screenshot.
This scenario can be seen in the Northwind sample database between Products and orders (OrderDetails table has composite PK on OrderID, ProdID but still shows duplicates of both). The PK was added afterwards, after data had been added.
The intermediate table is a link between the Many-to-Many tables and in general doesn't need a PK. Normalization 101.
For PK Constraint it create a Unique Index (Clustered / non Clustered ) on defined PK. If we disable indexes before loading that include PK Unique Index as well than we can insert duplicate PK in the column because the PK constraint is disabled. So while disabling Indexes please avoid PK index not be disable "is_primary_key=0 to disable all other indexes"
Tested -> it's possible if we create a primary key with nonclustered index. If we disable this index we will be able to insert duplicated record then. But - it won't be possible to enable (rebuild) the index back with duplicated values on primary key

Making one of my columns default the DateCreated to current time

I have the following SQL definition:
CREATE TABLE [dbo].[James] (
[JamesID] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (255) NOT NULL,
[DateCreated] DATETIME NULL,
CONSTRAINT [PK_dbo.James] PRIMARY KEY CLUSTERED ([JamesID] ASC)
);
How might I make it so new entries have the DateCreated filled out automatically when I create new entries.
What about existing data that has not had that column filled out?
If you are starting from scratch and assuming this is SQL Server:
CREATE TABLE [dbo].[James] (
[JamesID] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (255) NOT NULL,
[DateCreated] DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT [PK_dbo.James] PRIMARY KEY CLUSTERED ([JamesID] ASC)
);
If you want to update the table you can use this:
ALTER TABLE dbo.James
ADD CONSTRAINT DF_namehere DEFAULT CURRENT_TIMESTAMP FOR DateCreated;
However, any current NULL values will remain NULL with the ALTER TABLE solution. How you want to address this depends if you want to backfill information.