我特别需要一个名为 ProductCode
的计算列ProductId | SellerId | ProductCode
1 1 000001
2 1 000002
3 2 000001
4 1 000003
ProductId 是标识,递增1。 SellerId 是外键。
因此,我的计算列 ProductCode 必须查看卖家有多少产品,格式为000000.这里的问题是如何知道要查找哪些卖家产品?
我写过有一个TSQL,它不会看卖家有多少产品
ALTER TABLE dbo.Product
ADD ProductCode AS RIGHT('000000' + CAST(ProductId AS VARCHAR(6)) , 6) PERSISTED
答案 0 :(得分:2)
根据当前行之外的数据,您不能拥有计算列。您可以做的最好的事情是创建一个后触发器,查询整个表以查找产品代码的下一个值。但是为了完成这项工作,你必须使用一个独占的表锁,这将完全破坏并发性,所以这不是一个好主意。
我也不建议使用视图,因为每次读表时都必须计算ProductCode。这也是一个巨大的性能杀手。如果不保存数据库中的值永远不会再次触及,那么您的产品代码将会发生虚假更改(例如,可能会删除错误输入且从未使用过的产品)。
这是我推荐的内容。创建一个新表:
<强> dbo.SellerProductCode 强>
SellerID LastProductCode
-------- ---------------
1 3
2 1
此表格可靠地记录每个卖家的最后使用的产品代码。在INSERT
到Product
表上,触发器将针对所有受影响的LastProductCode
更新此表中的SellerID
,然后更新所有新插入的行具有适当值的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来查看演示该技术的经过测试的代码。现在您拥有真正的产品代码,这些代码没有任何理由可以改变并且可靠。
答案 1 :(得分: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。那时,您必须: