Создание корзины покупок, своего рода.
Две таблицы, Cart
и CartLine
, связанные с полем CartID
. Мне нужен триггер sql для обновления общих полей в таблице Cart
, когда запись CartLine
добавляется, обновляется, удаляется. Мой код ниже. Кажется, мои суммы удваиваются, когда обновление/и т.д. происходит.
Код: '
--Update cart totals.
begin
with cte2 as (
select
c.CartID,
isnull(sum(cl.GoodsTotal), 0) [TotalGoods],
isnull(sum(cl.LineTotal), 0) [TotalPrice],
isnull(sum(cl.TaxTotal), 0) [TotalTax],
(isnull(sum(cl.LineTotal), 0) + isnull(sum(cl.TaxTotal), 0)) [TotalTotal]
from tblCartLine cl with (nolock)
join inserted i with (nolock) on cl.CartID = i.CartID
inner join tblCart c with (nolock) on i.CartID = c.CartID
where isnull(cl.Deleted, 0) = 0
group by c.CartID
)
update tblCart
set
TotalCost = cte2.TotalGoods,
TotalPrice = cte2.TotalPrice,
TotalTax = cte2.TotalTax,
TotalTotal = cte2.TotalTotal
from tblCart c
inner join cte2 on c.CartID = cte2.CartID
where c.CartID = cte2.CartID
Как вы думаете, что все эти nolock
подсказки могут сделать для вас, во время обновления внутри триггера?
Триггер привязан к tblCartLine как после обновления, удаления, вставки. Код nolock существует, потому что я скопировал и вставил из одного набора кода в другой... лень, я знаю. Все обновляемые значения в долларах удваиваются (TotalCost, TotalPrice, TotalTax и т. д.).
Опубликуйте ВЕСЬ код триггера, начиная с "создать триггер...". А затем отформатируйте весь этот код, чтобы сделать его читабельным. Втиснуть все вместе без пробелов и отступов — это просто плохой знак. Я также предлагаю вам сосредоточиться на одном действии — INSERT, UPDATE или DELETE — чтобы вы могли сосредоточиться на подмножестве логики, которая вам в конечном итоге понадобится. Чтобы действительно понять ваш код, требуется знание вашей схемы, поэтому включите DDL для задействованных таблиц.
В вашей скрипке мне непонятен смысл обновления итогов строк, кажется, вместо этого они должны быть вычисляемыми столбцами. Также вы не принимаете во внимание таблицу deleted
, и вы даже можете просто сделать это с индексированным представлением вместо триггеров. Например, см. stackoverflow.com/a/71416097/14868997
Глядя на вашу схему, я вижу огромную кучу проблем. В произвольном порядке:
NULL
способны. Почему? Что бы это значило, если бы у Cart
не было DateTimeCreated
, почему вообще был бы такой ряд? А как насчет Deleted
, существует ли третье неопределенное состояние (квантовая механика?), которое ни удалено, ни не удалено?money
, который имеет серьезные проблемы с округлением. Вместо этого используйте decimal
с соответствующей точностью и масштабом.tbl
раздражает, все и так знают, что это таблицы.deleted
в случае обновлений и удалений. Вам нужно присоединиться к нему по первичному ключу.inserted
и deleted
и вычесть разницу.NOLOCK
это неправильно. Если вы беспокоитесь о блокировке, вам, вероятно, следует использовать изоляцию SNAPSHOT
, а если вы беспокоитесь о производительности, вы можете использовать WITH (TABLOCK)
для получения тех же преимуществ.CartLine
, вы можете просто использовать вычисляемые столбцы:
ALTER TABLE tblCartLine
ADD ExtCost AS (Quantity * Cost);
ALTER TABLE tblCartLine
ADD TaxTotal AS (Quantity * Price) * (TaxRate / 100.0);
ALTER TABLE tblCartLine
ADD LineTotal AS (Quantity * i.Price);
И тогда ваш триггер должен выглядеть так
CREATE TRIGGER [dbo].[UtblCartLine]
ON [dbo].[tblCartLine]
AFTER INSERT,DELETE,UPDATE
AS
SET NOCOUNT ON;
IF TRIGGER_NESTLEVEL(OBJECT_ID('dbo.UtblCartLine')) > 0
RETURN;
IF NOT EXISTS (SELECT 1 FROM inserted) AND NOT EXISTS (SELECT 1 FROM deleted)
RETURN;
UPDATE tblCart
SET
TotalCost += i.DiffGoods,
TotalPrice += i.DiffPrice,
TotalTax += i.DiffTax,
TotalTotal += i.DiffTotal
FROM tblCart c
JOIN (
SELECT
ISNULL(i.CartID, d.CartID) CartID,
ISNULL(SUM(i.GoodsTotal), 0) - ISNULL(SUM(d.GoodsTotal), 0) DiffGoods,
ISNULL(SUM(i.LineTotal), 0) - ISNULL(SUM(d.LineTotal), 0) DiffPrice,
ISNULL(SUM(i.TaxTotal), 0) - ISNULL(SUM(d.TaxTotal), 0) DiffTax,
ISNULL(SUM(i.LineTotal + i.TaxTotal), 0) - ISNULL(SUM(d.LineTotal + d.TaxTotal), 0) DiffTotal
FROM inserted i
FULL JOIN deleted d ON d.CartLineID = i.CartLineID
GROUP BY
ISNULL(i.CartID, d.CartID)
) i ON i.CartID = c.CartID;
Добавление Deleted = 0
требует корректной условной агрегации.
Однако я рекомендую вам вообще не использовать триггеры.
Вместо этого используйте представление. Если это необходимо для производительности, вы можете использовать индексированное представление. Сервер может поддерживать индекс в представлении в соответствии с любыми обновлениями/вставками и эффективно выполнять весь вышеуказанный код автоматически.
Индексированные представления имеют некоторые ограничения. Особенно:
COUNT_BIG(*)
, а единственная разрешенная агрегация — это SUM
.CREATE VIEW CartTotal
WITH SCHEMABINDING AS
SELECT
cl.CartID,
COUNT_BIG(*) NumberOfLines,
SUM(cl.GoodsTotal) TotalGoods,
SUM(cl.LineTotal) TotalPrice,
SUM(cl.TaxTotal) TotalTax,
SUM(cl.LineTotal + cl.TaxTotal) TotalTotal
FROM tblCartLine cl
WHERE cl.Deleted = 0
GROUP BY
cl.CartID;
go
CREATE UNIQUE CLUSTERED INDEX CX_CartTotal ON CartTotal (CartID);
Спасибо, @Charlieface, за то, что нашли время, чтобы просмотреть код. Я не пожизненный администратор базы данных, и я многого не знаю о SQL. Вы определенно указали на несколько хороших советов по изменениям, которые я должен внести в будущем. Ваш комментарий также помог мне найти то, что, как мне кажется, станет ответом на проблемы, с которыми я сталкивался. Еще раз спасибо за потраченное время и помощь.
Базовое форматирование является обязательным, и это даже не связано с кодированием, а просто со здравым смыслом и порядочностью для следующего человека, который его прочитает (которым может быть вы).
К какой таблице привязан триггер? Что такое удвоение?