Sql server 如何创建一个表约束,将一列限制为x个相同值?
在我们的零售系统中,我们计划将购物篮信息从浏览器cookie移动到表中。 目前,我们在C应用程序中实现了basket限制,但如果可能的话,我们希望将该业务逻辑作为约束向下移动到数据库中Sql server 如何创建一个表约束,将一列限制为x个相同值?,sql-server,constraints,sql-server-2017,Sql Server,Constraints,Sql Server 2017,在我们的零售系统中,我们计划将购物篮信息从浏览器cookie移动到表中。 目前,我们在C应用程序中实现了basket限制,但如果可能的话,我们希望将该业务逻辑作为约束向下移动到数据库中 CREATE TABLE [dbo].[BasketProduct]( [BasketProductId] [int] IDENTITY(1,1) NOT NULL, [BasketId] [int] NOT NULL, [ProductId] [int] NOT NULL, [Q
CREATE TABLE [dbo].[BasketProduct](
[BasketProductId] [int] IDENTITY(1,1) NOT NULL,
[BasketId] [int] NOT NULL,
[ProductId] [int] NOT NULL,
[Quantity] [int] NOT NULL,
CONSTRAINT [PK_BasketProduct] PRIMARY KEY CLUSTERED
([BasketProductId] ASC))
我们希望限制每个篮子最多有10个独特的项目,但允许每个项目的任何数量,因此BasketId值在表中最多只能出现10次
这样做似乎没有明显的限制,因为计算列中不允许包含窗口函数的聚合
有人能推荐一个合适的方法吗?请不要这样做,表定义中的函数会导致各种问题 但作为我上述评论的补充,这将满足您的要求 但请不要这样做
use tempdb;
DROP TABLE IF EXISTS CheckTbl
CREATE TABLE CheckTbl (col1 int);
GO
CREATE OR ALTER FUNCTION CheckCount(@tocheck int)
RETURNS int
AS
BEGIN
DECLARE @return INTEGER = (select count(*) from CheckTbl where col1 = @tocheck);
return @return;
END;
GO
ALTER TABLE CheckTbl
ADD CONSTRAINT chkRowCount CHECK (dbo.CheckCount(col1) <= 2 );
GO
INSERT INTO CheckTbl VALUES(1) -- allowed
INSERT INTO CheckTbl VALUES(2) -- allowed
INSERT INTO CheckTbl VALUES(1) -- allowed
INSERT INTO CheckTbl VALUES(2) -- allowed
INSERT INTO CheckTbl VALUES(1) -- NOT allowed
另请参见使用索引视图以适应这种情况的解决方案请不要这样做,表定义中的函数会导致各种问题 但作为我上述评论的补充,这将满足您的要求 但请不要这样做
use tempdb;
DROP TABLE IF EXISTS CheckTbl
CREATE TABLE CheckTbl (col1 int);
GO
CREATE OR ALTER FUNCTION CheckCount(@tocheck int)
RETURNS int
AS
BEGIN
DECLARE @return INTEGER = (select count(*) from CheckTbl where col1 = @tocheck);
return @return;
END;
GO
ALTER TABLE CheckTbl
ADD CONSTRAINT chkRowCount CHECK (dbo.CheckCount(col1) <= 2 );
GO
INSERT INTO CheckTbl VALUES(1) -- allowed
INSERT INTO CheckTbl VALUES(2) -- allowed
INSERT INTO CheckTbl VALUES(1) -- allowed
INSERT INTO CheckTbl VALUES(2) -- allowed
INSERT INTO CheckTbl VALUES(1) -- NOT allowed
另请参见使用索引视图以适应这种情况的解决方案,这并不理想,但正如我在评论中提到的,我将在执行插入/更新/删除操作的SP中处理这一问题,而不太可能需要删除操作。因此,对于插入,您将看到如下内容:
USE Sandbox;
GO
CREATE TABLE [dbo].[BasketProduct](
[BasketProductId] [int] IDENTITY(1,1) NOT NULL,
[BasketId] [int] NOT NULL,
[ProductId] [int] NOT NULL,
[Quantity] [int] NOT NULL,
CONSTRAINT [PK_BasketProduct] PRIMARY KEY CLUSTERED
([BasketProductId] ASC));
GO
CREATE PROC AddBasketProduct @BasketID int, @ProductID int, @Quantity int AS
BEGIN
DECLARE @DistinctProducts int;
SELECT @DistinctProducts = COUNT(DISTINCT ProductID)
FROM dbo.BasketProduct WITH (UPDLOCK) --As we need to control concurrency issues
WHERE BasketId = @BasketID
AND ProductID != @ProductID ;
IF @DistinctProducts >= 10
THROW 71245, N'Cannot have more than 10 different products in a single basket.',16; --Choose an error number and state appropraite for your applciation
ELSE
INSERT INTO dbo.BasketProduct (BasketId,
ProductId,
Quantity)
VALUES(@BasketID,@ProductID,@Quantity);
END;
GO
--Make some sample data
INSERT INTO dbo.BasketProduct (BasketId,
ProductId,
Quantity)
VALUES(1,1,1),
(1,2,1),
(1,3,1),
(1,4,1),
(1,5,1),
(1,6,1),
(1,7,1),
(1,8,1),
(1,9,1),
(1,10,1); --10 products.
GO
--11th product, will fail
EXEC dbo.AddBasketProduct @BasketID = 1,
@ProductID = 11,
@Quantity = 1;
GO
--Repetition of product 2, will work
EXEC dbo.AddBasketProduct @BasketID = 1,
@ProductID = 2,
@Quantity = 1;
GO
DROP PROC dbo.AddBasketProduct;
DROP TABLE dbo.BasketProduct;
这不太理想,但正如我在评论中提到的,我会在执行插入/更新/删除操作的SP中处理这一点,这不太可能需要删除操作。因此,对于插入,您将看到如下内容:
USE Sandbox;
GO
CREATE TABLE [dbo].[BasketProduct](
[BasketProductId] [int] IDENTITY(1,1) NOT NULL,
[BasketId] [int] NOT NULL,
[ProductId] [int] NOT NULL,
[Quantity] [int] NOT NULL,
CONSTRAINT [PK_BasketProduct] PRIMARY KEY CLUSTERED
([BasketProductId] ASC));
GO
CREATE PROC AddBasketProduct @BasketID int, @ProductID int, @Quantity int AS
BEGIN
DECLARE @DistinctProducts int;
SELECT @DistinctProducts = COUNT(DISTINCT ProductID)
FROM dbo.BasketProduct WITH (UPDLOCK) --As we need to control concurrency issues
WHERE BasketId = @BasketID
AND ProductID != @ProductID ;
IF @DistinctProducts >= 10
THROW 71245, N'Cannot have more than 10 different products in a single basket.',16; --Choose an error number and state appropraite for your applciation
ELSE
INSERT INTO dbo.BasketProduct (BasketId,
ProductId,
Quantity)
VALUES(@BasketID,@ProductID,@Quantity);
END;
GO
--Make some sample data
INSERT INTO dbo.BasketProduct (BasketId,
ProductId,
Quantity)
VALUES(1,1,1),
(1,2,1),
(1,3,1),
(1,4,1),
(1,5,1),
(1,6,1),
(1,7,1),
(1,8,1),
(1,9,1),
(1,10,1); --10 products.
GO
--11th product, will fail
EXEC dbo.AddBasketProduct @BasketID = 1,
@ProductID = 11,
@Quantity = 1;
GO
--Repetition of product 2, will work
EXEC dbo.AddBasketProduct @BasketID = 1,
@ProductID = 2,
@Quantity = 1;
GO
DROP PROC dbo.AddBasketProduct;
DROP TABLE dbo.BasketProduct;
添加当10个唯一项要求更改为20时会发生什么情况?或是10个独特的项目,除了经理/超级用户,他们可以有30个?我会通过一个最大允许项标志在插入级别控制它。我们已经妥协了,没有10列:每个产品一列。约束条件足够灵活,我们可以在不中断系统的情况下进行更改,但我们很高兴规则没有例外。我真的不建议尝试这样做,因为我不喜欢这样做。。。。但是……呢。。。。呃,我不敢相信我会这么说。。。。那扳机呢。。。。。啊。。。我现在需要去洗漱了。我不喜欢做很多事情,但是如果存储过程不能为您工作,那么我唯一能看到的另一件事就是让它工作而不会让我们其他人非常恼火,那就是触发器。。。没有人喜欢触发器。添加当10个唯一项的要求更改为20个时会发生什么?或是10个独特的项目,除了经理/超级用户,他们可以有30个?我会通过一个最大允许项标志在插入级别控制它。我们已经妥协了,没有10列:每个产品一列。约束条件足够灵活,我们可以在不中断系统的情况下进行更改,但我们很高兴规则没有例外。我真的不建议尝试这样做,因为我不喜欢这样做。。。。但是……呢。。。。呃,我不敢相信我会这么说。。。。那扳机呢。。。。。啊。。。我现在需要去洗漱了。我不喜欢做很多事情,但是如果存储过程不能为您工作,那么我唯一能看到的另一件事就是让它工作而不会让我们其他人非常恼火,那就是触发器。。。没有人喜欢触发器。如果我们做些改变,那就行了。没有任何安全模型可以阻止开发人员访问表,因此,我确信,绕过存储过程,开发人员就会这样做。如果我将其锁定并使用索引视图来保存篮框计数,它可能会工作。有超过10K的用户都在添加项目,可能会有大量的计数检查。如果我们做一些更改,这将起作用。没有任何安全模型可以阻止开发人员访问表,因此,我确信,绕过存储过程,开发人员就会这样做。如果我将其锁定并使用索引视图来保存篮框计数,它可能会工作。对于10K+以上的用户,所有添加项都可能会有大量计数检查。如果计数是从聚合索引视图而不是从表中获取,则这可能很有用。如果计数是从聚合索引视图而不是从表中获取,则这可能很有用。