SQL授予权限一次仅更新一行

时间:2018-10-22 12:54:20

标签: sql-server tsql

是否可以将SQL Server权限设置为特定用户,以允许一次仅更新一行? 如果答案是yes,那如何实现?

例如如果您有这样的事情:

use MyDB
go 

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

如果此选择将返回多个要更新的记录,则不允许这样做(...或仅更新第一个),否则,如果仅更新一个记录,则允许它。

4 个答案:

答案 0 :(得分:2)

如果您需要临时解决方案,则触发器是执行此操作的简便方法。
不过,这将阻止任何人一次更新多个记录。
如果您需要永久的解决方案,我会回答@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

答案 1 :(得分:2)

您可以为表使用UPDATE触发器。向用户授予该表的UPDATE权限,并定义一个UPDATE触发器来检查用户名和受影响的记录数:

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

答案 2 :(得分:1)

对此有两个答案。一种是通过触发器,另一种是通过限制权限并使用存储过程。我个人建议使用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;

当然,用户可以顺序更新多个行;使用多个语句(可能使用CURSOR或Dynamic SQL)。但是,如果您担心这一点,则应该提供一个应用程序界面而不是该人的登录名,以便他们可以直接运行查询。

答案 3 :(得分:0)

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