Is it possible to associate Unique constraint with a Check constraint? - sql-server

I have a table access whose schema is as below:
create table access (
access_id int primary key identity,
access_name varchar(50) not null,
access_time datetime2 not null default (getdate()),
access_type varchar(20) check (access_type in ('OUTER_PARTY','INNER_PARTY')),
access_message varchar(100) not null,
)
Access types allowed are only OUTER_PARTY and INNER_PARTY.
What I am trying to achieve is that the INNER_PARTY entry should be only once per day per login (user), but the OUTER_PARTY can be recorded any number of times. So I was wondering if its possible to do it directly or if there is an idiom to create this kind of restriction.
I have checked this question: Combining the UNIQUE and CHECK constraints, but was not able to apply it to my situation as it was aiming for a different thing.

A filtered unique index can be added to the table. This index can be based on a computed column which removes the time component from the access_time column.
create table access (
access_id int primary key identity,
access_name varchar(50) not null,
access_time datetime2 not null default (SYSDATETIME()),
access_type varchar(20) check (access_type in ('OUTER_PARTY','INNER_PARTY')),
access_message varchar(100) not null,
access_date as CAST(access_time as date)
)
go
create unique index IX_access_singleinnerperday on access (access_date,access_name) where access_type='INNER_PARTY'
go
Seems to work:
--these inserts are fine
insert into access (access_name,access_type,access_message)
select 'abc','inner_party','hello' union all
select 'def','outer_party','world'
go
--as are these
insert into access (access_name,access_type,access_message)
select 'abc','outer_party','hello' union all
select 'def','outer_party','world'
go
--but this one fails
insert into access (access_name,access_type,access_message)
select 'abc','inner_party','hello' union all
select 'def','inner_party','world'
go

unfortunately you cant add a "if" on a check constraint. I advise using a trigger:
create trigger myTrigger
on access
instead of insert
as
begin
declare #access_name varchar(50)
declare #access_type varchar(20)
declare #access_time datetime2
select #access_name = access_name, #access_type= access_type, #access_time=access_time from inserted
if exists (select 1 from access where access_name=#access_name and access_type=#access_type and access_time=#access_time) begin
--raise excetion
end else begin
--insert
end
end
you will have to format the #access_time to consider only the date part

Related

How to assign a permanent unique ID with T-SQL

Can someone let me know how to permanently assign a unique ID to a field?
I have the following table:
CREATE TABLE PrestigeCars.Reference.Staff
(
StaffName NVARCHAR(50) NULL,
ManagerID INT NULL,
Department NVARCHAR(50) NULL
) ON [PRIMARY]
GO
The following code assigns a new id field to to the table called 'myuniqueID'
SELECT
Staff.StaffName
,Staff.ManagerID
,Staff.Department
,NEWID() AS myuniqueID
FROM Reference.Staff
This will produce the following table:
The problem is I would like the unique IDs generated to become permanent.
Can someone let me know if that is possible?
CREATE TABLE PrestigeCars.Reference.Staff (
StaffName NVARCHAR(50) NULL
,ManagerID INT NULL
,Department NVARCHAR(50) NULL
, UniqueId NVARCHAR(255) NOT NULL default NEWID()
) ON [PRIMARY]
GO
Important is, that this only works for creating the table. If you want to alter the table, you firstly have to add the Column which has to allow null, then fill the values and at last set it to not null.
Edit:
To add a Column you need the alter table statement, as mentioned in many other posts before:
ALTER TABLE PrestigeCars.Reference.Staff
ADD UniqueId NVARCHAR(255) NULL default NEWID()
Next you have to set the UniqueId for the existing rows:
UPDATE PrestigeCars.Reference.Staff
SET UniqueId = NEWID()
WHERE UniqueId IS NULL
Last but not least you should set the column to not null:
ALTER TABLE PrestigeCars.Reference.Staff
ALTER COLUMN UniqueId NOT NULL
You could add an Unique-Index, if you want to, but this should not be necessary.

Reinsert primary key in the same record

I need to insert records into a production table. The problem is that one of the fields needs to be the same value as the primary key.
In the example below, the Insert query is dropping '99' into [AlsoMyID]. But that's just a placeholder. It needs to be whatever value is going into [MyID].
How do I write the Insert query so that the system will add the same PK value to both [MyID] and [AlsoMyID]?
Drop table #mylittletable
Create table #Mylittletable (
[MyID] int IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
[AlsoMyID] int,
[ActualData] varchar(1))
Select * from #Mylittletable
Insert into #Mylittletable values (99,'x')
Select * from #Mylittletable
If you're interested in the background, the developer is using AlsoMyID as a linking field so any number of records can be linked together using the original primary key value. That said, I have no control over the table structure.
Firstly, you cannot specify the value for identity column unless you use set identity_insert on. so according to your requirement, you need to insert the same value to AlsoMyID as MyID.
You can work it out as flowing:
insert into Mylittletable
select ##IDENTITY+1,'1'
With this trigger on the table you can insert anything on the alsoMyID-column and that will be overwritten with what get's set in the myID-column.
create trigger tr_Mylittletable ON Mylittletable
AFTER INSERT AS
BEGIN
declare #ID int = (select MyID from inserted)
update Mylittletable set AlsoMyID = #ID where MyID = #ID
END
NOTE: This only works when making inserts of one line at a time!

Error while inserting data with stored procedure in table with shared identity primary key

I've got a few tables linked together where data should be inserted to using a stored procedure. The tables are:
create table contactpersoon
(
contactpersoonnr integer identity(1,1),
klantnr integer,
naam varchar(50) not null,
telefoonnr varchar(10) not null,
emailadres varchar(50) not null,
constraint pk_contactpersoon
primary key(contactpersoonnr, klantnr),
constraint fk_contactpersoon_klantnr
foreign key(klantnr) references klant(klantnr)
)
create table klant
(
klantnr integer identity(1,1) primary key,
bedrijfsnaam varchar(50) not null
)
create table Logins
(
GebruikersNaam varchar(30),
Wachtwoord varchar(30),
Klantnr int,
MdwNr int,
constraint pk_logID primary key(GebruikersNaam),
constraint fk_klantnr foreign key(klantnr) references klant(klantnr),
constraint fk_mdwnr foreign key(mdwnr) references medewerker(mdwnr)
)
Stored procedure for adding data to these tables:
IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND name = 'spKlantAanmaken')
DROP PROCEDURE spKlantAanmaken
GO
Create Procedure spKlantAanmaken
(
#bedrijfsnaam as varchar(255),
#contactnaam as varchar(255),
#telnr as integer,
#email as varchar(255),
#gebruikersnaam as varchar(255),
#wachtwoord as varchar(255)
)
AS
Begin transaction
Declare #klantnr integer
Declare #contactpersoonnr integer
Insert into Klant Values (#klantnr, #bedrijfsnaam);
Insert into contactpersoon values(#contactpersoonnr, #klantnr, #contactnaam, #telnr, #email);
Insert into Logins values (#gebruikersnaam, #wachtwoord ,#klantnr, NULL);
Select * from contactpersoon
IF ##ERROR <> 0
BEGIN
ROLLBACK
RAISERROR ('Error tijdens uitvoeren van stap 2.', 16, 1)
RETURN
END
COMMIT
GO
I don't know if it is necessary to use these identity values in the inserts.
If I try this stored procedure I get the following error:
Msg 8101, Level 16, State 1, Procedure spKlantAanmaken, Line 923
An explicit value for the identity column in table 'Klant' can only be
specified when a column list is used and IDENTITY_INSERT is ON.
If I remove the identity values from the insert I get this error:
Msg 213, Level 16, State 1, Procedure spKlantAanmaken, Line 923
Column name or number of supplied values does not match table definition.
What am I doing wrong?
When you use Identity, the columns on which the identity is applied need not be in your INSERT statement VALUES. So edit your code like below
EDIT
It also seems you are missing out the columns you are trying to insert into
Insert into Klant (bedrijfsnaam) Values (#bedrijfsnaam)
Insert into contactpersoon (klantnr, contactnaam, telnr, email) Values (#klantnr, #contactnaam, #telnr, #email)
It seems all the answers saying the same thing so hope your issued is solved
Since you have identity columns, you must specify the list of columns to insert into, in your INSERT statement, and not supply a value for the identity column - like this:
Instead of
Insert into Klant Values (#klantnr, #bedrijfsnaam);
use
Insert into Klant(bedrijfsnaam) Values (#bedrijfsnaam);
and do this for all your INSERT operations.
This is a generally accepted "Best Practice" for any time you insert something into a table - it is recommend to always explicitly specify the list of columns in your table that you're inserting into (to avoid annoying errors and surprises).
Avoid the identity columns klantnr, contactpersoonnr in the INSERT query and explicitly define your column names:
So the below code will work in your case:
Insert into Klant(bedrijfsnaam) Values (#bedrijfsnaam);
Insert into contactpersoon(klantnr, naam, telefoonnr, emailadres) values(#klantnr, #contactnaam, #telnr, #email);
Just specify the column names AND the contents in the INSERT statement like:
INSERT INTO klant (bedrijfsnaam) VALUES ('XYZ');
If you don't specify the column name list, the SQL interpreter implies, you want the identity column, too. In this case you would want to set data for 2 columns, but only provide one content element, which explains the latter error message.
Edit these two lines in your SP
Insert into Klant (bedrijfsnaam)
Values (#bedrijfsnaam);
Insert into contactpersoon(klantnr,naam,telefoonnr,emailadres)
values(#klantnr, #contactnaam, #telnr, #email);
Provide a column list, excluding the identity columns in the insert statements

Is UNIQUEIDENTIFIER an auto-generated number when inserting values in a table?

I have an error when loading a procedure telling me
Cannot insert the value NULL into column 'requestID', table 'MCAST.a01.tbl_enrollmentRequests'; column does not allow nulls. INSERT fails.
Now requestID is a UNIQUEIDENTIFIER type of variable. Is UNIQUEIDENTIFIER an auto generated number or not? Below is a sample of my code where you can see requestID.
CREATE PROCEDURE [a01].[usp_auditAcceptRequest]
(#AccountID UNIQUEIDENTIFIER,
#GroupID UNIQUEIDENTIFIER,
#Reason NVARCHAR(45)
)
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [a01].[tbl_enrollmentRequests] (requestDate, groupID, accountID)
VALUES (SYSDATETIMEOFFSET(), #GroupID, #AccountID)
DECLARE #RequestID UNIQUEIDENTIFIER
SET #RequestID = (SELECT requestID
FROM [a01].tbl_enrollmentRequests
WHERE groupID = #GroupID AND accountID = #AccountID)
INSERT INTO [a01].[tbl_enrollmentAudits] (entryDate, requestID, groupID, accountID, accepted, reason)
VALUES (SYSDATETIMEOFFSET(), #RequestID, #GroupID, #AccountID, 1, #Reason)
DELETE FROM [a01].[tbl_enrollmentRequests]
WHERE requestID = #RequestID
END;
GO
Here is where I am implementing the above procedure
BEGIN
DECLARE #AccountID UNIQUEIDENTIFIER;
DECLARE #GroupID UNIQUEIDENTIFIER;
(SELECT #AccountID = accountID
FROM [a01].[tbl_userAccounts] WHERE accountUsername='saraht');
(SELECT #GroupID = groupID FROM [a01].[tbl_groups] WHERE groupName LIKE '%Foo%');
EXECUTE [a01].[usp_addRequest] #AccountID, #GroupID;
END;
GO
Thanks for your help !!
A uniqueidentifier is a normal column, and if you want to have a automatically assigned value you need to add a default to the column. Typically the functions used for the default are newid() or newsequentialid().
Edit based on the posted table definition; you could use this:
CREATE TABLE [a01].[tbl_enrollmentRequests](
requestID UNIQUEIDENTIFIER PRIMARY KEY DEFAULT (NEWID()),
requestDate DATETIMEOFFSET NOT NULL,
groupID UNIQUEIDENTIFIER REFERENCES [a01].[tbl_groups] (groupID) NOT NULL,
accountID UNIQUEIDENTIFIER REFERENCES [a01].[tbl_userAccounts] (accountID) NOT NULL
);
That being said, you can also pre-generate a uniqueidentifier and assign that to a variable in the stored procedure prior to insertion, since the generated GUID can be assumed not to collide with any existing GUID. The benefit of this is that you know the id of the inserted row even without retrieving it from an OUTPUT clause.
A notice on performance: a significant number of rows with a clustered primary key of random GUIDs (as generated bynewid()) are a performance issue, since the inserts will cause many page splits to occur due to the randomness. The newsequentialid() function pretty much completely resolves the performance problem, but it makes the generated GUIDs guessable, so that this can only be used when "random" IDs are not required.
Is UNIQUEIDENTIFIER an auto generated number or not?
What do you ask us? You have a look at the table definition and see whether a default that sets a new uniqueidentifier is defined or not.
If it is not - then no.
If you try to insert null, then also not (as your insert overrides the default value).
---Edit:
As per the table definition you posted:
requestID UNIQUEIDENTIFIER PRIMARY KEY
no default value defined that sets it. So no.

Auto increment a bigint column?

I want a bigint ID column for every row of data that i insert into a table. I want Sql server to generate the numbers. I tried to create a table with a bigint column ID. I want this to be autoincrement with the first value as 1. I tried using [ID] [bigint] AUTO_INCREMENT NOT NULL, in my create table statement, but I got the error - Incorrect syntax near 'AUTO_INCREMENT'. How do I do this ?
Can you not just declare it as an IDENTITY column:
[ID] [bigint] IDENTITY(1,1) NOT NULL;
The 1,1 refers to the start index and the amount it is being incremented by.
NOTE: You do not have to provide a value for the ID column when you do an insert. It will automatically choose it. You can modify these values later if required.
EDIT:
Alternatively, you can use a stored procedure to handle all the inserts.
Example:
Stored Procedure will take in variables as you would a normal insert (one variable for every column). The logic within the stored procedure can select the max value currently existing in the table and choose that as its max value.
DECLARE #yourVariable = SELECT MAX(ID) FROM YourTable
Use #yourVariable as your insert value. You can increment it or change value as necessary.
I got the answer here - http://www.sqlservercentral.com/Forums/Topic1512425-149-1.aspx
CREATE TABLE Test (
ID BIGINT IDENTITY NOT NULL,
SomeOtherColumn char(1)
)
INSERT INTO Test (SomeOtherColumn)
values ('a')

Resources