【问题标题】:CRUD 3 Inner Joined Tables Cleanly with SQL ServerCRUD 3 使用 SQL Server 干净地连接表
【发布时间】:2015-11-18 03:52:41
【问题描述】:

我正在使用 Identity (2.2) 开发一个 ASP.NET 项目。我试图放在一起的一件事是数据网格,其中用户和角色将被加载到一个可以运行 CRUD 命令的数据网格上。存储选择语句没有问题:

CREATE PROCEDURE [dbo].SelectUsersRoles
AS
    SET NOCOUNT ON;

    SELECT 
        p.Id, p.Email, p.PhoneNumber, p.LockoutEnabled, p.UserName, 
        p.AspNetUserRole, AspNetRoles.Name 
    FROM 
        AspNetUsers AS p 
    INNER JOIN 
        AspNetUserRoles ON p.Id = AspNetUserRoles.UserId 
    INNER JOIN 
        AspNetRoles ON AspNetUserRoles.RoleId = AspNetRoles.Id
GO

这选择了数据,我可以很好地加载网格。在reviewing this questionthis one 之后,我的想法是正确的更新代码应该是:

[编辑,运行存储过程更新如下,同样的问题]

CREATE PROCEDURE [dbo].UpdateUserRoles
(
    @Email nvarchar(256),
    @UserName nvarchar(256),
    @PhoneNumber nvarchar(MAX),
    @LockoutEnabled bit,
    @original_ID nvarchar(128)
)
AS
    SET NOCOUNT ON;

    UPDATE p
    SET p.Email = @Email, 
        p.Username = @UserName, 
        p.PhoneNumber = @PhoneNumber, 
        p.LockoutEnabled = @LockoutEnabled
    FROM [AspNetUsers] AS p
    INNER JOIN [AspNetUserRoles] AS r ON  p.ID = r.UserID
    INNER JOIN [AspNetRoles] AS b ON r.RoleID = b.ID
    WHERE p.ID = @original_ID
    GO

如果我尝试使用查询生成器,我注意到 VS'13 会自动向其中添加 CROSS JOIN 语句:[并且通常会降低可读性]

UPDATE p
SET p.Email = @Email, p.UserName = @UserName, 
    p.PhoneNumber = @PhoneNumber, p.LockoutEnabled = @LockoutEnabled
FROM            
    AspNetUsers AS p 
INNER JOIN
    AspNetUserRoles AS r ON p.Id = r.UserId 
INNER JOIN
    AspNetRoles AS b ON r.RoleId = b.Id 
CROSS JOIN
    p
WHERE
    (p.Id = @original_ID)

当我尝试运行查询时,这会不断抛出“p”是无效对象的错误。

我是否缺少一些简单的东西,或者将其拆分为连续的更新命令会更好?最终目标是更新用户信息(即电话号码)以及分配给用户的角色。

只是为了设置有问题的数据库,我认为您可以从 Visual Studio 启动任何新的 MVC/Web 表单模板,创建一些用户并设置一些角色。

【问题讨论】:

  • 您可以使用存储过程而不是将代码保留在应用程序中吗?它提供了更好的分离来获取数据层中的数据和应用层之外的数据。它还可以轻松解决这类问题。
  • @SeanLange 据我所知,我正在尝试使用存储过程。我没有通过尝试将 UPDATE 采石场添加到存储的 SELECT 采石场(有效)。我想我可以将它作为第一个存储形式起草……但这不会改变实际的采石场部分吗?
  • 那么第一个代码sn -p就是一个程序。第二个(更新语句)不是第一个过程的一部分,而且看起来根本不是一个过程。但如果没有上下文,就很难说。我非常怀疑工作室会在存储过程中为您的查询添加交叉连接。这似乎有点奇怪。
  • @SeanLange 一旦我粘贴代码,似乎采石场生成器正在执行此操作,然后单击“确定”或在执行中对其进行测试。如果我放弃该步骤并仅存储该过程,那么当我调用它时,我会得到相同的“p”是无效对象错误。我将尝试不重命名任何表,但我认为这是错误的?
  • 不要使用查询生成器。这就是问题的开始。然后,您需要删除要更新的列列表中的别名。 (更新 p 设置 Email = @Email...)

标签: sql asp.net sql-server asp.net-mvc


【解决方案1】:

请删除= 左侧UPDATE P SET 正下方的表别名。这是隐含的,是您的错误的原因。

UPDATE p SET
    Email = @Email, 
    Username = @UserName, 
    PhoneNumber = @PhoneNumber, 
    LockoutEnabled = @LockoutEnabled
FROM [AspNetUsers] AS p
    INNER JOIN [AspNetUserRoles] AS r
        ON  p.ID = r.UserID
    INNER JOIN [AspNetRoles] AS b
        ON r.RoleID = b.ID
WHERE p.ID = @original_ID

注意:连接没有理由 - 假设这些是因为您的查询生成器。你只需要在你的存储过程中。

UPDATE AspNetUsers SET
    Email = @Email, 
    Username = @UserName, 
    PhoneNumber = @PhoneNumber, 
    LockoutEnabled = @LockoutEnabled
WHERE ID = @original_ID

【讨论】:

  • 好吧,目前我的计划是尝试获取更新 [AspNetUsers] 的有效语法,但我的目标也是更新 [AspNetUserRoles].RoleID,尽管我正在显示 [AspNetRoles] .名称
  • 您需要第二条 UPDATE 语句来更新 RoleID,因为您无法在同一查询中更新 2 个单独的表。您可以考虑将 2 个 UPDATE 语句包装在一个事务中,因为它们与正在执行的相同“更新”有关,我假设您不希望有 1 个更新保存,但另一个由于系统错误而不保存(死锁受害者、网络等)。但通常你只是插入/删除 AspNetUserRoles 表,而不是尝试更新它 - 似乎总是不是一个好主意。
  • 您可以指出为什么不应该更新 UserRoles 表的任何特定原因或来源?看来我必须将其扩展为两个步骤,首先插入新值然后删除旧值?
  • 主键是 UserRoles 中由 UserId 和 RoleId 组成的复合键。出于多种原因,我一直试图避免直接更新表主键的任何部分。其中一个是实用的,因为它是您的聚集索引的一部分,因此,您正在重新组织表中的底层页面,增加页面级碎片的级别,随着时间的推移,大数据会减慢这些非常常见的查询。一篇好文章:dba.stackexchange.com/questions/42276/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-28
  • 2017-06-28
  • 2011-03-05
相关资源
最近更新 更多