存储过程执行时是否有“你确定”的选项? :)

7

我有一个存储过程,它会删除大量的记录,数十万条。虽然不会从应用程序中运行,但我仍然担心我的客户之一会意外运行它(由于他们的“好奇心”,我之前就遇到过问题):D

是的,有备份等等,但我在想……不要吓唬他们……有没有一种方法在执行之前询问用户“你确定?”? :) 谢谢


11
为什么不为你的存储过程设置权限呢?这样,只有授权用户才能运行它。 - Buhake Sindi
@The Elite Gentleman:我并不是说授权用户有时可能希望感觉像人类而不是机器。这种情况表明,一些开发人员并不介意将用户视为凡人。 :) - Andriy M
8个回答

7
我想你可以设置一个名为“确认”的参数,要求传递特定的字符串(例如“I know what I am doing”),如果未设置或设置不正确,则仅返回过程而不执行主代码。这不完全符合您的要求,但这是一种选择。
例如 - (未经测试且可能是可怕的语法)
CREATE PROCEDURE dbo.mySproc (
@Confirmation Varchar(100)
) AS
BEGIN
    if(@Confirmation <> 'I know what I am doing')
    BEGIN
        return;
    END
    DELETE from table_name where condition
END

它可能能够运行,但我认为它很快就会变成一个“复制粘贴并忽略参数”的东西。 - Jhonny D. Cano -Leftware-
5
同意,但如果他们能够直接访问存储过程,那么就没有任何方法阻止他们绕过任何措施。更好的做法是在整个过程中添加适当的访问/执行安全性控制。 - ZombieSheep

6
简而言之,不行。
理论认为,任何具有查找和运行存储过程权限的人都应该被允许。更好的做法是限制权限,以便那些过于好奇的人没有运行此程序的权限。
另一个不太安全的选择是要求预定义的秘密作为参数传递 - 当然,他们可以通过脚本来查找秘密存储过程...
当然,另一个观点是:如果它不能被调用,为什么要包含它?毕竟,当您进行管理类型任务时,可以将语句脚本化为文件,并将其保护在自己的计算机上。

4
采用分层的多管齐下方法:
1)控制执行安全性,例如:
GRANT EXECUTE ON [dbo].[yourProcedure] TO [userxyz]

2) 使用一个非常描述性/可怕的过程名称,比如

CREATE PROCEDURE Will_Delete_All_Your_Data ...

3) 在存储过程的开头放置一个大而引人注目的注释

--NOTE, will delete all of your data--
--NOTE, will delete all of your data--
--NOTE, will delete all of your data--
--NOTE, will delete all of your data--
--NOTE, will delete all of your data--

4) 让用户传递一个混淆的特殊访问码:

CREATE PROCEDURE Will_Delete_All_Your_Data
(
    @SpecialCode varchar(30)
)

IF @SpecialCode!=CHAR(83)+CHAR(112)+CHAR(101)+CHAR(99)+CHAR(105)+CHAR(97)+CHAR(108)+CHAR(67)+CHAR(111)+CHAR(100)+CHAR(101)
BEGIN
    RETURN 999
END
...

请注意,特殊代码必须是'SpecialCode'或者当达到RETURN 999时。


2
您可以在存储过程中添加一个 @reallyReallyReallyDelete 参数,作为安全措施:如果将其设置为 YesYesYes,则会实际提交事务。

1

这里有另一种方法,我认为适用于用户直接调用过程而不是从应用程序中调用的特定情况。

我必须说,与大多数其他建议相比,它对用户提出的问题较少,但对开发人员提出的问题更多(也许是不成比例的)。您可以决定它是否适合您。

无论如何,下面开始。

首先,您需要创建一个特殊的表CriticalCalls,以注册对关键过程的调用。该表的结构如下:

SPID int,
ProcName sysname,
CallTime datetime

基本上,这个想法是一个关键的SP应该被调用两次:首先它注册它的调用并告知用户在一定时间间隔内重复调用以确认他们的意图,第二次调用,如果按照要求进行,它实际上会继续完成它的任务。
因此,每个关键过程的起始部分都将具有这种逻辑:
IF NOT EXISTS (
  SELECT *
  FROM CriticalCalls
  WHERE SPID = @@SPID AND ProcName = @ThisProcName
    AND GETDATE() - CallTime BETWEEN @LowerCallTimeLimit AND @UpperCallTimeLimit
    /* the actual test for the time interval might be somewhat different */
) BEGIN
  ...  /* upsert CriticalCalls with the current time stamp */
  PRINT 'To proceed, please call this procedure again within...';
  RETURN;
END;

DELETE FROM CriticalCalls WHERE SPID = @@SPID AND ProcName = @ThisProcName;

...  /* proceed with your critical task */

其实,我认为最好使用一个专门的SP(下面命名为CheckCriticalCalls)来处理所有与CriticalCalls相关的操作,包括所有必要的修改。 CheckCriticalCalls将接收要检查的过程名称,并返回一种标志,显示指定的过程是否应执行其真正的操作。
因此,它可能看起来像这样:
EXECUTE @result = CheckCriticalCalls 'ThisProcedureName';
IF @result = -1 BEGIN
  PRINT 'Call me again';
  RETURN;
END;

...  /* go on with the task */

设置区间下限的想法仅是为了防止用户在一个批处理中执行两个相同的EXECUTE...行,从而自动调用关键过程两次。 当然,上限是必要的,以确保用户确认他们最近的意图执行关键操作;如果CriticalCalls中的现有记录实际上是留给具有相同SPID的过去会话,则防止执行。
因此,对我来说,1-2秒到半分钟的时间间隔似乎非常自然。 您可以选择不同的数字。

1
您可以使用名为@UserKnowsWhatTheyAreDoing的位输入,在执行之前检查其是否为真。如果为假,请打印友好的消息并从过程中优雅地返回。

1
该过程可能需要一个特定值的参数,例如'是的,我知道我在做什么'。或者它可能会在一个特殊表格中查找一行具有类似确认和最近时间戳的内容。

0
你真的需要从数据库中彻底删除它们吗?如果我有足够的空间,我会在我的表中放置一个“已删除”标志和一个最后更新的列。这样,如果记录被意外删除,至少我通常可以追踪并相对容易地恢复它。这只是一个想法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接