2010-09-08 58 views
8

我正在處理一個正在擴展以在多租戶配置中運行的舊應用程序。基本體系結構採用舊應用程序,併爲每個表添加一個StoreID列。然後,每個租戶通過一組的觀點,即在商店ID過濾器,像看到了遺留表:如何編寫INSTEAD OF INSERT觸發器,爲任何表設置一列?

create view AcmeBatWings.data as 
select * from dbo.data d where d.StoreId = 99 

它比票友了一點,但這種簡化的問題。現在

,我可以創造這樣的

create trigger tr_Tenant_fluff on AcmeBatWings 
instead of insert 
as 
insert into AcmeBatWings (Name, StoreId) 
select i.Name, 99 from inserted i 

觸發假設有名稱和STOREID列一個簡單的表。

我的問題是,我有100多個表,如果我要按照這種模式,我將不得不爲每個表列出專門的觸發器,列出每個表的所有字段。不僅在短期內令人厭煩的是維護噩夢,因爲任何表更改都需要包含觸發器修改。

那麼,如何寫一個觸發器,只要在每次插入或更新時說,就可以將StoreId字段設置爲99,以使用StoreId的任何表?

感謝您幫助SQL新手出來!

+0

你提到該系統是多租戶的;你打算爲所有的觸發器硬編碼爲'99'還是StoreId取決於誰在使用系統?另外,什麼版本的SQL Server? – LittleBobbyTables 2010-09-08 20:59:52

+0

你是說你對每個參照相同基表的不同意見,唯一的區別是他們有不同的id過濾器? – 2010-09-08 20:59:57

+0

您的示例中的視圖和觸發器未對齊。在視圖中,AcmeBatWings是一個模式名稱。在觸發器中,它是一個對象名稱。這是什麼?我假設一個模式名稱。它有很大的不同。 – 2010-09-09 01:45:59

回答

7

所以看起來你正在使用多個模式來傳達信息存儲,同時保持對象名稱一致,每家商店瓦特/一個模式,是嗎?以及某種連接/用戶魔法,以便查詢達到正確的視圖。

如果是這樣,我提出了兩個令人震驚的黑客和一個推薦的解決方案(所以你知道你的選擇)。

程度嚴重劈#1,假設商店視圖包括從基表所有列除外 STOREID,在相同的序號位置爲基準表,並沒有其他列:

CREATE TRIGGER tr_Tenant_fluff ON AcmeBatWings.data 
INSTEAD OF INSERT 
AS BEGIN 
    DECLARE @StoreId INT 

    SELECT @StoreId = StoreId FROM dbo.StoreSchemas 
    WHERE StoreSchema = OBJECT_SCHEMA_NAME(@@PROCID) 

    INSERT dbo.data SELECT *, @StoreId FROM inserted 
END 

如果你曾經向基表添加一列,你必須更新所有的商店視圖才能包含該列,否則觸發器將會中斷。

性質惡劣的黑客#2,假設同(1),除了STOREID包括在商店訪問量:

CREATE TRIGGER tr_Tenant_fluff ON AcmeBatWings.data 
INSTEAD OF INSERT 
AS BEGIN 
    DECLARE @StoreId INT 

    SELECT @StoreId = StoreId FROM dbo.StoreSchemas 
    WHERE StoreSchema = OBJECT_SCHEMA_NAME(@@PROCID) 

    SELECT * INTO #inserted FROM inserted 
    UPDATE #inserted SET StoreId = @StoreId 

    INSERT dbo.data SELECT * FROM #inserted 
END 

的劈#2的好處超過砍#1,你可以使用SELECT *定義商店視圖,如果基表發生更改,則只需使用sp_refreshview重新編譯所有商店視圖。缺點是您要將插入的數據從一箇中間表複製到另一箇中,並更新第二個表。這已經使您的INSTEAD OF INSERT觸發器的開銷增加了三倍,這已經相當昂貴。即,INSTEAD OF INSERT觸發的

  • 基開銷 - >成本來填充inserted - >x
  • 成本人口#insertedinserted→約x
  • 成本更新#inserted - >約x
  • 的惡劣劈#2總開銷:約3 x

所以否則,做的最好的事情就是腳本觸發出來。這是一個相當直接的過程,一旦你熟悉系統表,並且無論如何你都可以調整觸發器的產生。對於這個問題,你也應該編寫商店視圖的腳本。

爲了讓你開始:

CREATE TABLE dbo.data (Name VARCHAR(10), StoreId INT) 
GO 
CREATE SCHEMA StoreA 
GO 
CREATE SCHEMA StoreB 
GO 
CREATE SCHEMA StoreC 
GO 
CREATE VIEW StoreA.data AS SELECT Name FROM dbo.data WHERE StoreId = 1 
GO 
CREATE VIEW StoreB.data AS SELECT Name FROM dbo.data WHERE StoreId = 2 
GO 
CREATE VIEW StoreC.data AS SELECT Name FROM dbo.data WHERE StoreId = 3 
GO 
CREATE TABLE dbo.StoreSchemas (StoreSchema SYSNAME UNIQUE, StoreId INT PRIMARY KEY) 
GO 
INSERT dbo.StoreSchemas VALUES ('StoreA', 1), ('StoreB', 2), ('StoreC', 3) 
GO 

DECLARE @crlf NCHAR(2) = NCHAR(13)+NCHAR(10) 
SELECT 
    N'CREATE TRIGGER tr_Tenent_fluff ON '+schema_name(v.schema_id)+N'.data'[email protected] 
+ N'INSTEAD OF INSERT'[email protected] 
+ N'AS BEGIN'[email protected] 
+ N' INSERT dbo.data (' 
+ STUFF((
    SELECT @crlf+N' , '+name FROM sys.columns tc 
    WHERE tc.object_id = t.object_id 
     AND (tc.name IN (SELECT name FROM sys.columns vc WHERE vc.object_id = v.object_id) 
     OR tc.name = N'StoreId') 
    ORDER BY tc.column_id 
    FOR XML PATH(''), TYPE).value('.','NVARCHAR(MAX)') 
    ,5,1,N' ')[email protected] 
+ N' )'[email protected] 
+ N' SELECT' 
+ STUFF((
    SELECT @crlf+N' , '+name 
     + CASE WHEN name = N'StoreId' THEN ' = '+(
      SELECT CONVERT(NVARCHAR,StoreId) FROM dbo.StoreSchemas s 
      WHERE s.StoreSchema = SCHEMA_NAME(v.schema_id) 
     ) 
     ELSE '' END 
    FROM sys.columns tc 
    WHERE tc.object_id = t.object_id 
     AND (tc.name IN (SELECT name FROM sys.columns vc WHERE vc.object_id = v.object_id) 
     OR tc.name = N'StoreId') 
    ORDER BY tc.column_id 
    FOR XML PATH(''), TYPE).value('.','NVARCHAR(MAX)') 
    ,5,1,N' ')[email protected] 
+ N' FROM inserted'[email protected] 
+ N'END'[email protected] 
+ N'GO'[email protected] 
FROM sys.tables t 
JOIN sys.views v 
    ON t.name = v.name 
AND t.schema_id = SCHEMA_ID('dbo') 
AND v.schema_id <> t.schema_id 
WHERE t.name = 'data' 
GO 
+0

Oooo,我喜歡腳本部分。我現在要睡覺了,但我會在早上仔細看看。 – Ukko 2010-09-09 02:43:42

+0

這是票!謝謝! – Ukko 2010-09-09 17:17:34

2

而不是使用觸發器,爲什麼不通過使 StoreId NOT NULL更新每個表,並給它默認值99?

編輯基於澄清

你可以嘗試的插入操作後,UPDATE觸發器來替代INSTEAD OF觸發器

create trigger tr_Tenant_fluff on AcmeBatWings 
AFTER insert, update 
as 

-- You'll need to get @StoreID here somehow 

update AcmeBatWings 
set StoreID = @StoreID 
where [Name] IN (SELECT [Name] FROM inserted) -- update based on primary key 

雖然這種更新您剛纔插入或更新的數據,當您添加或刪除表中的列時,它確實具有不會中斷的好處。

+0

@Martin Smith討論的第三種模型 - 這是我想知道的,但這個例子使用了一個硬編碼的'99',所以這讓我困惑。 – LittleBobbyTables 2010-09-08 20:58:44

+0

是的,我刪除了該評論,因爲我並不確定自己。 – 2010-09-08 21:00:55

+0

商店編號是不是綁定到視圖,而不是基表?在這種情況下,默認基表中的列不會解決問題。 – 2010-09-09 01:40:17

2

所以,如果我有這個權利,每個商店都有自己的ID。數據庫部署到每個商店,並且數據庫應該使用最少的代碼工作量基於其已部署的位置記錄不同的StoreId。這是我的建議。在數據庫中創建一個表來容納StoreId。創建一個函數來檢索StoreId。然後在每個表中創建StoreId列作爲使用該函數的計算列。因此,在每個部署中,唯一的更改是將StoreId更新到一個表中。喜歡的東西:

/* This table is updated with the unique value for each individual store */ 
create table MyStore (
    StoreId int 
) 

insert into MyStore 
    (StoreId) 
    values 
    (99)   
go 

/* This function will be used in the computed column of each table */ 
create function dbo.LookupStoreId() 
returns int 
as 
begin 
    return (select StoreId from MyStore) 
end 
go 

create table AcmeBatWings (
    Name char(10), 
    StoreId as dbo.LookupStoreId() 
) 

insert into AcmeBatWings 
    (Name) 
    values 
    ('abcde') 

select Name, StoreId from AcmeBatWings 
go 

/* Clean up after demo */ 
drop table AcmeBatWings 
drop table MyStore 
drop function dbo.LookupStoreId 
go 
+0

此解決方案的缺點: – 2015-12-07 13:58:38

0

只是絆了這個老問題:

您可以創建爲STOREID默認約束(通過選擇SYS.COLUMNS找到與STOREID列的所有表) ,對於固定的99或者在另一個表中查找的函數或者返回固定99的函數(這樣當你將db移動到另一個存儲時,你只需要改變一個函數而不是100個約束)

相關問題