2013-02-19 2 views
0

У меня есть конкретные потребности для вычисляемого столбца под названием ProductCodeВычисляется выражение колонки

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

PRODUCTID является удостоверением, приращения по 1. SellerId является внешним ключом.

Таким образом, моя вычисленная колонка ProductCode должна посмотреть, сколько товаров имеет Продавец и быть в формате 000000. Проблема здесь в том, как узнать, какие продукты Sellers искать?

я написал есть TSQL, который не выглядит как много продуктов делает продавец имеет

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

Я бы порекомендовал использовать представление для этого типа расчета. Представление можно даже индексировать, если выбранная производительность является наиболее важным фактором (я вижу, что вы используете 'persisted'). –

+0

@TimLehner Можете ли вы предоставить образец? Я все еще хотел бы знать, возможно ли это использование вычисляемых столбцов? –

ответ

2

У вас нет расчетного столбца на основе данных за пределами текущей строки, которая обновляется. Лучшее, что вы можете сделать, чтобы сделать это автоматическое, - создать после триггер, который запрашивает всю таблицу, чтобы найти следующее значение для кода продукта. Но для выполнения этой работы вам придется использовать эксклюзивную блокировку таблицы, которая полностью уничтожит параллелизм, поэтому это не очень хорошая идея.

Я также не рекомендую использовать представление, потому что он должен будет вычислять ProductCode каждый раз, когда вы читаете таблицу. Это было бы огромным убийцей производительности. Не сохраняя значение в базе данных, которое никогда не будет затронуто снова, ваши коды продуктов будут подвержены ложным изменениям (как, например, при удалении ошибочно введенного и неиспользуемого продукта).

Вот что я рекомендую вместо этого. Создать новую таблицу:

dbo.SellerProductCode

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

Эта таблица надежно фиксирует последний использовавшийся код продукта для каждого продавца. В таблице INSERT вашей таблицы Product триггер обновит 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

Спасибо за подробное объяснение. –

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. Это означает, что если Продукт вставлен в середине последовательности, то на самом деле изменит ProductCodes существующих продуктов с более высоким ProductId. В этот момент, вы должны либо: (? Еще сомнительное, но, возможно, CreateDate)

  • Гарантировать последовательность PRODUCTID (одна личность не делает этого)
  • Положитесь на другой столбец, который имеет гарантированную последовательность
  • Используйте триггер, чтобы получить счет во вставке, который затем не изменяется.

 Смежные вопросы

  • Нет связанных вопросов^_^