Суммарные суммы удвоения триггера SQL

Создание корзины покупок, своего рода.

Две таблицы, 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

дб <> рабочий пример

К какой таблице привязан триггер? Что такое удвоение?

nicomp 22.03.2022 18:56

Как вы думаете, что все эти nolock подсказки могут сделать для вас, во время обновления внутри триггера?

Aaron Bertrand 22.03.2022 19:01

Триггер привязан к tblCartLine как после обновления, удаления, вставки. Код nolock существует, потому что я скопировал и вставил из одного набора кода в другой... лень, я знаю. Все обновляемые значения в долларах удваиваются (TotalCost, TotalPrice, TotalTax и т. д.).

bet08075 22.03.2022 19:03

Опубликуйте ВЕСЬ код триггера, начиная с "создать триггер...". А затем отформатируйте весь этот код, чтобы сделать его читабельным. Втиснуть все вместе без пробелов и отступов — это просто плохой знак. Я также предлагаю вам сосредоточиться на одном действии — INSERT, UPDATE или DELETE — чтобы вы могли сосредоточиться на подмножестве логики, которая вам в конечном итоге понадобится. Чтобы действительно понять ваш код, требуется знание вашей схемы, поэтому включите DDL для задействованных таблиц.

SMor 22.03.2022 21:43

В вашей скрипке мне непонятен смысл обновления итогов строк, кажется, вместо этого они должны быть вычисляемыми столбцами. Также вы не принимаете во внимание таблицу deleted, и вы даже можете просто сделать это с индексированным представлением вместо триггеров. Например, см. stackoverflow.com/a/71416097/14868997

Charlieface 22.03.2022 22:16
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
0
5
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Глядя на вашу схему, я вижу огромную кучу проблем. В произвольном порядке:

  • Большинство колонок 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. Вы определенно указали на несколько хороших советов по изменениям, которые я должен внести в будущем. Ваш комментарий также помог мне найти то, что, как мне кажется, станет ответом на проблемы, с которыми я сталкивался. Еще раз спасибо за потраченное время и помощь.

bet08075 23.03.2022 12:57

Базовое форматирование является обязательным, и это даже не связано с кодированием, а просто со здравым смыслом и порядочностью для следующего человека, который его прочитает (которым может быть вы).

Charlieface 23.03.2022 12:59

Другие вопросы по теме