是否可以防止在没有WHERE子句的情况下执行UPDATE或DELETE语句?

时间:2018-11-27 13:51:55

标签: sql-server tsql sql-server-2008

正如标题所示,如果没有WHERE子句,则我们希望防止-至少通过手动接口-UPDATE和DELETE语句在特定的SQL Server(2008或更高版本)数据库或表上执行。

有可能吗?

3 个答案:

答案 0 :(得分:4)

可以。这并不理想,但是您可以创建表触发器来评估每个UPDATEDELETE以确定它们将影响多少行。

Erik Darling在Brent Ozar's blog上写了关于它的博客。无论是他还是我都不一定会推荐它,但是如果情况非常迫切,那将是可行的。

这里是链接中的相关代码,以防万一,Brent停业。他很轻浮。

CREATE OR ALTER TRIGGER dbo.UseAWhereClauseNextTime
ON dbo.TriggerTest
FOR UPDATE, DELETE
AS
    BEGIN

        DECLARE @rc INT = @@ROWCOUNT;
        DECLARE @table_rows INT = (   SELECT row_count
                                      FROM   sys.dm_db_partition_stats
                                      WHERE  object_id = OBJECT_ID('dbo.TriggerTest')
                                             AND index_id IN ( 0, 1 ));

        IF EXISTS ( SELECT 1 FROM Inserted ) AND EXISTS ( SELECT 1 FROM Deleted )
            BEGIN
                IF (( @rc * 100 ) / ( @table_rows )) >= 98
                    BEGIN
                        RAISERROR('USE A WHERE CLAUSE DUMMY', 0, 1) WITH NOWAIT;
                        ROLLBACK;
                    END;
            END;

        IF NOT EXISTS ( SELECT 1 FROM Inserted ) AND EXISTS ( SELECT 1 FROM Deleted )
            IF (   ( @table_rows = 0 )
                   OR (   @table_rows > 0
                          AND (( @rc * 100 ) / ( @table_rows + @rc )) >= 98 ))
                BEGIN
                    RAISERROR('USE A WHERE CLAUSE DUMMY', 0, 1) WITH NOWAIT;
                    ROLLBACK;
                END;

    END;

GO

带有(( @rc * 100 ) / ( @table_rows )) >= 98的两个子句将“不良”语句的阈值设置为受影响表的98%,Erik指出这是任意的,但也是可配置的。

答案 1 :(得分:2)

我认为您是在问错问题。您要防止删除删除所有行(或较高百分比的行)的删除,而不是阻止不具有WHERE子句的删除。

无论是恶意的,草率的,有意的还是无意的,我都可以轻松地取消对显式WHERE子句的任何分析,并且仍然删除所有行。

DELETE dbo.table WHERE 1 = 1;

DELETE dbo.table WHERE key_column > 0;

无论有意还是无意,我都可以解雇,不阅读或不小心按下Enter键,或者对插件提供的任何弹出式窗口进行错误的鼠标单击,仅因为插件向我发出警告,我就不会并不意味着它会阻止我做我尝试做的事情。

当然,如果您认为缺少WHERE子句意味着它将删除所有行,那么您可以错误地阻止仅影响一行的查询。即使没有找到WHERE子句,以下所有语句也应限制为一个受影响的行(或更多具有联系的行):

DELETE TOP (1) dbo.table;

;WITH RowToDelete AS
(
  SELECT key_column FROM dbo.table
    GROUP BY key_column
    HAVING (key_column = 1)
)
DELETE RowToDelete;

;WITH RowToDelete AS
(
  SELECT key_column, rn = ROW_NUMBER() OVER (ORDER BY key_column)
    FROM dbo.table
)
DELETE TOP (1) RowToDelete;

如果您想实际上防止意外删除所有行,则可以按照另一个答案中的建议在触发器中执行此操作,只要记住触发器就可以了,并请注意,这对truncatedroptransfer没有帮助。

答案 2 :(得分:0)

如果您在谈论 Microsoft SQL Server Management Studio ,那么您可以选择使用名为SSMSBoost的插件。

有了它,当您运行DELETE而没有WHERE时,您将始终得到警告。现在,我不确定100%是否可以为UPDATE进行配置,但可能是。