以下是问题的简化版本:
表1列:名称,颜色,形状,数量。
表2列:颜色,形状,数量
表2应该保留所有颜色/形状对的总量,例如,如果table1有条目
Joe, blue, square, 2
Bob, red, square, 1
Alice, blue, square, 3
然后table2应为:
blue, square, 5
red, square, 1
我想编写一个触发器,每次将某些内容插入table1时都会更新table2。对于INSERTED表的每一行,它应检查特定颜色/形状组合是否已在table2中。如果是,则应更新该行以反映新总计。如果不是,则应添加该行。但是,我是SQL的新手,我不知道如何检查INSERTED的每一行。我已经在下面发布了我的早期尝试。任何和所有的帮助表示赞赏!
编辑:插入触发器的最终工作版本。
CREATE TRIGGER after_insert_table1 ON table1
AFTER INSERT
AS
BEGIN
MERGE table2 AS TARGET
USING (select color,shape,SUM(amount) as amount from INSERTED
group by color,shape) AS SOURCE
ON TARGET.color = SOURCE.color
AND TARGET.shape = SOURCE.shape
WHEN MATCHED THEN
UPDATE
SET TARGET.amount = (TARGET.amount + SOURCE.amount)
WHEN NOT MATCHED THEN
INSERT (color,shape,amount)
VALUES(SOURCE.color, SOURCE.shape, SOURCE.amount)
;
END/
编辑:几乎正在工作!所以它可以根据需要运行。但我担心这种方法效率低下,特别是当table2非常大时。我希望能够只检查已删除的行,而不是检查table2中的每一行。
CREATE TRIGGER after_delete_table1 ON table1
AFTER DELETE
AS
BEGIN
MERGE table2 AS TARGET
USING (select color,shape,SUM(amount) as amount from DELETED
group by color,shape) AS SOURCE
ON TARGET.color = SOURCE.color
AND TARGET.shape = SOURCE.shape
WHEN MATCHED THEN
UPDATE
SET TARGET.amount = (TARGET.amount - SOURCE.amount)
;
DELETE FROM table2
WHERE NOT EXISTS
(SELECT NULL FROM
table1 t WHERE t.color = table2.color AND
t.shape = table2.shape);
END/
table2中允许零值。但是,如果table1中不再有相应的行,则应删除table2中的一行。这就是我现在要做的事情,但是把它放在合并中肯定是行不通的。
答案 0 :(得分:2)
MERGE
的问题在于它有效地并行应用所有更改。因此,如果您一次插入所有3行(而不是单独的语句),那么对于blue
,square
行,不会找到现有行,因此两者都进入NOT MATCHED
MERGE
并插入一行。
因此,您需要预先合并任何只应影响目标表中一行的新行:
MERGE table2 AS TARGET
USING (select color,shape,SUM(amount) as amount from INSERTED
group by color,shape) AS SOURCE
ON TARGET.color = SOURCE.color
AND TARGET.shape = SOURCE.shape
WHEN MATCHED THEN
UPDATE
SET TARGET.amount = (TARGET.amount + SOURCE.amount)
WHEN NOT MATCHED THEN
INSERT (color,shape,amount)
VALUES(SOURCE.color, SOURCE.shape, SOURCE.amount)
;
对于所有可能的操作,并且要求仅在实际行数返回0时消除,我再次向cnt
引入Table2
列,并将触发器实现为:< / p>
create table dbo.Table1 (
name varchar(10) not null,
color varchar(10) not null,
shape varchar(10) not null,
amount int not null,
constraint PK_Table1 PRIMARY KEY (name /*any other columns?*/)
)
go
create table dbo.Table2 (
color varchar(10) not null,
shape varchar(10) not null,
amount int not null,
cnt int not null
)
go
CREATE TRIGGER after_insert_table1 ON table1
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
MERGE table2 AS TARGET
USING (select color,shape,SUM(amount) as amount,SUM(Cnt) as Cnt from
(select color,shape,amount,1 as Cnt from INSERTED
union all
select color,shape,-amount,-1 from deleted) t
group by color,shape) AS SOURCE
ON TARGET.color = SOURCE.color
AND TARGET.shape = SOURCE.shape
WHEN MATCHED AND TARGET.cnt + SOURCE.cnt = 0 THEN
DELETE
WHEN MATCHED THEN
UPDATE
SET
TARGET.amount = (TARGET.amount + SOURCE.amount),
TARGET.cnt= (TARGET.cnt + SOURCE.cnt)
WHEN NOT MATCHED THEN
INSERT (color,shape,amount,cnt)
VALUES(SOURCE.color, SOURCE.shape, SOURCE.amount,SOURCE.cnt)
;
END
go
insert into dbo.Table1 (name,color,shape,amount) values
('Joe', 'blue', 'square', 2),
('Bob', 'red', 'square', 1),
('Alice', 'blue', 'square', 3)
go
select * from Table2
go
delete from dbo.Table1 where name='Bob' or name='Alice'
go
select * from Table2
go
update dbo.Table1 set Amount = 0
go
select * from Table2
如果您想以indexed view代替,请按照以下方式进行操作:
create table dbo.Table1 (
name varchar(10) not null,
color varchar(10) not null,
shape varchar(10) not null,
amount int not null,
constraint PK_Table1 PRIMARY KEY (name /*any other columns?*/)
)
go
create view dbo.Table2
with schemabinding
as
select color,shape,SUM(amount) as amount
,COUNT_BIG(*) as cnt /* Needed to make indexed view possible */
from dbo.Table1
group by color,shape
go
create unique clustered index IX_Table2 on Table2(color,shape)
go
insert into dbo.Table1 (name,color,shape,amount) values
('Joe', 'blue', 'square', 2),
('Bob', 'red', 'square', 1),
('Alice', 'blue', 'square', 3)
go
select * from Table2
这里的优势在于,SQL Server有效地编写了触发器(但是它们远离您,无法看到它们)并自动维护Table2
。这些实现已在(数千,数百万,更多?)系统上进行了测试 - 它们肯定有效。
您必须接受视图中的其他Cnt
列 - 这是能够执行SUM()
的要求。
答案 1 :(得分:1)
我想提出一个替代解决方案,它同样处理DELETE
以及INSERT
和UPDATE
,所有这些都在一个触发器中。我选择在组的总计数为0时删除(通过删除,明确设置为零,或通过将正值和负值相加为0)。
CREATE TABLE dbo.NameShape (
Name varchar(30) NOT NULL CONSTRAINT PK_NameShape PRIMARY KEY CLUSTERED,
Color varchar(20) NOT NULL,
Shape varchar(20) NOT NULL,
Amount int NOT NULL
);
CREATE TABLE dbo.ShapeCount (
Color varchar(20) NOT NULL,
Shape varchar(20) NOT NULL,
Total int NOT NULL
);
然后触发器如下所示:
CREATE TRIGGER TR_NameShape_IUD ON dbo.NameShape FOR INSERT, UPDATE, DELETE
AS
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRAN;
WITH ColorShapes AS (
SELECT
Color,
Shape
FROM Inserted
UNION
SELECT
Color,
Shape
FROM Deleted
)
SELECT
CS.Color,
CS.Shape,
Total = Sum(Coalesce(S.Amount, 0))
INTO #NewValues
FROM
ColorShapes CS
LEFT JOIN dbo.NameShape S WITH (ROWLOCK, HOLDLOCK)
ON CS.Color = S.Color
AND CS.Shape = S.Shape
GROUP BY
CS.Color,
CS.Shape
;
DELETE SC
FROM
dbo.ShapeCount SC
INNER JOIN #NewValues V
ON SC.Color = V.Color
AND SC.Shape = V.Shape
WHERE
V.Total = 0
;
UPDATE SC
SET SC.Total = V.Total
FROM
dbo.ShapeCount SC
INNER JOIN #NewValues V
ON SC.Color = V.Color
AND SC.Shape = V.Shape
WHERE
SC.Total <> V.Total
;
INSERT dbo.ShapeCount (Color, Shape, Total)
SELECT
V.Color,
V.Shape,
V.Total
FROM
#NewValues V
WHERE
V.Total <> 0
AND NOT EXISTS (
SELECT *
FROM dbo.ShapeCount SC
WHERE
V.Color = SC.Color
AND V.Shape = SC.Shape
)
;
COMMIT TRAN
我不想吓唬你,但我认为你应该被告知MERGE may have some issues in some situations。
可以使用上面的第一个查询作为MERGE
语句的来源,但要小心:您必须过滤掉任何不受影响的行。请注意,将DELETE
添加到合并中会有点困难,因为您必须确保不删除DELETE
中未涉及的形状/颜色。
答案 2 :(得分:0)
实际上,INSERTED
是special table。
试试这个:
CREATE TRIGGER after_insert_table1 ON table1
AFTER INSERT
AS
BEGIN
IF NOT EXISTS (
SELECT *
FROM table2
JOIN INSERTED
ON table2.color = INSERTED.color
AND table2.shape = INSERTED.shape)
INSERT INTO table2(color,shape,amount)
SELECT INSERTED.color,
INSERTED.shape,
INSERTED.amount
FROM INSERTED;
ELSE
UPDATE table2
SET amount = (amount + INSERTED.amount)
FROM table2
JOIN INSERTED
ON color = INSERTED.color
AND shape = INSERTED.shape;
END