Sql server SQL授予每次只更新一行的权限

Sql server SQL授予每次只更新一行的权限,sql-server,tsql,Sql Server,Tsql,是否可以将SQL server权限设置为特定用户,以便一次只更新一行? 如果答案是“是”,那么如何实现呢 例如,如果你有这样的东西: use MyDB go update t1 set t1.something = 'xxx' --select * from table t1 如果此选择将返回一条以上要更新的记录,则不允许(…或只更新第一条记录),否则,如果只更新一条记录,则允许更新。SQL Server没有在安全/任务级别执行此操作的方法。但是,我相信您可以通过存储过程来实现这一点。

是否可以将SQL server权限设置为特定用户,以便一次只更新一行? 如果答案是“是”,那么如何实现呢

例如,如果你有这样的东西:

use MyDB
go 

update t1 set t1.something = 'xxx' 
--select *
from table t1 

如果此选择将返回一条以上要更新的记录,则不允许(…或只更新第一条记录),否则,如果只更新一条记录,则允许更新。

SQL Server没有在安全/任务级别执行此操作的方法。但是,我相信您可以通过存储过程来实现这一点。因此,您可以授予用户通过存储过程执行工作的能力。一旦存储过程中的工作“完成”,就可以检查
@@rowcount
。如果这超过了您的阈值(我将其作为参数进行互换),则回滚并引发一个用户错误(5000)

添加limit参数的优点是可以在运行时从应用层进行配置

create procedure dbo.LimitRecordsChanged
  @NumChangesAllowed int = 1
  --other params
as  
begin

  -- parameter sniffing 
  declare @__NumChangesAllowed int = @NumChangesAllowed;

  begin tran;

  -- do your work here

  if @@rowcount > @__NumChangesAllowed
    rollback;

  commit tran;
end

如果需要临时解决方案,触发器是一种简单的方法。
但这将阻止任何人一次更新多条记录。
如果你需要一个永久的解决方案,我会选择@Larnu

create trigger U_YourTableName on dbo.YourTableName
for update
as
begin
     set nocount on

     if (select count(1) from inserted) > 1
     begin
           ;THROW 99001, 'your only allowed one update at a time', 1
     end
end

这个问题有几个答案。一种是通过触发器,另一种是通过限制权限和使用存储过程。就我个人而言,我建议SP

下面的示例应该解释一切:

--Create a sample table and data
CREATE TABLE SomeTable (ID int IDENTITY(1,1),
                        SomeString varchar(100));
GO
INSERT INTO SomeTable (SomeString)
VALUES ('adsfasdfad'),
       ('sdafkjsdlf'),
       ('kjlsdahfsldjkhflds');
GO
SELECT *
FROM SomeTable;
GO
--Create a test user, give UPDATE permissions and test
CREATE USER TestUser WITHOUT LOGIN;
GO
GRANT UPDATE ON SomeTable TO TestUser;
GO
EXECUTE AS USER = 'TestUser';

UPDATE SomeTable
SET SomeString = 'abcdefg'; --this will update every row
GO
REVERT;
GO
SELECT *
FROM SomeTable;
GO
--Trigger technique (not what I recommend)
CREATE TRIGGER LimitToOne ON SomeTable
INSTEAD OF UPDATE
AS 
    IF (SELECT COUNT(*) FROM inserted) > 1 BEGIN
        --Should raise an error here, but soing a PRINT for speed
        PRINT 'Can''t update more than one row at a time';
    END ELSE BEGIN
        UPDATE ST
        SET SomeString = i.SomeString
        FROM inserted i
             JOIN SomeTable ST On i.ID = ST.ID
    END
GO

UPDATE SomeTable
SET SomeString = '123456'; --this won't work

SELECT *
FROM SomeTable;

UPDATE SomeTable
SET SomeString = '123456'
WHERE ID = 1; --this will work

SELECT *
FROM SomeTable;
GO

--Allow use of SP's only (the better solution, in my opinion)
--Cleanup previous example
DROP TRIGGER LimitToOne;
REVOKE UPDATE ON SomeTable TO TestUser;
GO
--Check we can't update
EXECUTE AS USER = 'TestUser';

UPDATE SomeTable
SET SomeString = 'abcdefg'; --this will fail, as we revoked the permissions
GO
REVERT;
GO
SELECT *
FROM SomeTable;
GO
--Create the SP
CREATE PROC Update_SomeTable @ID int, @String varchar(100) AS

    UPDATE SomeTable
    SET SomeString = @String
    WHERE ID = @ID;
GO
--Give Permissions
GRANT EXECUTE ON Update_SomeTable TO TestUser;
GO
--Test
EXECUTE AS USER = 'TestUser';

EXEC Update_SomeTable 3,'Did this work?'; --Op is forced to only supply one value
GO
REVERT;
GO
SELECT *
FROM SomeTable;

GO
--Clean up
DROP PROC Update_SomeTable;
DROP TABLE SomeTable;
DROP USER TestUser;

当然,用户可以依次更新多行;通过使用多个语句(可能通过使用
游标
或动态SQL)。但是,如果您对此感到担心,您应该提供一个应用程序界面,而不是登录到用户,这样用户就可以直接运行查询。

您可以为表使用更新触发器。授予用户对表的更新权限,并定义检查用户名和受影响记录数的更新触发器:

CREATE TRIGGER dbo.u_yourtable ON dbo.yourtable
FOR UPDATE AS

    IF (ROWCOUNT_BIG() > 1) AND (SUSER_SNAME() IN (N'yourdomain\specificuser'))
    BEGIN
        RAISERROR ('You are not allowed to update more than one record at a time.', 16, 1);
        ROLLBACK TRAN;
    END

GO

不。不能那样做。有什么特别的原因要这样做吗?措辞拙劣的问题-如果您试图说您只想在有多行时更新,您可以通过不让他们直接更新表来执行子查询,而是强制他们通过存储过程执行数据访问,您可以将
TOP 1
设置行数
或任何其他机制放在其中以限制它…@JamesCooke否,完全相反,如果更新将影响多行,我不希望执行更新。我不想修改查询,我想拥有权限(如果可行的话)来阻止编写此类查询的用户这样做。@AaronBertrand是的,这会起作用,但是,我只是想知道通过调整分配给用户的实际权限是否可行。如果没有其他办法,我会用它作为备用解决方案。好主意/答案。虽然我倾向于尽可能避免触发器,因为我发现从维护的角度来看,触发器不太有利,而且还会带来性能影响。@pimbrouwers我同意性能影响。关于维护,他们也可以有优势,当然。我的意思是,随着系统的老化或新开发人员的加入,总是会出现那种程度的“健忘”。触发因素并不总是立即显现。我还更喜欢特定的存储过程方法,因为它允许在运行时配置行限制。