I have a table where each row represents a key-value pair containing application-specific settings (such as the number of days to retain alerts, etc.). Each of these key-value pairs has a different range of valid values, so no single check constraint will apply equally to all rows. Some rows might need no validation at all and others might have string values needing special consideration. Is there some way I can create a check constraint on a per-row basis and have that constraint enforced when that row is updated?
I have attempted several times to achieve this, but have run into hurdles each time. Each attempt relies on the existence of a [Check] column on the table, wherein the constraint is defined for that row, similar to a normal table-based constraint (such as "((CAST Value AS INTEGER) <= 60)").
My first attempt was to create a normal check constraint that calls a user-defined function that reads the contents of the [Check] column (based on an identity value), performs a test of the constraint, and returns a true/false result, depending on whether or not the constraint is not violated. The problem with this approach is that it requires writing dynamic SQL to get the contents of the [Check] column as well as executing the code that it contains. But of course, dynamic SQL is not permitted in a function.
Next, I tried changing the function to a stored procedure, but it does not appear to be possible to call a stored procedure via a check constraint.
Finally, I tried creating a function AND a stored procedure, and calling the stored procedure from the function, but that is not permitted either.
The only way I know that will work is to write a huge, monolithic check constraint, containing checks for each row by identity value, all OR'ed together, like this:
(ID = 1 AND (CAST Value AS INTEGER) <= 100) OR (ID = 2 AND Value IN ('yes', 'no')) OR...
But that's an error-prone maintenance nightmare. Does anyone know of a way to accomplish what I want, without resorting to a monolithic check constraint?
As requested, consider the following table definition and some sample rows:
CREATE TABLE [dbo].[GenericSetting]
(
[ID] [INT] IDENTITY(1,1) NOT NULL,
[Name] [NVARCHAR](50) NOT NULL,
[Value] [NVARCHAR](MAX) NULL,
[Check] [NVARCHAR](MAX) NULL,
CONSTRAINT [PK_GenericSetting] PRIMARY KEY CLUSTERED ([ID])
)
INSERT INTO [dbo].[GenericSetting] ([Name],[Value],[Check]) VALUES ('AlertRetentionDays', 60, 'CAST(Value AS INTEGER) <= 60');
INSERT INTO [dbo].[GenericSetting] ([Name],[Value],[Check]) VALUES ('ExampleMode', 60, 'CAST(Value AS INTEGER) IN (1,2,5)');