2016-07-22 35 views
0

以下是我正在嘗試的操作。我試圖創建一個存儲過程,只需輸入表,列和列值的名稱,它將刪除該表中與該值關聯的所有記錄。有沒有簡單的方法來做到這一點?我不太瞭解SQL,但仍然在學習它。如何創建DELETE語句存儲過程使用TableName,ColumnName和ColumnValue作爲傳遞參數

這是我到目前爲止。

ALTER PROCEDURE [dbo].[name of stored procedure] 
@TABLE_NAME varchar(50), 
@COLUMN_NAME varchar(50), 
@VALUE varchar(5) 

AS 
BEGIN 
    SET NOCOUNT ON; 

    DECLARE @RowsDeleted int; 
    DECLARE @sql VARCHAR(500); 

    SET @sql = 'DELETE FROM (name of table).' + @TABLE_NAME + ' WHERE ' + @COLUMN_NAME + '=' + '@VALUE' 
    EXEC(@sql) 

    SET @[email protected]@ROWCOUNT 
END 
GO 
+1

接受的答案爲[此相關的問題(http://stackoverflow.com/questions/2838490/table-name-as-variable)將有望激發。訪問上述答案中的[鏈接](http://www.sommarskog.se/dynamic_sql.html)以獲取更多詳細信息。 –

+0

你現在的程序有什麼問題? – FLICKER

+0

我不認爲'@@ ROWCOUNT'在這種情況下工作。您需要從動態SQL語句中執行並傳遞它。這就是爲什麼您應該使用'sp_executesql',因爲您可以輕鬆地將數據傳遞給_and_從動態SQL。 – Nicarus

回答

0

夫婦發出

首先,你不需要(表名)

SET @sql = 'DELETE FROM ' + @TABLE_NAME + etc. 

一般來說,你應該儘量包括適當的模式前綴

SET @sql = 'DELETE FROM dbo.' + @TABLE_NAME + etc. 

如果你的表名有特殊字符,可能應該用括號括起來

SET @sql = 'DELETE FROM dbo.[' + @TABLE_NAME + ']' + etc. 

由於@Value是一個字符串,所以在計算@SQL的值時,必須用單引號括起來。要插入一個單引號成一個字符串,你必須使用兩個單引號,像這樣逃吧:

SET @SQL = 'DELETE FROM dbo.[' + @TABLE_NAME + '] WHERE [' + @COLUMN_NAME + '] = '''' + @VALUE + '''' 

如果@VALUE本身包含一個單引號,這整個事情會打破,所以你需要逃脫以及

SET @SQL = 'DELETE FROM dbo.[' + @TABLE_NAME + '] WHERE [' + @COLUMN_NAME + '] = '''' + REPLACE(@VALUE,'''','''''') + '''' 

此外,@@ ROWCOUNT將不會從EXEC填充。如果您希望能夠閱讀@@ ROWCOUNT,使用sp_executesql的,而不是

EXEC sp_ExecuteSql @SQL 

最後,讓我editorialize的minute--

這種類型的存儲過程是不是一個好主意。我知道它看起來非常酷,因爲它很靈活,而且對於其他語言來說,這種想法通常很聰明,但在數據庫世界中,這種方法會導致問題,例如,存在安全問題(例如注入,以及您需要提升特權來調用sp_executeSql)以及預編譯/性能方面的問題(因爲SQL未提前知道,SQL Server將需要生成一個新的查詢計劃並且每次你調用這個函數時),因爲調用者可以爲表和列名提供任何值,所以你不知道這個刪除語句是否有效並且使用索引,或者它是否會導致巨大的性能問題,因爲表很大,列未編入索引。

正確的做法是有一系列適當的存儲過程,其中包含強類型輸入,這些輸入特定於每個需要根據條件刪除的數據用例。數據庫工程師不應該試圖讓事情變得靈活;你應該強迫人們思考他們將需要什麼,並且實現這個目標並且只有這個目標。這是確保人們遵守規則,保持R/I完好,有效使用索引等的唯一途徑。

是的,這可能看起來像是重複性和冗餘性工作,但是可以看到。如果您不喜歡額外的輸入,有些工具可用於生成CRUD操作的代碼。

+0

是否在@value處加上一個單引號確實取決於列數據類型(用於優化目的) – jyao

0

除了John Wu提供的一些信息,您不必擔心數據類型,@@ROWCOUNT可能不準確,如果您的表和事物上有triggers .....您可以解決這兩個問題通過轉換成nvarchar()使用OUTPUT子句與temp tableCOUNT()

所以只是爲了好玩在這裏是一種方法可以做到這一點:

CREATE PROCEDURE dbo.[ProcName] 
@TableName SYSNAME 
,@ColumnName SYSNAME 
,@Value NVARCHAR(MAX) 
,@RecordCount INT OUTPUT 
AS 

BEGIN 

    DECLARE @SQL NVARCHAR(1000) 

    SET @SQL = N'IF OBJECT_ID(''tempdb..#DeletedOutput'') IS NOT NULL 
     BEGIN 
      DROP TABLE #DeletedOutput 
     END 

    CREATE TABLE #DeletedOutput (
     ID INT IDENTITY(1,1) 
     ColumnValue NVARCHAR(MAX) 
    ) 

    DELETE FROM dbo.' + QUOTENAME(@TableName) + ' 
    OUTPUT deleted.' + QUOTENAME(@ColumnName) + ' INTO #DeletedOutput (ColumnValue) 
    WHERE CAST(' + QUOTENAME(@ColumnName) + ' AS NVARCHAR(MAX)) = ' + CHAR(39) + @Value + CHAR(39) + ' 

    SELECT @RecordCountOUT = COUNT(ID) FROM #DeletedOutput 

    IF OBJECT_ID(''tempdb..#DeletedOutput'') IS NOT NULL 
     BEGIN 
      DROP TABLE #DeletedOutput 
     END' 

    DECLARE @ParmDefinition NVARCHAR(200) = N'@RecordCountOUT INT OUTPUT' 

    EXECUTE sp_executesql @SQL, @ParmDefinition, @RecordCountOUT = @RecordCount OUTPUT 

END 

所以使用QOUTENAME將有助於對注入攻擊,但不是完美的。我用CHAR(39)而不是轉義序列的單引號的價值,因爲我覺得它更容易當在這一點上串建設....使用參數OUTPUTsp_executesql你仍然可以回到你的計數。

請記住,只是因爲你能在SQL並不總是意味着你應該什麼。

相關問題