2017-02-15 4 views
0

このような注文に関する情報を含むこのような表「Order」があります。MS SQL 1つの表の列を他の表の値に依存させるsum

Order ID | ... | Order Total

順序は、しかし、いくつかの項目で構成され、 "項目の順序" テーブルもあります:

Item Order ID | Order ID | Item ID

そして、 "項目" テーブル:

Item ID | Cost

このように、注文< - >アイテムオーダーは1対多の関係を持っていますtionshipとItem Order < - > Itemは多対1の関係にあります。

論理的には、Order Totalは、各Item Orderのコストに依存する必要があります。これにより、Totalのコストが加算されます。

注文の合計がこの注文に対応するすべての商品注文に依存し、必要なすべての商品費用を合計するように、依存関係を設定するにはどうすればよいですか?毎回新しい商品注文を注文に追加すると更新されるはずです。

+1

私は実際にパフォーマンスの問題を実証することができない限り、あなたが計算できる*データを保存しないことをお勧めします。 *冗長なデータを保存するとすぐに、間違ったことになる可能性があります。 –

答えて

0

Item OrderItemテーブルに2つのトリガーを作成できます。 Item Orderテーブルの上に

CREATE TRIGGER T_Item_Recalc_Order_Total 
ON Item 
FOR DELETE, INSERT, UPDATE 
AS 
BEGIN 
SET NOCOUNT ON 

    UPDATE o 
     SET [Order Total] = c.[Cost] 
    FROM Order o 
     INNER JOIN 
     (
      SELECT i_o.[Order_ID] 
        ,SUM(Cost) AS Cost 
      FROM Item_Order i_o 
       INNER JOIN Item i 
        ON i.[Item ID] = i_o.[Item ID] 
      WHERE i_o.[Item ID] IN (
       -- Sum up cost for changed rows only 
       SELECT COALESCE(d.[Item ID], i.[Item ID]) AS [Item ID] 
       FROM deleted d 
        FULL OUTER JOIN inserted i 
         ON d.[Item ID] = i.[Item ID] 
       WHERE d.[Item ID] IS NULL OR i.[Item ID] IS NULL OR d.[Cost] <> i.[Cost] 
      ) 
      GROUP BY i_o.[Order_ID] 
     ) c 
      ON o.[Order_ID] = c.[Order_ID] 

END 

トリガー::Itemテーブルの上に

トリガー

CREATE TRIGGER T_Item_Order_Recalc_Order_Total 
ON Item_Order 
FOR DELETE, INSERT, UPDATE 
AS 
BEGIN 
SET NOCOUNT ON 

    UPDATE o 
     SET [Order Total] = c.[Cost] 
    FROM Order o 
     INNER JOIN 
     (
      SELECT i_o.[Order_ID] 
        ,SUM(Cost) AS Cost 
      FROM Item_Order i_o 
       INNER JOIN Item i 
        ON i.[Item ID] = i_o.[Item ID] 
      WHERE i_o.[Order_ID] IN (
       -- Sum up cost for changed rows only 
       SELECT deleted.[Order_ID] 
       UNION 
       SELECT inserted.[Order_ID] 
      ) 
      GROUP BY i_o.[Order_ID] 
     ) c 
      ON o.[Order_ID] = c.[Order_ID] 

END 
+0

最初のトリガーが壊れています。 'IF'は、' inserted'と 'deleted'にゼロまたは一つの行が含まれていると仮定します。これは安全でない仮定です。 –

+0

@Damien_The_Unbeliever、ありがとう。私はそのロジックを 'WHERE'節に移しました – Serge

1

コメントで示された、私は通常、冗長保存するために希望されないため、潜在的に正しくありませんデータ。しかし、オンザフライで合計を計算する際にパフォーマンス上の問題がある場合は、システムに計算を依頼することをお勧めします。これは、のインデックス付きビューを使用する場合のオプションです。

表のセットアップ:

create table dbo.Orders (
    OrderID int not null, 
    /* NO Total here */ 
    constraint PK_Orders PRIMARY KEY (OrderID) 
) 
go 
create table dbo.Items (
    ItemID int not null, 
    Cost decimal (19,4) not null, 
    constraint PK_Items PRIMARY KEY (ItemID) 
) 
go 
create table dbo.OrderItems (
    OrderItemID int not null, 
    OrderID int not null, 
    ItemID int not null, 
    /* I'd normally prefer Order/Item/Quantity and making Order/Item the PK */ 
    constraint PK_OrderItems PRIMARY KEY (OrderItemID), 
    constraint FK_OrderItems_Orders FOREIGN KEY (OrderID) references Orders (OrderID), 
    constraint FK_OrderItems_Items FOREIGN KEY (ItemID) references Items (ItemID) 
) 

そして今、我々は、ビューを作成することができます:あなたは、挿入、更新を実行すると、のOrderItemsまたはアイテムテーブルに対して、このビューのインデックスを(削除して、今すぐ

create view dbo.OrderTotals 
with schemabinding 
as 
    select 
     OrderID, 
     COUNT_BIG(*) as LineCount, /* Required for indexed view with aggregate */ 
     SUM(Cost) as OrderTotal 
    from 
     dbo.Items i 
      inner join 
     dbo.OrderItems o 
      on 
       i.ItemID = o.ItemID 
    group by 
     OrderID 
go 
create unique clustered index IX_OrderTotals on OrderTotals (OrderID) 

を実際にすべてのビューデータを含む)が自動的に更新されます。

これにより、コーナーケースの問題が回避されます。手動で更新を実行するようトリガーします。

関連する問題