我们如何创建表约束,以将一列限制为x个相同值?

问题描述 投票:0回答:2

在我们的零售系统中,我们计划将购物篮信息从浏览器Cookie移至表格。我们目前在C#应用程序中实现了篮子的限制,但如果可能的话,希望将该业务逻辑作为约束向下移动到数据库。

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个唯一商品,但每个商品都允许有任意数量,因此,篮子ID值在表格中最多只能显示10次。

似乎似乎没有明显的约束,因为在计算列中不允许包括窗口函数的聚合。

有人可以推荐合适的方法吗?

sql-server constraints sql-server-2017
2个回答
0
投票

这不理想,但是正如我在评论中提到的,我将在执行INSERT /UPDATE/ DELETE的SP中处理此问题(不太可能需要DELETE)。因此,对于INSERT,您将具有如下所示的内容:

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;

DB<>Fiddle


0
投票

请不要这样做,表定义中的函数会导致各种问题...

但是作为我上面评论的补充,这将满足您的要求

但是请不要这样做

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

另请参阅Custom function with check constraint SQL Server 2008,以使用适合这种情况的索引视图解决方案

© www.soinside.com 2019 - 2024. All rights reserved.