2013-02-19 104 views
0

我有1 SellerId用于计算列特定需要叫产品代码计算列表达式

ProductId | SellerId | ProductCode 
1   1   000001 
2   1   000002 
3   2   000001  
4   1   000003 

产品编号是身份,增量是一个外键。

所以我的计算专栏ProductCode必须看卖家有多少产品,并且格式为000000.这里的问题是如何知道要查找哪些卖家产品?

我已经写了有一个TSQL不看多少产品没有卖家有

ALTER TABLE dbo.Product 
ADD ProductCode AS RIGHT('000000' + CAST(ProductId AS VARCHAR(6)) , 6) PERSISTED 
+1

我会建议使用视图来做这种计算。如果选择性能是最重要的因素(我看你使用'persisted'),这个视图甚至可以被索引。 – 2013-02-19 21:15:13

+0

@TimLehner你能提供样品吗?我仍然想知道是否可以使用Computed列? – 2013-02-19 21:19:40

回答

2

根据当前正在更新的行以外的数据,您不能拥有计算列。您可以做的最好的做法是自动创建一个后触发器,查询整个表以查找产品代码的下一个值。但为了完成这项工作,你必须使用独占表锁,这将完全破坏并发性,所以这不是一个好主意。

我也不建议使用视图,因为每次读取表时都必须计算ProductCode。这也是一个巨大的性能杀手。通过不再将数据保存在数据库中,永远不会再次触及,您的产品代码将受到虚假更改(例如可能会删除错误输入和未使用的产品)。

这是我推荐的。创建一个新表格:

dbo。SellerProductCode

SellerID LastProductCode 
-------- --------------- 
    1  3 
    2  1 

这台可靠地记录了每个卖家上次使用的产品代码。在INSERTProduct表中,触发器将适当地更新此表中LastProductCode对所有受影响的SellerID s,然后使用适当的值更新Product表中的所有新插入的行。它可能看起来像下面那样。

See this trigger working in a Sql Fiddle

CREATE TRIGGER TR_Product_I ON dbo.Product FOR INSERT 
AS 
SET NOCOUNT ON; 
SET XACT_ABORT ON; 
DECLARE @LastProductCode TABLE (
    SellerID int NOT NULL PRIMARY KEY CLUSTERED, 
    LastProductCode int NOT NULL 
); 

WITH ItemCounts AS (
    SELECT 
     I.SellerID, 
     ItemCount = Count(*) 
    FROM 
     Inserted I 
    GROUP BY 
     I.SellerID 
) 
MERGE dbo.SellerProductCode C 
USING ItemCounts I 
    ON C.SellerID = I.SellerID 
WHEN NOT MATCHED BY TARGET THEN 
    INSERT (SellerID, LastProductCode) 
    VALUES (I.SellerID, I.ItemCount) 
WHEN MATCHED THEN 
    UPDATE SET C.LastProductCode = C.LastProductCode + I.ItemCount 
OUTPUT 
    Inserted.SellerID, 
    Inserted.LastProductCode 
INTO @LastProductCode; 

WITH P AS (
    SELECT 
     NewProductCode = 
     L.LastProductCode + 1 
     - Row_Number() OVER (PARTITION BY I.SellerID ORDER BY P.ProductID DESC), 
     P.* 
    FROM 
     Inserted I 
     INNER JOIN dbo.Product P 
      ON I.ProductID = P.ProductID 
     INNER JOIN @LastProductCode L 
     ON P.SellerID = L.SellerID 
) 
UPDATE P 
SET P.ProductCode = Right('00000' + Convert(varchar(6), P.NewProductCode), 6); 

请注意,即使插入多行这种触发的工作。没有必要预加载SellerProductCode表格 - 新的卖家会自动添加。这将处理并发性问题。如果遇到并发问题,可以添加适当的锁定提示而不会产生有害影响,因为表格将保持非常小,并且可以使用ROWLOCK(除了需要范围锁定的INSERT)。

请做see the Sql Fiddle工作,测试代码演示技术。现在您有real产品代码,没有任何理由可以更改并且可靠。

+0

感谢您的详细解释。 – 2013-02-20 03:11:24

0

我通常会建议使用视图做这类型的计算。如果选择性能是最重要的因素(我看你使用的是persisted),该视图甚至可以被索引。

在计算列中不能有子查询,这实际上意味着您只能访问当前行中的数据。获得此计数的唯一方法是使用user-defined function in your computed column或触发器来更新非计算列。

视图看起来可能像下面这样:

create view ProductCodes as 
select p.ProductId, p.SellerId, 
    (
      select right('000000' + cast(count(*) as varchar(6)), 6) 
      from Product 
      where SellerID = p.SellerID 
       and ProductID <= p.ProductID 
    ) as ProductCode 
from Product p 

一个重要的提醒到你的产品的编号方案,并为双方的观点和UDF选项的垮台,是我们在行的总数依赖用较低的ProductId。这意味着如果在序列中间插入产品,实际上将更高的ProductId更改现有产品的ProductCodes。在这一点上,你必须:(?还是半信半疑,但也许CREATEDATE)

  • 保证产品编号的顺序(单独的身份并没有这样做)
  • 依靠具有保证序列的不同列
  • 使用触发器来获得插入计数,然后永远不会改变。