Teamcenter 替代组变更审计触发器
# 替代组变更审计触发器
## 1. 创建审计表
```sql
IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'ALT_ALTERNATE_AUDIT')
CREATE TABLE [dbo].[ALT_ALTERNATE_AUDIT] (
audit_id BIGINT IDENTITY(1,1) PRIMARY KEY,
operation_type NVARCHAR(10) NOT NULL,
operation_time DATETIME2 NOT NULL DEFAULT GETDATE(),
operation_user NVARCHAR(128) NOT NULL DEFAULT SYSTEM_USER,
alternate_group_puid NVARCHAR(30) NOT NULL,
preferred_item_puid NVARCHAR(30) NULL,
preferred_item_id NVARCHAR(30) NULL,
occurrence_puid NVARCHAR(30) NULL,
parent_bom_view_rev_puid NVARCHAR(30) NULL,
parent_bom_view_puid NVARCHAR(30) NULL,
parent_item_puid NVARCHAR(30) NULL,
parent_item_id NVARCHAR(30) NULL,
parent_item_revision_id NVARCHAR(30) NULL,
pmodifier_user_id NVARCHAR(30) NULL,
pmodifier_user_name NVARCHAR(100) NULL,
plast_mod_date DATETIME2 NULL,
alternate_type NVARCHAR(10) NOT NULL,
old_pvalu_0 NVARCHAR(30) NULL,
old_item_id NVARCHAR(30) NULL,
old_revision_id NVARCHAR(30) NULL,
old_pseq INT NULL,
old_pobject_name NVARCHAR(200) NULL,
new_pvalu_0 NVARCHAR(30) NULL,
new_item_id NVARCHAR(30) NULL,
new_revision_id NVARCHAR(30) NULL,
new_pseq INT NULL,
new_pobject_name NVARCHAR(200) NULL
);
GO
```
## 2. PALT_ITEMS 触发器(零件替代)
```sql
IF EXISTS (SELECT * FROM sys.triggers WHERE name = 'TR_PALT_ITEMS_Audit')
DROP TRIGGER [dbo].[TR_PALT_ITEMS_Audit];
GO
CREATE TRIGGER [dbo].[TR_PALT_ITEMS_Audit]
ON [dbo].[PALT_ITEMS]
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
SET NOCOUNT ON;
DECLARE @op_type NVARCHAR(10);
IF EXISTS (SELECT 1 FROM inserted) AND EXISTS (SELECT 1 FROM deleted)
SET @op_type = 'UPDATE';
ELSE IF EXISTS (SELECT 1 FROM inserted)
SET @op_type = 'INSERT';
ELSE
SET @op_type = 'DELETE';
WITH change_data AS (
SELECT
ISNULL(i.puid, d.puid) AS alternate_group_puid,
@op_type AS operation_type,
'ITEM' AS alternate_type,
d.pvalu_0 AS old_pvalu_0,
d.pseq AS old_pseq,
i.pvalu_0 AS new_pvalu_0,
i.pseq AS new_pseq
FROM inserted i
FULL OUTER JOIN deleted d
ON i.puid = d.puid AND i.pvalu_0 = d.pvalu_0
),
parent_ctx AS (
SELECT DISTINCT
alt.puid AS alternate_group_puid,
alt.rpreferred_itemu AS preferred_item_puid,
pref_item.pitem_id AS preferred_item_id,
occ.puid AS occurrence_puid,
occ.rparent_bvru AS parent_bom_view_rev_puid,
bvr.rbom_viewu AS parent_bom_view_puid,
bv.rparent_itemu AS parent_item_puid,
par_item.pitem_id AS parent_item_id,
par_rev.pitem_revision_id AS parent_item_revision_id,
mod_user.puser_id AS pmodifier_user_id,
mod_user.puser_name AS pmodifier_user_name,
pao.plast_mod_date AS plast_mod_date
FROM [dbo].[PPSALTERNATELIST] alt
INNER JOIN change_data cd
ON cd.alternate_group_puid = alt.puid
LEFT JOIN [dbo].[PITEM] pref_item
ON pref_item.puid = alt.rpreferred_itemu
LEFT JOIN [dbo].[PPSOCCURRENCE] occ
ON occ.ralternate_etc_refu = alt.puid
LEFT JOIN [dbo].[PPSBOMVIEWREVISION] bvr
ON bvr.puid = occ.rparent_bvru
LEFT JOIN [dbo].[PPSBOMVIEW] bv
ON bv.puid = bvr.rbom_viewu
LEFT JOIN [dbo].[PITEM] par_item
ON par_item.puid = bv.rparent_itemu
LEFT JOIN [dbo].[PSTRUCTURE_REVISIONS] sr
ON sr.pvalu_0 = occ.rparent_bvru
LEFT JOIN [dbo].[PITEMREVISION] par_rev
ON par_rev.puid = sr.puid
LEFT JOIN [dbo].[PPOM_APPLICATION_OBJECT] pao
ON pao.puid = alt.puid
LEFT JOIN [dbo].[PPOM_USER] mod_user
ON mod_user.puid = pao.rlast_mod_useru
)
INSERT INTO ALT_ALTERNATE_AUDIT (
operation_type, alternate_group_puid, alternate_type,
occurrence_puid, parent_bom_view_rev_puid, parent_bom_view_puid,
parent_item_puid, parent_item_id, parent_item_revision_id,
preferred_item_puid, preferred_item_id,
pmodifier_user_id, pmodifier_user_name, plast_mod_date,
old_pvalu_0, old_item_id, old_revision_id, old_pseq, old_pobject_name,
new_pvalu_0, new_item_id, new_revision_id, new_pseq, new_pobject_name
)
SELECT
cd.operation_type,
cd.alternate_group_puid,
cd.alternate_type,
ctx.occurrence_puid,
ctx.parent_bom_view_rev_puid,
ctx.parent_bom_view_puid,
ctx.parent_item_puid,
ctx.parent_item_id,
ctx.parent_item_revision_id,
ctx.preferred_item_puid,
ctx.preferred_item_id,
ctx.pmodifier_user_id,
ctx.pmodifier_user_name,
ctx.plast_mod_date,
cd.old_pvalu_0,
old_item.pitem_id,
old_rev.pitem_revision_id,
cd.old_pseq,
old_pwo.pobject_name,
cd.new_pvalu_0,
new_item.pitem_id,
new_rev.pitem_revision_id,
cd.new_pseq,
new_pwo.pobject_name
FROM change_data cd
INNER JOIN parent_ctx ctx
ON ctx.alternate_group_puid = cd.alternate_group_puid
LEFT JOIN [dbo].[PITEM] old_item
ON old_item.puid = cd.old_pvalu_0
LEFT JOIN [dbo].[PITEMREVISION] old_rev
ON old_rev.puid = cd.old_pvalu_0
LEFT JOIN [dbo].[PWORKSPACEOBJECT] old_pwo
ON old_pwo.puid = cd.old_pvalu_0
LEFT JOIN [dbo].[PITEM] new_item
ON new_item.puid = cd.new_pvalu_0
LEFT JOIN [dbo].[PITEMREVISION] new_rev
ON new_rev.puid = cd.new_pvalu_0
LEFT JOIN [dbo].[PWORKSPACEOBJECT] new_pwo
ON new_pwo.puid = cd.new_pvalu_0
WHERE cd.operation_type IN ('INSERT', 'DELETE')
OR (cd.operation_type = 'UPDATE'
AND (ISNULL(cd.old_pvalu_0, '') <> ISNULL(cd.new_pvalu_0, '')
OR ISNULL(cd.old_pseq, -1) <> ISNULL(cd.new_pseq, -1)));
END;
GO
```
## 3. PALT_VIEWS 触发器(组件替代)
```sql
IF EXISTS (SELECT * FROM sys.triggers WHERE name = 'TR_PALT_VIEWS_Audit')
DROP TRIGGER [dbo].[TR_PALT_VIEWS_Audit];
GO
CREATE TRIGGER [dbo].[TR_PALT_VIEWS_Audit]
ON [dbo].[PALT_VIEWS]
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
SET NOCOUNT ON;
DECLARE @op_type NVARCHAR(10);
IF EXISTS (SELECT 1 FROM inserted) AND EXISTS (SELECT 1 FROM deleted)
SET @op_type = 'UPDATE';
ELSE IF EXISTS (SELECT 1 FROM inserted)
SET @op_type = 'INSERT';
ELSE
SET @op_type = 'DELETE';
WITH change_data AS (
SELECT
ISNULL(i.puid, d.puid) AS alternate_group_puid,
@op_type AS operation_type,
'VIEW' AS alternate_type,
d.pvalu_0 AS old_pvalu_0,
d.pseq AS old_pseq,
i.pvalu_0 AS new_pvalu_0,
i.pseq AS new_pseq
FROM inserted i
FULL OUTER JOIN deleted d
ON i.puid = d.puid AND i.pvalu_0 = d.pvalu_0
),
parent_ctx AS (
SELECT DISTINCT
alt.puid AS alternate_group_puid,
alt.rpreferred_itemu AS preferred_item_puid,
pref_item.pitem_id AS preferred_item_id,
occ.puid AS occurrence_puid,
occ.rparent_bvru AS parent_bom_view_rev_puid,
bvr.rbom_viewu AS parent_bom_view_puid,
bv.rparent_itemu AS parent_item_puid,
par_item.pitem_id AS parent_item_id,
par_rev.pitem_revision_id AS parent_item_revision_id,
mod_user.puser_id AS pmodifier_user_id,
mod_user.puser_name AS pmodifier_user_name,
pao.plast_mod_date AS plast_mod_date
FROM [dbo].[PPSALTERNATELIST] alt
INNER JOIN change_data cd
ON cd.alternate_group_puid = alt.puid
LEFT JOIN [dbo].[PITEM] pref_item
ON pref_item.puid = alt.rpreferred_itemu
LEFT JOIN [dbo].[PPSOCCURRENCE] occ
ON occ.ralternate_etc_refu = alt.puid
LEFT JOIN [dbo].[PPSBOMVIEWREVISION] bvr
ON bvr.puid = occ.rparent_bvru
LEFT JOIN [dbo].[PPSBOMVIEW] bv
ON bv.puid = bvr.rbom_viewu
LEFT JOIN [dbo].[PITEM] par_item
ON par_item.puid = bv.rparent_itemu
LEFT JOIN [dbo].[PSTRUCTURE_REVISIONS] sr
ON sr.pvalu_0 = occ.rparent_bvru
LEFT JOIN [dbo].[PITEMREVISION] par_rev
ON par_rev.puid = sr.puid
LEFT JOIN [dbo].[PPOM_APPLICATION_OBJECT] pao
ON pao.puid = alt.puid
LEFT JOIN [dbo].[PPOM_USER] mod_user
ON mod_user.puid = pao.rlast_mod_useru
)
INSERT INTO ALT_ALTERNATE_AUDIT (
operation_type, alternate_group_puid, alternate_type,
occurrence_puid, parent_bom_view_rev_puid, parent_bom_view_puid,
parent_item_puid, parent_item_id, parent_item_revision_id,
preferred_item_puid, preferred_item_id,
pmodifier_user_id, pmodifier_user_name, plast_mod_date,
old_pvalu_0, old_item_id, old_revision_id, old_pseq, old_pobject_name,
new_pvalu_0, new_item_id, new_revision_id, new_pseq, new_pobject_name
)
SELECT
cd.operation_type,
cd.alternate_group_puid,
cd.alternate_type,
ctx.occurrence_puid,
ctx.parent_bom_view_rev_puid,
ctx.parent_bom_view_puid,
ctx.parent_item_puid,
ctx.parent_item_id,
ctx.parent_item_revision_id,
ctx.preferred_item_puid,
ctx.preferred_item_id,
ctx.pmodifier_user_id,
ctx.pmodifier_user_name,
ctx.plast_mod_date,
cd.old_pvalu_0,
old_bv_item.pitem_id,
old_rev.pitem_revision_id,
cd.old_pseq,
old_pwo.pobject_name,
cd.new_pvalu_0,
new_bv_item.pitem_id,
new_rev.pitem_revision_id,
cd.new_pseq,
new_pwo.pobject_name
FROM change_data cd
INNER JOIN parent_ctx ctx
ON ctx.alternate_group_puid = cd.alternate_group_puid
LEFT JOIN [dbo].[PPSBOMVIEW] old_bv
ON old_bv.puid = cd.old_pvalu_0
LEFT JOIN [dbo].[PITEM] old_bv_item
ON old_bv_item.puid = old_bv.rparent_itemu
LEFT JOIN [dbo].[PITEMREVISION] old_rev
ON old_rev.puid = cd.old_pvalu_0
LEFT JOIN [dbo].[PWORKSPACEOBJECT] old_pwo
ON old_pwo.puid = cd.old_pvalu_0
LEFT JOIN [dbo].[PPSBOMVIEW] new_bv
ON new_bv.puid = cd.new_pvalu_0
LEFT JOIN [dbo].[PITEM] new_bv_item
ON new_bv_item.puid = new_bv.rparent_itemu
LEFT JOIN [dbo].[PITEMREVISION] new_rev
ON new_rev.puid = cd.new_pvalu_0
LEFT JOIN [dbo].[PWORKSPACEOBJECT] new_pwo
ON new_pwo.puid = cd.new_pvalu_0
WHERE cd.operation_type IN ('INSERT', 'DELETE')
OR (cd.operation_type = 'UPDATE'
AND (ISNULL(cd.old_pvalu_0, '') <> ISNULL(cd.new_pvalu_0, '')
OR ISNULL(cd.old_pseq, -1) <> ISNULL(cd.new_pseq, -1)));
END;
GO
```
## 4. PPSALTERNATELIST 触发器(首选零组件变更)
```sql
IF EXISTS (SELECT * FROM sys.triggers WHERE name = 'TR_PPSALTERNATELIST_Audit')
DROP TRIGGER [dbo].[TR_PPSALTERNATELIST_Audit];
GO
CREATE TRIGGER [dbo].[TR_PPSALTERNATELIST_Audit]
ON [dbo].[PPSALTERNATELIST]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
WITH changed AS (
SELECT
i.puid AS alternate_group_puid,
d.rpreferred_itemu AS old_preferred_puid,
i.rpreferred_itemu AS new_preferred_puid,
occ.puid AS occurrence_puid,
occ.rparent_bvru AS parent_bom_view_rev_puid,
bvr.rbom_viewu AS parent_bom_view_puid,
bv.rparent_itemu AS parent_item_puid,
par_item.pitem_id AS parent_item_id,
par_rev.pitem_revision_id AS parent_item_revision_id,
mod_user.puser_id AS pmodifier_user_id,
mod_user.puser_name AS pmodifier_user_name,
pao.plast_mod_date AS plast_mod_date
FROM inserted i
INNER JOIN deleted d
ON d.puid = i.puid
LEFT JOIN [dbo].[PPSOCCURRENCE] occ
ON occ.ralternate_etc_refu = i.puid
LEFT JOIN [dbo].[PPSBOMVIEWREVISION] bvr
ON bvr.puid = occ.rparent_bvru
LEFT JOIN [dbo].[PPSBOMVIEW] bv
ON bv.puid = bvr.rbom_viewu
LEFT JOIN [dbo].[PITEM] par_item
ON par_item.puid = bv.rparent_itemu
LEFT JOIN [dbo].[PSTRUCTURE_REVISIONS] sr
ON sr.pvalu_0 = occ.rparent_bvru
LEFT JOIN [dbo].[PITEMREVISION] par_rev
ON par_rev.puid = sr.puid
LEFT JOIN [dbo].[PPOM_APPLICATION_OBJECT] pao
ON pao.puid = i.puid
LEFT JOIN [dbo].[PPOM_USER] mod_user
ON mod_user.puid = pao.rlast_mod_useru
WHERE ISNULL(d.rpreferred_itemu, '') <> ISNULL(i.rpreferred_itemu, '')
)
INSERT INTO ALT_ALTERNATE_AUDIT (
operation_type, alternate_group_puid, alternate_type,
occurrence_puid, parent_bom_view_rev_puid, parent_bom_view_puid,
parent_item_puid, parent_item_id, parent_item_revision_id,
preferred_item_puid, preferred_item_id,
pmodifier_user_id, pmodifier_user_name, plast_mod_date,
old_pvalu_0, old_item_id, old_pseq,
new_pvalu_0, new_item_id, new_pseq
)
SELECT
'UPDATE' AS operation_type,
cd.alternate_group_puid,
'GROUP' AS alternate_type,
cd.occurrence_puid,
cd.parent_bom_view_rev_puid,
cd.parent_bom_view_puid,
cd.parent_item_puid,
cd.parent_item_id,
cd.parent_item_revision_id,
cd.new_preferred_puid,
new_pref_item.pitem_id,
cd.pmodifier_user_id,
cd.pmodifier_user_name,
cd.plast_mod_date,
cd.old_preferred_puid,
old_pref_item.pitem_id,
NULL,
cd.new_preferred_puid,
new_pref_item.pitem_id,
NULL
FROM changed cd
LEFT JOIN [dbo].[PITEM] old_pref_item
ON old_pref_item.puid = cd.old_preferred_puid
LEFT JOIN [dbo].[PITEM] new_pref_item
ON new_pref_item.puid = cd.new_preferred_puid;
END;
GO
```
## 验证查询
```sql
-- 查看所有审计记录
SELECT * FROM [dbo].[ALT_ALTERNATE_AUDIT] ORDER BY audit_id DESC;
-- 按父项零组件ID查询
SELECT * FROM [dbo].[ALT_ALTERNATE_AUDIT]
WHERE parent_item_id = 'xxx'
ORDER BY operation_time DESC;
-- 按修改人查询
SELECT * FROM [dbo].[ALT_ALTERNATE_AUDIT]
WHERE pmodifier_user_id = 'xxx'
ORDER BY operation_time DESC;
-- 统计各类操作数量
SELECT operation_type, alternate_type, COUNT(*) AS cnt
FROM [dbo].[ALT_ALTERNATE_AUDIT]
GROUP BY operation_type, alternate_type
ORDER BY operation_type, alternate_type;
```
## 数据追溯路径
```
PALT_ITEMS.puid / PALT_VIEWS.puid
→ PPSALTERNATELIST.puid
→ PPSOCCURRENCE.ralternate_etc_refu (1:1)
→ PPSOCCURRENCE.rparent_bvru → PPSBOMVIEWREVISION.puid
→ PPSBOMVIEWREVISION.rbom_viewu → PPSBOMVIEW.puid
→ PPSBOMVIEW.rparent_itemu → PITEM.puid (父项)
→ PITEM.pitem_id (父项零组件ID)
→ PSTRUCTURE_REVISIONS.pvalu_0 = PPSBOMVIEWREVISION.puid
→ PSTRUCTURE_REVISIONS.puid → PITEMREVISION.puid
→ PITEMREVISION.pitem_revision_id (父项版本)
→ PPOM_APPLICATION_OBJECT.puid = PPSALTERNATELIST.puid
→ PPOM_APPLICATION_OBJECT.rlast__mod_useru → PPOM_USER.puid
→ PPOM_USER.puser_id (修改人账户)
→ PPOM_USER.puser_name (修改人名称)
→ PPOM_APPLICATION_OBJECT.plast_mod_date (最后修改时间)
```
## 说明
- 所有 `[tc].` 前缀已去掉,在 `[tc]` 数据库下直接执行
- `PPSOCCURRENCE` 1:1 关系,每个替代组对应一个 BOM 行
- `rpreferred_itemu` 双 r 拼写
- 零件替代 → `PALT_ITEMS`(pvalu_0 → PITEM.puid)
- 组件替代 → `PALT_VIEWS`(pvalu_0 → PPSBOMVIEW.puid → PITEM.puid)
- 修改人信息 → `PPOM_APPLICATION_OBJECT.puid = PPSALTERNATELIST.puid` → `PPOM_USER`
- 审计表修改人字段统一加 `p` 前缀:`pmodifier_user_id`, `pmodifier_user_name`, `plast_mod_date`
- UPDATE 只记录值实际变化的行,避免无意义审计
浙公网安备 33010602011771号