Why doesn't this Check constraint work? - sql

I have created a check constraint in SQL Server 2005, but this check constraint doesn't work. The SQL Server Management Studio tells me by an insert statement the following message:
Msg 547, Level 16, State 0, Line 1
The INSERT statement conflicted with the CHECK constraint "MY_CHECK_CONSTAINT". The conflict occurred in database "MY_DB", table "dbo.MY_TABLE", column 'MY_COLUMN'.
I have added the check constraint with the following code:
ALTER TABLE MY_TABLE WITH NOCHECK
ADD CONSTRAINT CK_MY_CHECK_CONSTRAINT CHECK (dbo.MY_FUNCTION(MY_PARAMETER)<1)
The calling function "MY_FUNCTION" returns an int.
My target is that if my function returns an int which is less than 1 the insert statement can successfully be completed and if the return value is bigger than 0 the insert statement has to be terminated.
My problem now is that my function returns the value 0 but the insert statement has been terminated always. What am I doing wrong?
The code of my function is the following:
CREATE FUNCTION MY_FUNCTION(#MY_PARAMETER uniqueidentifier)
RETURNS int
AS
BEGIN
declare #return int = 0
SET #return = (SELECT COUNT(MY_COLUMN) FROM MY_TABLE WHERE MY_COLUMN= #MY_PARAMETER )
return #return
END
Thanks for your help.

I was able to reproduce your problem in 100%. Try this new
example below. Now my table table1 is empty, and I cannot insert
any number into it :) Very interesting situation, absolutely the
same as yours, I believe.
"Maybe when your UDF code is executed, it already sees this same
row which you're just trying to insert (it sees it's in the table).
I don't know the inner workings and don't have much time now to check it.
But that could be the issue. My UDF doesn't perform a check based on some
SELECT in the same table, that's what's conceptually different between
your example and my example."
OK, after 5 more minutes of research, turns out my guess was right.
When your UDF is called, it sees the row you're just trying
to insert.
See the accepted answer here.
Check constraint UDF with multiple input parameters not working
So - mystery uncovered, it seems :)
--- 1 ---
USE [test]
GO
/****** Object: UserDefinedFunction [dbo].[ContainsNumber] Script Date: 11/26/2013 07:06:41 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[ContainsNumber]
(
#number int
)
RETURNS INT AS
BEGIN
declare #result int
select #result = count(*)
from test.dbo.table1
where
number = #number
if (#result > 0)
begin
set #result = 1
end
return #result
END
GO
--- 2 ---
USE [test]
GO
/****** Object: Table [dbo].[table1] Script Date: 11/26/2013 07:06:33 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[table1](
[id] [int] IDENTITY(1,1) NOT NULL,
[number] [int] NULL,
CONSTRAINT [PK_table1] PRIMARY KEY CLUSTERED
(
[id] 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
ALTER TABLE [dbo].[table1] WITH CHECK ADD CONSTRAINT [CK_table1] CHECK (([dbo].[ContainsNumber]([number])=(0)))
GO
ALTER TABLE [dbo].[table1] CHECK CONSTRAINT [CK_table1]
GO

1) Maybe you can try this in your code:
SELECT #return = COUNT(MY_COLUMN)
FROM MY_TABLE WHERE MY_COLUMN = #MY_PARAMETER
instead of what you did.
2) Try naming the variable not #return but e.g. #result.
3) Try walking your function outside of its normal call.
4) Also, you might want to try this test below.
I don't see any issues with it ... But OK,
it's not 100% the same as yours.
This check constraint below works fine and uses
my user defined function IsEvenNumber.
----- 1 -----
USE [test]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[IsEvenNumber]
(
#number int
)
RETURNS INT AS
BEGIN
declare #result int
set #result = 1
if (((#number) % 2) <> 0)
begin
set #result = 0
end
return #result
END
GO
----- 2 -----
USE [test]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[table1](
[id] [int] IDENTITY(1,1) NOT NULL,
[number] [int] NULL,
CONSTRAINT [PK_table1] PRIMARY KEY CLUSTERED
(
[id] 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
ALTER TABLE [dbo].[table1] WITH CHECK ADD CONSTRAINT [CK_Table1] CHECK (([dbo].[IsEvenNumber]([number])<(1)))
GO
ALTER TABLE [dbo].[table1] CHECK CONSTRAINT [CK_Table1]
GO
----- 3 -----
insert into table1(number) values (3) -- OK
insert into table1(number) values (10) -- FAILED
insert into table1(number) values (0) -- FAILED
insert into table1(number) values (5) -- OK

Related

How can I select the top 1 column value into a variable?

Here is my table definition where I store the installation information:
CREATE TABLE [dbo].[InstallInfo](
[ID] [uniqueidentifier] NOT NULL,
[Module] [int] NOT NULL,
[Version] [nvarchar](50) NOT NULL,
[InstallDate] [datetime] NOT NULL,
CONSTRAINT [PK_InstallInfo] PRIMARY KEY CLUSTERED
([ID] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
What I want to do is get the top 1 row (install version) for a module, based on the latest date:
ALTER PROCEDURE [dbo].[InstallInfo_GetLatest]
-- Add the parameters for the stored procedure here
#Module int,
#Version nvarchar(50) out
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
set #Version = ''
-- Insert statements for procedure here
SELECT TOP 1 #Version=[Version]
from InstallInfo
where [Module] = #Module
order by InstallDate
END
However, when I run the stored proc, I get this error:
Msg 8114, Level 16, State 5, Procedure InstallInfo_GetLatest, Line 0
Error converting data type nvarchar to int.
Here is how I am executing the stored proc:
declare #Module int
set #Module = 1
declare #versionOut nvarchar(50)
exec InstallInfo_GetLatest Module, #versionOut output
select #versionOut as 'v OUT'
FOUND IT: Forgot the # sign
exec InstallInfo_GetLatest #Module, #versionOut output
Thanks everybody !
Here is the answer after multiple comments and feedback:
exec InstallInfo_GetLatest #Module, #versionOut output

Understanding creating simple stored procedures

I'm in the process of learning to create stored procedures in Microsoft SQL Server Management Studio. I need to create a stored procedure that adds a single new record to my table. Also, I need to create two extra output parameters along with the stored procedure (I chose ##error and SCOPE_IDENTITY()).
This is the code I use to create my stored procedure:
use bieren
go
if exists
(select name from sysobjects
where name = 'spBierInsert' and xtype = 'p')
drop procedure spBierInsert
go
create procedure spBierInsert
#Biernr int = 0,
#Naam nvarchar(100) = '',
#BrouwerNr int = 0,
#SoortNr int = 0,
#Alcohol real,
#gelukt nvarchar(10) output,
#id int output
as
begin
declare #fout int
insert into bieren
values (#Biernr, #Naam, #BrouwerNr, #SoortNr, #Alcohol)
set #fout = ##error
print 'Foutnummer:' + cast(#fout as varchar(4))
if #fout > 0
set #gelukt = 'Neen: ' + cast(#fout as varchar(4))
else
set #gelukt = 'Ja'
set #id = SCOPE_IDENTITY()
end
I must be doing something wrong, because the result is the following:
Msg 547, Level 16, State 0, Procedure spBierInsert, Line 92
The INSERT statement conflicted with the FOREIGN KEY constraint
"FK_Bieren_Brouwers". The conflict occurred in database "Bieren", table
"dbo.Brouwers", column 'BrouwerNr'.
The statement has been terminated.
Foutnummer:547
(1 row(s) affected)
What have I done incorrectly?
EDIT 30/12/2015: I have updated this question with new information. I originally just used terms like "exampletable" because I had no idea that the search to the answer to my question would be more involved than a single answer, so I've gone ahead and changed the entire code above (as well as the text for the error), and I've added the script for my table underneath. The point of this question is that I come out with code that works, or, that I at least understand what's wrong with it.
USE [Bieren]
GO
/****** Object: Table [dbo].[Bieren] Script Date: 30/12/2015 0:19:56 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Bieren](
[BierNr] [int] NOT NULL,
[Naam] [nvarchar](100) NULL,
[BrouwerNr] [int] NULL,
[SoortNr] [int] NULL,
[Alcohol] [real] NULL,
CONSTRAINT [PK_Bieren] PRIMARY KEY CLUSTERED
(
[BierNr] 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
ALTER TABLE [dbo].[Bieren] WITH CHECK ADD CONSTRAINT [FK_Bieren_Brouwers] FOREIGN KEY([BrouwerNr])
REFERENCES [dbo].[Brouwers] ([BrouwerNr])
GO
ALTER TABLE [dbo].[Bieren] CHECK CONSTRAINT [FK_Bieren_Brouwers]
GO
ALTER TABLE [dbo].[Bieren] WITH CHECK ADD CONSTRAINT [FK_Bieren_Soorten] FOREIGN KEY([SoortNr])
REFERENCES [dbo].[Soorten] ([SoortNr])
GO
ALTER TABLE [dbo].[Bieren] CHECK CONSTRAINT [FK_Bieren_Soorten]
GO
Your procedure is created fine. The problem is that you are inserting a value in column 'BrouwerNr' of table "dbo.Brouwers" which doesn't exist in "SoortNr" column of table "dbo.Soorten". There is foreign set on the table "dbo.Brouwers" named "[FK_Bieren_Soorten]" which is causing this restriction. I suggest you look into this article to know more about foreign keys.
The error is because you are inserting 1600 in #ColumnNr, which is a foreign key of another table and does not have 1600 in it.
You can do the following :
right click on "exampletable" table and select 'Script table as'->'Create to'->'new query editor window'
Now,find "ColumnNr" in it. It will be something like this =>
ALTER TABLE [dbo].[exampleTable] WITH CHECK ADD CONSTRAINT [FK_exampleTable_**OtherTableName**_ColumnNr] FOREIGN KEY([ColumnNr])
REFERENCES [dbo].**[OtherTableName]** ([ColumnNr])
GO
Now open the mentioned table "OtherTableName" in the query and look for the column "ColumnNr". It will not be having value 1600.
Try to insert any value in
#ColumnNr = {//Any value from **OtherTableName**},
which is in table "OtherTableName"

why this insert statement cannot insert values in it's second iteration?

I have this reasonably sized stored procedure that accepts 3 collections two UDTT collection and one CSV collection.
I have passed the following values to test stored procedure. However the problems occurred in one small master table called EmpDesignations with 5 columns. There is a loop I used in the store procedure to insert values to EmpDesignations. The loop is required because the values are extracted from the CSV string. As expected it does iterate 2 times with two sets of values. The first iteration the data was inserted successfully but in the second iteration data was not inserted. I have checked weather the data is empty but those variables #tmpEmpID and #tmp contain data. So cannot figure out the problem
The EmpDesignations table definition is
EmpID PK, FK not null
DesigID PK, FK not null
IsValid int not null
UpdtDT datetime not null containts to getDate()
AuthID int not null  EmpID
here is the snapshot of the table columns and types
In the first iteration the passing values to the insert statement is shown in the watch window
As you can see, ##rowcount is 1 so worked in the first round!
In the following watch shows the passing values to the insert statement, this is in the second iteration:
But the ##rowcount is not 1 therefore the control rollbacks all insertions
Here is a video link of the debugging of the insert statement
here is the video of THE SECOND INSERT statement debugging
Table definition is correct, the number of values passed matches the number of input columns, and the variables are filled with values in both iterations, so what could be the problem???
here is the script that was generated by SQL server for the table EmpDesignations
USE [SMSV100]
GO
/****** Object: Table [dbo].[EmpDesignations] Script Date: 04/12/2014 08:50:23 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[EmpDesignations](
[EmpID] [int] NOT NULL,
[DesigID] [int] NOT NULL,
[IsValid] [int] NOT NULL,
[UpdtDT] [datetime] NOT NULL,
[AuthID] [int] NOT NULL,
CONSTRAINT [PK_EmpDesignations] PRIMARY KEY CLUSTERED
(
[EmpID] ASC,
[DesigID] 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
ALTER TABLE [dbo].[EmpDesignations] WITH CHECK ADD CONSTRAINT [FK_EmpDesignations_Designations] FOREIGN KEY([DesigID])
REFERENCES [dbo].[Designations] ([DesigID])
GO
ALTER TABLE [dbo].[EmpDesignations] CHECK CONSTRAINT [FK_EmpDesignations_Designations]
GO
ALTER TABLE [dbo].[EmpDesignations] WITH CHECK ADD CONSTRAINT [FK_EmpDesignations_Employees] FOREIGN KEY([EmpID])
REFERENCES [dbo].[Employees] ([EmpID])
GO
ALTER TABLE [dbo].[EmpDesignations] CHECK CONSTRAINT [FK_EmpDesignations_Employees]
GO
ALTER TABLE [dbo].[EmpDesignations] ADD CONSTRAINT [DF_EmpDesignations_UpdtDT] DEFAULT (getdate()) FOR [UpdtDT]
GO
thanks

Need very simple 'sequence' for GetNextOrderNumber for SQL Server

I'm trying to make an even simpler function than the one described here to get the next value of an order number for a shopping cart.
I don't care if there are gaps
Only completed orders get an ID (i.e. I'm deliberately not using IDENTITY)
Obviously there must not be duplicates
I don't care about performance and locking. If we have so many new orders that I care about lockin then I'll have other problems first
I've found quite a few other similar questions, but not the exact solution i'm looking for.
What I have so far is this :
USE [ShoppingCart]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Sequence_CompletedOrderID]
([val] [int] NOT NULL
CONSTRAINT [DF_Sequence_CompletedOrderID_NextValue] DEFAULT ((520000))
) ON [PRIMARY]
then for the stored proc :
CREATE PROC dbo.GetNextCompletedOrderId
#nextval AS INT OUTPUT
AS
UPDATE dbo.sequence_completedorderid SET #nextval=val += 1;
GO
Like I said I'm trying to base it on the article I linked to above - so perhaps this just a clumsy way of doing it. My SQL isn't quite up to much for even simple things like this, and its past my bedtime. Thanks!
OK, so you already have an IDENTITY column in your main table - but how about just having an additional table with again has an IDENTITY column?? This would save you so much trouble and hassle.....
USE [ShoppingCart]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Sequence_CompletedOrderID]
([val] [int] NOT NULL IDENTITY(520000, 1)
) ON [PRIMARY]
CREATE PROC dbo.GetNextCompletedOrderId
#nextval AS INT OUTPUT
AS
INSERT INTO dbo.Sequence_CompletedOrderID DEFAULT VALUES
SELECT #nextval = SCOPE_IDENTITY()
GO
That way, you can leave all the hassle of making sure things are unique etc. to SQL Server, and it will also make sure you won't ever get back the same value twice from the IDENTITY column!
If you use the Sequence_CompletedOrderID table as a one row table of order IDs then you should use UPDATE and rely on the OUTPUT clause to capture the new value:
CREATE PROC dbo.GetNextCompletedOrderId
#nextval AS INT OUTPUT
AS
SET NOCOUNT ON;
UPDATE dbo.Sequence_CompletedOrderID
SET val=val + 1
OUTPUT #nextval = INSERTED.val;
GO
The solution from #marc_s creates a new row for each number generated. At first I didn't think I liked this, but realized I can use it to my advantage.
What I did was added a date time audit column, and also an #orderid parameter to the stored proc. For a particular orderid it will be guaranteed to return the same completedorderid, which is the number from the sequence generator.
If for some reason my application layer requests the next id, but then crashes before it can commit the transaction - it will still be linked to that order so that when it is requested again the same number will be returned.
This is what I ended up with:
USE [ShoppingCart]
GO
/****** Object: Table [dbo].[Sequence_CompletedOrderID] Script Date: 11/29/2009 03:36:40 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Sequence_CompletedOrderID](
[val] [int] IDENTITY(520000,1) NOT NULL,
[CreateDt] [datetime] NOT NULL CONSTRAINT [DF_Sequence_CompletedOrderID_CreateDt] DEFAULT (getdate()),
[Orderid] [int] NOT NULL CONSTRAINT [DF_Sequence_CompletedOrderID_Orderid] DEFAULT ((0)),
CONSTRAINT [PK_Sequence_CompletedOrderID] PRIMARY KEY CLUSTERED
(
[Orderid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
USE [ShoppingCart]
GO
/****** Object: StoredProcedure [dbo].[GetCompletedOrderId] Script Date: 11/29/2009 03:34:08 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROC [dbo].[GetCompletedOrderId]
#orderid AS INT,
#completedorderid AS INT OUTPUT
AS
IF EXISTS (SELECT * FROM dbo.Sequence_CompletedOrderID WHERE orderid = #orderid)
BEGIN
SET #completedorderid =(SELECT val FROM dbo.Sequence_CompletedOrderID WHERE orderid = #orderid)
END
ELSE
BEGIN
INSERT INTO dbo.Sequence_CompletedOrderID (orderid) VALUES (#orderid)
SET #completedorderid =(SELECT SCOPE_IDENTITY())
END
How about using the following statement after inserting data in your table?
UPDATE dbo.sequence_completedorderid
SET #nextval = (SELECT MAX(val) + 1 FROM dbo.sequence_completedorderid)
You don't need a new id column, all you need is to add a new OrderCompleted column (bit), and combine that with the id you already have.
SELECT Id FROM T_Order WHERE OrderCompleted = 1

sometimes Identity isn't working

I have a following table
CREATE TABLE [dbo].[test_table]
(
[ShoppingCartID] [int] IDENTITY(1,1) NOT NULL,
[CartTimeoutInMinutes] [int] NOT NULL,
[MaximumOrderLimitPerUser] [int] NOT NULL,
[MaximumOrderLimitPerSession] [int] NOT NULL,
CONSTRAINT [PK_test_table] PRIMARY KEY CLUSTERED
(
[ShoppingCartID] 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
Sometimes Identity isn't working, it's start with 0 and sometimes its start with 1.
Thank you in advance.
How are you putting the data in there? If you are using regular INSERT it should start at 1. You can, however, bulk-insert into the table, or otherwise use identity-insert; in which case all bets are off:
create table test (
id int not null identity(1,1),
name varchar(20) not null)
set identity_insert test on
insert test (id, name) values (0, 'abc')
insert test (id, name) values (27, 'def')
set identity_insert test off
select * from test
with output:
id name
----------- --------------------
0 abc
27 def
Or is the problem relating to ##IDENTITY (in which case: use SCOPE_IDENTITY() instead).
Possible
Are you using DBCC CHECKIDENT? This is invoked by some data compare tools (eg Red Gate) and has the following behaviour:
DBCC CHECKIDENT ( table_name, RESEED, new_reseed_value )
Current identity value is set to the new_reseed_value.
If no rows have been inserted into the table since the table was created, or if all rows have been removed by using the TRUNCATE TABLE statement, the first row inserted after you run DBCC CHECKIDENT uses new_reseed_value as the identity. Otherwise, the next row inserted uses new_reseed_value + the current increment value.
Or: are you using SET IDENTITY_INSERT?
These assume you are looking at the table, rather then using ##IDENTITY (as Mark suggested)