Using a function in a SQL CHECK constraint - sql

I am trying to replace simple CHECK constraint with an embedded function within a CHECK constraint however it doesn't seem to restrict the data I can enter. The constraint is to prevent product amount of less than 0.25 and more than 5,000. The original check worked fine, however the function doesn't seem to do anything at all.
The original constraint:
ALTER TABLE Prices
ADD CONSTRAINT CheckPrices CHECK ((Amount > 0.25) AND (Amount <= 5000.00))
The function:
ALTER FUNCTION dbo.CheckProductPrices
(
#productSKU int,
#priceDate smalldatetime
)
RETURNS bit
AS
BEGIN
DECLARE #retVal bit = 0
SELECT #retVal = CASE WHEN Amount > 0.25 AND Amount <= 5000.00 THEN 1 ELSE 0 END
FROM Prices
WHERE productSKU = #productSKU
AND priceDate = #priceDate
RETURN #retVal
END
The new CHECK constraint:
ALTER TABLE Prices
ADD CONSTRAINT CheckPrices CHECK (dbo.CheckProductPrices([productItem], [priceValidDate]) = 1)
I don't understand why the new constraint isn't stopping invalid prices the way the original constraint did.

Related

Can't use UDF as constraint in MariaDB

I'm running MariaDB 10.3.17 and I'm trying to add a constraint to an existing table. The constraint uses a UDF - which should be allowed.
Here's my table and UDF.
CREATE OR REPLACE TABLE real_estate.sample_two_expected_output (
u_id int (9) NOT NULL,
first_date date NOT NULL,
last_date date NOT NULL,
days int AS (DATEDIFF(last_date,first_date)+1),
address varchar(50),
price varchar(50),
--Constraints
CONSTRAINT dates CHECK (last_date >= first_date),
PRIMARY KEY (u_id,first_date));
DELIMITER //
USE real_estate;
CREATE OR REPLACE FUNCTION overlap(
u_id INT,
first_date DATE,
last_date DATE
) RETURNS INT DETERMINISTIC
BEGIN
DECLARE valid INT;
SET valid = 1;
IF EXISTS(SELECT * FROM real_estate.sample_two_expected_output t WHERE t.u_id = u_id AND first_date <= t.last_date AND t.first_date <= last_date) THEN SET valid = 0;
ELSE SET valid = 1;
END IF;
RETURN valid;
END; \\
DELIMITER;
I try to add this function as a constraint in the table.
ALTER TABLE real_estate.sample_two_expected_output ADD CONSTRAINT overlap CHECK(overlap(u_id,first_date,last_date)=1);
However I get the below error message and I don't know why.
EXECUTE FAIL:
ALTER TABLE real_estate.sample_two_expected_output ADD CONSTRAINT overlap CHECK(overlap(u_id,first_date,last_date)=1);
Message :
Function or expression '`overlap`()' cannot be used in the CHECK clause of `overlap`
In general you can use any deterministic user defined function (UDF) but not a stored function (SF) in constraints like DEFAULT, CHECK, etc.
A big difference between UDFs and SFs is the fact that a UDF is usually written in C/C++ while a SF is written in SQL. That means it is not possible to execute SQL code in a UDF within the same connection, which would lead to significant problems, as your SF shows:
Depending on the storage engine ALTER TABLE locks the entire table, parts of it or creates a temporary copy. I cannot imagine a way to execute the SQL statement SELECT * FROM real_estate.sample_two_expected_output t WHERE t.u_id = u_id .. in your SF while the table is locked or reorganized.

SQL Server 2012 check constraint error on udf

Just trying to make a simple function in SQL Server 2012 to be used as a check constraint. Cant get past this error. Thanks for any help!
The error I receive:
Msg 547, Level 16, State 0, Line 1 The ALTER TABLE statement
conflicted with the CHECK constraint "CheckBatchQuantity". The
conflict occurred in database "Ians23_SnackManufacturer", table
"dbo.Batch", column 'BatchQuantity'.
Code:
CREATE FUNCTION udfBatchNumber2
(#BatchQuantity int)
RETURNS int
AS
BEGIN
DECLARE #Return int
IF #BatchQuantity >10
SET #Return = 0
ELSE
SET #Return = 1
RETURN #Return
END
ALTER TABLE Batch
ADD CONSTRAINT CheckBatchQuantity
check ((dbo.[udfBatchNumber](BatchQuantity)) <= 0)
The error is due to existing records in Batch table whose BatchQuantity is greater than zero.
First update/remove the records whose BatchQuantity > 0 then create the check constraint
update Batch
set BatchQuantity = NULL
Where BatchQuantity > 0
ALTER TABLE Batch
ADD CONSTRAINT CheckBatchQuantity
check ((dbo.[udfBatchNumber](BatchQuantity)) <= 0)
or you can create the constraint with NOCHECK which will not check the existing data
ALTER TABLE Batch WITH NOCHECK
ADD CONSTRAINT CheckBatchQuantity
check ((dbo.[udfBatchNumber](BatchQuantity)) <= 0)

MSSQL - set limit for a column value

I have a table named Test with the following columns.
id PK, int, not null
amount money, not null
I want to set a limit on the amount, say, 1000. I don't want anyone to insert a value greater than 1000 in this column. Can anyone help me on how to do this?
Something like this
CREATE TABLE tablename
(
-------
--------
amount money,
CONSTRAINT chk_amount CHECK (amount <= 1000)
)
You can add a check constraint, like this:
ALTER TABLE Test
ADD CONSTRAINT chk_money CHECK (amount<=1000)
You can create check constraint for your table.
alter table dbo.Your_Table with check add constraint your_Table_Amount check (Amount <= 1000)
go
alter table dbo.Your_Table check constraint your_Table_Amount

constraint check against values of foreign key

I have these two tables
Table: Guards
ID int
Name varchar
Rank int
Table: Squads
SquadId
Leader
SquadName
The Leader column points to the ID column in the Guard table and I'm trying to create a constraint that checks if the Rank column linked to the guard id provided as the leader is a specific value (in this case 1)
Is this possible or do I have to use a trigger?
You need to add a CHECK constraint. I'd wrap the constraint into a function since you need to check another table's value.
CREATE FUNCTION CheckLeaderRank
(#LeaderID INTEGER)
RETURNS INTEGER
AS
BEGIN
DECLARE #value INTEGER;
DECLARE #MinimumRank INTEGER = 3;
SET #value = CASE WHEN (SELECT RANK FROM Guards WITH(NOLOCK) WHERE Id = #LeaderID) >= #MinimumRank THEN 1 ELSE 0 END
RETURN #value
END
The function will check if the guard's Rank is high enough : make sure to set #MinimumRank to the proper value or, even better, to fetch it from another table.
Now add the constraint to your Squads table.
ALTER TABLE Squads
ADD CONSTRAINT chk_rank CHECK (dbo.CheckLeaderRank(i) = 1)

Check constraint in SQL Server

I have three tables a,b and c and need to add a constraint like below for checking the data integrity
The below is wrong but I need help in enforcing the below condition.
ALTER TABLE [a]
ADD CONSTRAINT UOMGROUPIG CHECK UNITOFMEASURID IN (SELECT UnitOfMeasureId FROM b WHERE UOMGroupId=1 )
ALTER TABLE [c]
ADD CONSTRAINT UOMGROUPIG CHECK UNITOFMEASURID IN (SELECT UnitOfMeasureId FROM b WHERE UOMGroupId=2 )
Thanks
A scalar valued function like this works for your example, you can easily modify it and create your second constraint:
CREATE FUNCTION your_schema_name.udf_Check1(
#UNITOFMEASURID INT
)
RETURNS BIT
AS
BEGIN
DECLARE #returnValue BIT = 0
SELECT #returnValue = CASE WHEN COUNT(UnitOfMeasureId) > 0 THEN 1 ELSE 0 END
FROM your_schema_name.b
WHERE UOMGroupId=1
AND #UNITOFMEASURID = UnitOfMeasureId
RETURN #returnValue
END
GO
ALTER TABLE [a]
ADD CONSTRAINT UOMGROUPIG
CHECK (your_schema_name.udf_Check1(UNITOFMEASURID) = 1)
GO
Here is the example: SQL Fiddle
You cannot write query within Check constraint. Instead you can call user defined function.
You can use same function in both constraints by passing #UOMGroupId value along with UNITOFMEASURID.
CREATE FUNCTION CheckFnctn(#UNITOFMEASURID int, #UOMGroupId int)
RETURNS int
AS
BEGIN
if exists(SELECT UnitOfMeasureId FROM b WHERE UOMGroupId=#UOMGroupId
and UnitOfMeasureId = #UNITOFMEASURID)
BEGIN
RETURN 1;
END
RETURN 0; --missed this line
END;
ALTER TABLE [a]
ADD CONSTRAINT UOMGROUPIG CHECK (dbo.CheckFnctn(UNITOFMEASURID, 1)=1);
ALTER TABLE [c]
ADD CONSTRAINT UOMGROUPIG CHECK (dbo.CheckFnctn(UNITOFMEASURID, 2)=1);