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

Is it possible to prevent UPDATE or DELETE statements being executed without a WHERE clause?

正如标题所暗示的那样,我们希望防止 - 至少通过手动界面 - 在特定 SQL 服务器(2008 或更高版本)数据库或 table 如果他们没有 WHERE 子句。

这可能吗?

如果您谈论的是 Microsoft SQL Server Management studio,那么您可以选择使用名为 SSMSBoost.

的插件

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

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

Erik Darling 在 Brent Ozar's blog 上发表了关于它的博客。他和我都不一定会推荐它,但如果情况足够绝望,嗯,这会起作用。

这是来自 link 的相关代码,以防万一,您知道,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 的两个子句将 "bad" 语句的阈值设置为受影响的 table 的 98%,Erik 指出这有点随意,但也是可配置的。

我认为你问错了问题。您想要阻止删除所有行(或一些高百分比的行)的删除,而不是阻止没有 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.