应用程序服务器需要 SET IDENTITY_INSERT ON/OFF,但 ALTER 权限似乎很危险。建议?

SET IDENTITY_INSERT ON/OFF needed on application server, but ALTER permission seems dangerous. Suggestion?

我们正在构建一个多用户网络应用程序,他们需要为他们创建的 post 提供唯一的 postId。每个 post 都有 (userId, postId) 作为复合主键。

目前,postId是一个标识值,但由于需要支持一些需要postId按原样插入(不重新编号)的操作,我们决定使用SET IDENTITY_INSERT ON/OFF.

但是,我们的 DBA 告诉我们,这样的操作不应该被应用服务器使用,因为 ALTER 权限要求:

Permissions

User must own the table or have ALTER permission on the table.

https://msdn.microsoft.com/en-ca/library/ms188059.aspx

如果应用服务器被黑了,有 ALTER 权限似乎相当冒险。我们的 DBA 建议我们根本不要使用身份值,并在本地为每个用户生成一个唯一的 postId

SET IDENTITY_INSERT ON 可以全局保留吗?

如果它不能全局保留,是否避免标识值并使用本地生成的 postId(每个用户)和每个用户 max(postId)+1 是否有意义?如果可能的话,我们更愿意使用标识值,因为我们担心与自定义 postId 生成相关的可能死锁和性能问题。

从 SQL Server 2012 开始,您可以像在 Oracle 中一样使用 sequences。那些你可能会过得更好。首先,创建序列:

CREATE SEQUENCE mySeq AS LONG START WITH 1 INCREMENT BY 1;
GO

然后让 table 的主键默认为下一个序列值(而不是 IDENTITY 值):

CREATE TABLE myTable (
   myPK LONG PRIMARY KEY DEFAULT (NEXT VALUE FOR mySeq),
   myWhatever...
);

如果您不使用 INSERT 指定 PK 值,您将获得一个唯一的生成序列值。它与 IDENTITY 的行为基本相同。但是如果你想指定一个 PK 值你可以,只要你不违反主键的唯一性 - 但同样,这与 IDENTITYSET IDENTITY INSERT ON.[=17= 的行为相同]

听起来您需要评估您的数据库设计(如果可能的话)。 post 应该是一个固定的实体,并且作为单个主键的标识列应该足够了。在您的评论中,您提到您可能希望将 posts 从一个用户复制到另一个用户。如果要拆分 post 以便 user1 和 user2 可以独立控制自己的 post 版本,那么只需将所有 post 属性复制到新记录中即可(这会创建一个新的身份密钥),然后将新记录的用户属性从 User1 更新到 User2。但是,如果您希望用户共享相同的 post... 那么您应该通过用户与 post 的关系来做到这一点,以避免需要在您的 post table。换句话说,如果要将 user1 和 user2 分配给 post 的 相同版本 ,则创建一个包含两个字段的关系 table (Post ID,用户 ID)。这样您就可以通过在关系 table.

中插入一条新记录来简单地将用户添加到 post

示例:Post1 归用户 1 所有。Post2 归用户 1 和 2 所有。

 Post Table - Key (Post ID)
 (Post ID=1, PostText="This post is important!")
 (Post ID=2, PostText="This post is also important!")

 Users - Key (User ID)
 (User ID=1, Name="Bob")
 (User ID=2, Name="George")

 Post Users - Key (Post ID, User ID)
 (Post ID=1, User ID=1)
 (Post ID=2, User ID=1)
 (Post ID=2, User ID=2)

这让我有点担心:

"...需要支持一些需要 postId 按原样插入(不重新编号)的操作..."

我假设这种操作是例外情况,而不是常态?我也只能假设您将具有相同 Post ID 的相同 post 插入相同的 table 而不删除原始文件?目前还不清楚你为什么要这样做。

如果您将 post 分配给其他用户,我真的不明白为什么您需要担心更改 Post ID。除了 User ID 列中的值被它的声音改变之外,没有其他变化。除非您的意思是您可以拥有两个或更多具有相同 Post ID 和相同用户 ID 的 post。如果是,为什么?

回答您的问题:

  1. 不行,IDENTITY_INSERT不能全局设置。它是针对每个对象、每个会话的设置。
  2. 使用 MAX(PostID) + 1 没有意义。 IDENTITY(1, 1) 对 PostID 列不起作用有什么原因吗?如有必要,您可以重新播种。
  3. 不要使用应用程序生成的 UUID 作为键值。他们使查询慢得多。在最坏的情况下,如果您绝对想使用 UUID,请在 SQL 服务器中使用 NEWSEQUENTIALID() 。 UUID 不必要地膨胀 tables 和索引,除了 NEWSEQUENTIALID,是查询性能杀手。
  4. 你可以做的是有一个简单称为 ID 的主键列,然后是一个称为 Post ID 的代理键,如果它需要是非唯一的。这样,当你复制一个 post 时,原件的副本会得到一个新的 ID,但仍然保留原来的 Post ID,而不必担心对 PK 做任何不必要的事情。

但是,在不对应用程序或数据库进行任何更改的情况下,我建议使用作为存储过程所有者执行的存储过程(它还拥有 Posts table ) 仅在绝对必要时才将 IDENTITY_INSERT 设置为 ON。这是我刚刚写的一个例子:

create procedure dbo.sp_CopyPost
(
    @PostID     INT
    ,@UserID    INT
)
with execute as owner
as
begin
    set nocount on;
    set identity_insert dbo.Posts on;

    begin tran
        insert into dbo.Posts
        (
            PostID
            ,UserID
            --Whatever other columns
        )
        values
        (
            @PostID
            ,@UserID
            --Whatever other values
        )
    commit tran

    set identity_insert dbo.Posts off;

    select @@IDENTITY
end

根据您问题的当前措辞和您发表的评论,这将按照您的意愿进行。

如果您基本上需要在身份列中 "plug the gaps",您会在此处的 B 部分中找到 Microsoft 推荐的查询:

https://msdn.microsoft.com/en-us/library/ms186775.aspx