2009-07-16 44 views
0

我有3張桌子。如何更新兩個在SQL Server中具有參考表的表?

1. Users 4 Cols 
UserID - UserName - RealName - Flags 

2. UsersGroups 2 Cols 
UserID - GroupID 

3. Groups 3 Cols 
GroupID - GroupName - Flags 

我想要做的是選擇一個特定的UserName即USERA並更新Flags列。 但我也想更新組表中的標誌列爲相同的值。

我有2個表之間的唯一連接是UsersGroups表。

這樣做的最好方法是什麼?

+0

內連接將花費更多的時間,它會影響應用程序和查詢 – Ahmy 2009-07-16 14:31:41

+0

如果表索引適當,不應顯著的運行時間。 – 2009-07-16 14:34:35

+0

沒有人提及他們解決方案中數據完整性的危險,要小心。 – 2009-07-16 14:35:35

回答

0

UPDATE語句只能更新單個表的記錄。

UPDATE Users SET Flags = @Flags WHERE UserID = @UserID 

UPDATE Groups SET Flags = @Flags 
FROM Groups 
INNER JOIN UsersGroups ON UsersGroups.GroupID = Groups.GroupID 
WHERE UsersGroups.UserID = @UserID 
+0

當兩次更新之間出現錯誤時會發生什麼? (不好意思挑你,但你在頂部,這是針對每個人相同的答案太) – 2009-07-16 14:37:30

0
UPDATE dbo.Users 
SET Flags = @var 
WHERE UserName = 'UserA' 

UPDATE g 
SET g.Flags = @var 
FROM dbo.Users u INNER JOIN dbo.UsersGroups ug ON u.UserID = ug.UserID 
INNER JOIN dbo.Groups g ON g.GroupID = ug.GroupID 
WHERE u.UserName = 'UserA' 
+0

如果我做的: SELECT * FROM dbo.UsersüINNER JOIN dbo.UsersGroups微克ON u.UserID = UG .UserID INNER JOIN dbo.Groups g還g.GroupID = ug.GroupID WHERE u.UserName = '用戶A' 然後我得到正確的信息。 但是,如果我嘗試更新,我收到錯誤: 消息209,級別16,狀態1,行2 歧義列名稱'標誌'。 – Alan 2009-07-16 14:34:21

0

對於選擇特定的用戶使用下面的:

select UserName from Users where UserID = <USERA> 

而在兩個2個表用戶和組updatting標誌列執行以下操作:

update Users set Flags = <Your Flag> where UserID = <USERA> 
update Groups set Flags = <Your Flag> where GroupID in (select GroupID from UsersGroups where UserID = <USERA>) 

希望這些有用

0

這應該確實是保持數據完整性的原子單位!當更新多個表,他們必須保持同步使用BEGIN和COMMIT/ROLLBACK TRAN或者,如果您有SQL Server 2008中使用新的TRY CATCH語法

BEGIN TRAN 
    BEGIN TRY 
    UPDATE dbo.Users 
    SET Flags = @var 
    WHERE UserName = 'UserA' 

    UPDATE dbo.Groups 
    SET Flags = @var 
    FROM dbo.Users u INNER JOIN dbo.UsersGroups ug ON u.UserID = ug.UserID 
     INNER JOIN dbo.Groups g ON g.GroupID = ug.GroupID 
    WHERE u.UserName = 'UserA' 
END TRY 
BEGIN CATCH 
    IF @@TRANCOUNT > 0 
    ROLLBACK TRANSACTION; 

END CATCH 
IF @@TRANCOUNT > 0 
    COMMIT TRAN 

彷彿你的數據去歸一化性能的一邊那麼這是你最好的解決方案。如果情況並非如此,我建議你排除其中一列。 (等待典型的「這不是我的模式我繼承了它..遺產等等......他他:))

PS的事務塊裏面的代碼是公然複製/克里斯的回答貼上。

編輯

有很多的關於不明確的列名的意見,但沒有什麼錯在這裏TSQL這是一個完整的DML和查詢我MSSMS已經測試:

IF NOT EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE 

TABLE_NAME='Users') 
BEGIN 
    CREATE TABLE Users 
    (
     UserID  INT IDENTITY(1,1) PRIMARY KEY, 
     UserName NVARCHAR(32) NOT NULL, 
     RealName NVARCHAR(64) NOT NULL, 
     Flags  NVARCHAR(16) NOT NULL 
    ) 
END 
GO 

IF NOT EXISTS (SELECT ix.name FROM sys.indexes ix WHERE ix.name='IX_Users_UserName') 
BEGIN 
    CREATE UNIQUE INDEX IX_Users_UserName ON Users(UserName) 
END 
GO 

IF NOT EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='Groups') 
BEGIN 
    CREATE TABLE Groups 
    (
     GroupID  INT IDENTITY(1,1) PRIMARY KEY, 
     GroupName NVARCHAR(32) NOT NULL, 
     Flags  NVARCHAR(16) NOT NULL 
    ) 
END 
GO 

IF NOT EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='UsersGroups') 
BEGIN 
    CREATE TABLE UsersGroups 
    (
     UserID INT NOT NULL, 
     GroupID INT NOT NULL, 
     CONSTRAINT PK_UsersGroups PRIMARY KEY CLUSTERED (UserID, GroupID), 
     CONSTRAINT FK_UsersGroups_UserID FOREIGN KEY (UserID) REFERENCES Users(UserID), 
     CONSTRAINT FK_UsersGroups_GroupID FOREIGN KEY (GroupID) REFERENCES Groups(GroupID), 
    ) 
END 
GO 

DECLARE @count INT = (SELECT COUNT(*) FROM Users) 
IF @count = 0 
BEGIN 
    INSERT INTO Users(UserName, RealName, Flags) 
    SELECT 'USERA', 'User A', 'Flags A' 
    UNION ALL 
    SELECT 'USERB', 'User B', 'Flags B' 
END 

SELECT @count = (SELECT COUNT(*) FROM Groups) 
IF @count = 0 
BEGIN 
    INSERT INTO Groups(GroupName, Flags) 
    SELECT 'Group A', 'Flags A' 
    UNION ALL 
    SELECT 'Group B', 'Flags B' 
END 

SELECT @count = (SELECT COUNT(*) FROM UsersGroups) 
IF @count = 0 
BEGIN 
    INSERT INTO UsersGroups(GroupID, UserID) 
    SELECT 1, 1 
    UNION ALL 
    SELECT 2, 2 
END 
GO 

BEGIN TRAN 
    BEGIN TRY 
     DECLARE @var NVARCHAR(16) 
     SET @var = 'New Flags A' 

     UPDATE dbo.Users 
     SET Flags = @var 
     WHERE UserName = 'UserA' 

     UPDATE dbo.Groups 
     SET Flags = @var 
     FROM dbo.Users u INNER JOIN dbo.UsersGroups ug ON u.UserID = ug.UserID 
      INNER JOIN dbo.Groups g ON g.GroupID = ug.GroupID 
     WHERE u.UserName = 'UserA' 

    END TRY 
    BEGIN CATCH 

     IF @@TRANCOUNT > 0 
     ROLLBACK TRANSACTION; 

    END CATCH 

IF @@TRANCOUNT > 0 
    COMMIT TRAN 

SELECT Flags FROM Users 

SELECT Flags FROM Groups 
2

這應該做我t:

Create Proc spUpdateUsersFlag(@UserName as Varchar(32), @Flags as int) 
AS 
Declare @UserID as int 

BEGIN Transaction 
BEGIN TRY 
    SELECT @UserID = UserID 
     From Users 
     Where UserName = @UserName 
    UPDATE Users 
     SET Flags = @Flags 
     WHERE UserID = @UserID 
    UPDATE Groups 
     SET Flags = @Flags 
     FROM Groups G INNER JOIN UserGroups UG ON G.GroupId = UG.GroupID 
     WHERE UG.UserID = @UserID 
END TRY 
BEGIN CATCH 
    DECLARE @ErrorMessage NVARCHAR(4000), @ErrorSeverity INT 
    -- Assign variables to error-handling functions that 
    -- capture information for RAISERROR. 
    SELECT @ErrorMessage = ERROR_MESSAGE(), @ErrorSeverity = ERROR_SEVERITY() 
    -- Rollback the failed transaction 
    ROLLBACK; 
    -- Raise an error: with the original error information. 
    RAISERROR(@ErrorMessage, @ErrorSeverity, 1); 
END CATCH 
COMMIT Transaction; 

編輯:更正了第二個查詢中的錯誤。