2016-02-19 27 views
3

我目前使用的應用程序對其表具有不同的模式名稱,例如Table1可以有多個存在,如A.Table1和B.Table1。我所有的存儲過程都存儲在dbo下。我正在使用動態sql編寫下面的存儲過程。我目前使用的MS SQL Server 2008R2和它很快將被遷移到MS SQL服務器2012R2在sql server中使用動態模式名​​稱時編寫存儲過程

create procedure dbo.usp_GetDataFromTable1 
@schemaname varchar(100), 
@userid bigint 
as 
begin 
    declare @sql nvarchar(4000) 
    set @sql='select a.EmailID from '[email protected]+'.Table1 a where [email protected]_id'; 
    exec sp_executesql @sql, N'@user_id bigint', @[email protected] 
end 

現在我的問題是, 1是這種類型的方法會影響我的存儲過程的性能呢? 2.如果性能受到影響,那麼如何編寫這種場景的程序?

+0

也許你沒有參與設計,但對於2個數據庫來說聽起來像是一個很棒的場景。就我個人而言,我認爲這是一個可怕的想法。當你需要加入兩張桌子時會發生什麼?索引和所有的優化技巧都在窗外。 – Jeremy

+0

@傑里米是的,你是對的。我沒有參與設計。 – tuhin

回答

0

動態Sql通常影響性能和安全性,大部分時間是最糟糕的。但是,因爲你不能參數標識,這可能是唯一的方法可以讓你,除非你願意複製存儲過程對每個方案:

create procedure dbo.usp_GetDataFromTable1 
@schemaname varchar(100), 
@userid bigint 
as 
begin 
    if @schemaname = 'a' 
    begin 
     select EmailID from a.Table1 where ID = @user_id 
    end 
    else if schemaname = 'b' 
    begin 
     select EmailID from b.Table1 where ID = @user_id 
    end 

end 
+0

downvoter,謹慎解釋? –

+0

我無法使用它,因爲架構名稱a是在用戶註冊時動態創建的。 – tuhin

+1

你在問題中沒有說清楚。 – CompanyDroneFromSector7G

1

解決這個問題的最好的辦法是重新設計的,如果在所有可能的。

您甚至可以通過添加新列來替換模式來實現此功能,例如:Profile,然後將每個模式的所有表合併到一個模式中(例如dbo)。

那麼你的程序將顯示如下:

create procedure dbo.usp_GetDataFromTable1 
@profile int, 
@userid bigint 
as 
begin 
    select a.EmailID from dbo.Table1 a 
    where a.ID = @user_id 
    and a.Profile = @profile 
end 

我已經使用配置文件列的int,但如果你使用一個varchar你甚至可以讓你的架構名稱的輪廓值,是否有幫助使事情更清楚。

1

我會看看一種配置方法,您可以在其中動態創建表和存儲過程,作爲某些預先過程的一部分。我不是100%確定你的方案,但也許這可能是當你添加一個新用戶。然後,您可以在應用程序中按慣例調用這些SP。

例如,新用戶創建調用創建c.Table和c.GetDetails SP的SP。

然後在應用程序中,您可以調用c.GetDetails基於「c」作爲用戶定義的屬性。

這讓您瞭解使用動態SQL時出現的任何安全問題。它仍然是動態的,但一旦建成就會建成。

+0

是的,這可以完成,但如果我需要在SP中更改會發生什麼情況?那已經創建了? – tuhin

+0

這是一個挑戰。你可能需要腳本來更新特效(可能來自應用程序內)並跟蹤模式版本,或者讓所有特效都調用單個參數化過程。 –

+0

謝謝你的建議......我會試一試。 – tuhin

1

動態模式和相同的表結構是相當不尋常的,但你仍然可以得到你想要使用像這樣的內容:

declare @sql nvarchar(4000) 
declare @schemaName VARCHAR(20) = 'schema' 
declare @tableName VARCHAR(20) = 'Table' 
-- this will fail, as the whole string will be 'quoted' within [..] 
-- declare @tableName VARCHAR(20) = 'Category; DELETE FROM TABLE x;' 

set @sql='select * from ' + QUOTENAME(@schemaName) + '.' + QUOTENAME(@tableName) 
PRINT @sql 

-- @user_id is not used here, but it can if the query needs it 
exec sp_executesql @sql, N'@user_id bigint', @user_id=0 

所以,QUOTENAME應該繼續關於SQL注入安全可靠。

1.性能 - 動態SQL不能從一些性能改進中受益(我認爲程序相關的統計數據或類似的東西),所以存在性能風險。但是,對於在相當少量的數據(最多數千萬)上運行的簡單事情,以及對於沒有大量更改(插入和刪除)的數據,我不認爲你會有明顯的問題。

2.替代方案 - bukko已提出解決方案。由於所有表都具有相同的結構,因此可以合併它們。如果它變得巨大,好的索引和分區應該能夠減少查詢執行時間。

0

我能想到這樣做的唯一理由是滿足多個租戶。你很近,但你採取的方法是錯誤的。

我知道有3種多租戶解決方案:每租戶數據庫,每租戶單數據庫模式或單數據庫單個模式(又名逐行租戶)。

其中兩個已被其他用戶在這裏提及。沒有真正詳細說明的是每個租戶的架構,這是它看起來像你在哪裏。對於這種方法,你需要改變你看到數據庫的方式。此時的數據庫只是模式的容器。每個模式可以有自己的設計,存儲過程,觸發器,隊列,函數等。主要目標是數據隔離。你不希望租戶A看到租戶Bs的東西。每個租戶方案的架構的優點是您可以更靈活地使用租戶特定的數據庫更改。它還允許您比每個租戶方法的數據庫更容易擴展。

答案:應該爲每個模式創建相同的存儲過程(創建過程示例:schema_name.stored_proc_name),而不是使用DBO用戶編寫動態SQL來考慮模式。爲了運行架構的存儲過程,您需要模擬與相關架構相關的用戶。它看起來像這樣:

execute as user = 'tenantA' 
exec sp_testing 
revert --revert will take us back to the original user, most likely DBO in your case. 

跨所有租戶的數據整理有點困難。我知道的唯一解決方案是使用DBO用戶運行,並將所有模式的結果「聯合起來」分開執行,如果您有大量模式,則可能會很乏味。

相關問題