2013-02-19 1 views
0

나는는 외래 키 1. SellerId하여 특정계산 열 표현

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

제품 ID 신원입니다 제품 코드라는 계산 된 컬럼에 대한 필요성 증가가있다.

그래서 내 계산 열 ProductCode은 판매자가 보유한 제품의 개수를보고 000000 형식이어야합니다. 여기에서 문제는 어떤 판매자 제품을 찾을지를 아는 것입니다. 내가 작성한

은 판매자가

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

이 유형의 계산을 수행하려면보기를 사용하는 것이 좋습니다. 선택 성능이 가장 중요한 요인이라면 뷰를 인덱싱 할 수도 있습니다 (당신이'persisted '를 사용하고있는 것을 봅니다). –

+0

@TimLehner 샘플을 제공해 주시겠습니까? Computed 컬럼을 사용하는 것이 가능한지 아직도 알고 싶습니다. –

답변

2

업데이트되는 현재 행 외부의 데이터를 기반으로 계산 된 열을 가질 수 없습니다. 이 기능을 자동으로 수행하는 최선의 방법은 제품 코드의 다음 값을 찾기 위해 전체 테이블을 쿼리하는 사후 트리거를 만드는 것입니다. 하지만이 작업을하기 위해서는 독점적 인 테이블 잠금을 사용해야하는데, 이것은 동시성을 완전히 파괴 할 것이므로 좋은 생각이 아닙니다.

테이블을 읽을 때마다 ProductCode를 계산해야하기 때문에보기 사용을 권장하지 않습니다. 이것은 거대한 성과 - 살인자가 될 것입니다. 데이터베이스에 값을 저장하지 않고 다시 만지면 제품 코드가 잘못 변경 될 수 있습니다 (실수로 입력 한 제품과 사용하지 않은 제품을 삭제 한 경우처럼).

다음은 내가 대신 권장하는 내용입니다. 새 테이블 만들기 :

dbo.SellerProductCode

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

이 테이블은 안정적으로 각각의 판매자에 대한 마지막으로 사용한 제품 코드를 기록합니다. INSERT에서 Product 테이블로 트리거하면 영향을받는 모든 테이블 SellerID에 대해이 테이블의 LastProductCode이 적절히 업데이트되고 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을 수행하여 기술을 입증 한 코드를 테스트하십시오. 이제 변경된 이유가없고 신뢰할 수있는 제품 코드가 있습니다.

+0

그런 설명을 해주셔서 감사합니다. –

0

내가 일반적으로 이러한 종류의 계산을 할 수있는 뷰를 사용하는 것이 좋습니다 것이 있나요 얼마나 많은 제품 보이지 않는 TSQL 있습니다. 선택 실적이 가장 중요한 요소 인 경우 색인을 생성 할 수도 있습니다 (귀하가 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 옵션을 모두 몰락에 하나의 큰주의해야 할 점은, 우리가 행의 수에 의존하고 있다는 점이다 제품 ID가 낮습니다. 즉, 제품이 시퀀스의 중간에 삽입되면 실제로는 이 더 높은 ProductId가있는 기존 제품의 ProductCodes를으로 변경합니다. 그 시점에서, 다음 중 하나를 수행해야합니다 (? 의심스러운,하지만 어쩌면 CreateDate 여전히)

  • 보증 제품 일련의 순서 (이 작업을 수행하지 않습니다 만의 정체성)
  • 이 보장 된 순서가 다른 컬럼에 의존
  • 트리거를 사용하여 삽입시 카운트를 얻습니다.이 카운트는 변경되지 않습니다.