SQL Server更新组按多个表中的名称?

时间:2013-07-28 11:44:52

标签: sql sql-server tsql sql-server-2005 sql-update

我有两个传说配方和参数。配方表包含具有相同配方名称的多个版本。参数表包含一个配方的多个参数名称。如何按配方名称和参数名称将constantguid更新为相同的配方和相同的参数组?

CREATE TABLE [dbo].[Recipe](
    [VersionGUID] [varchar](36) NULL,
    [Name] [nvarchar](50) NULL
) ON [PRIMARY]

GO

CREATE TABLE [dbo].[Parameter](
    [ParameterGUID] [varchar](36) NULL,
    [VersionGUID] [varchar](36) NULL,
    [ParameterName] [nvarchar](50) NULL,
    [ConstantGUID] [varchar](50) NULL
) ON [PRIMARY]

GO


-- Add 5 rows to [dbo].[Parameter]
INSERT INTO [dbo].[Parameter] ([ParameterGUID], [VersionGUID], [ParameterName], [ConstantGUID]) VALUES ('1B00E5ED-25A5-4FEA-AE73-14CDC7871951', 'AB00E5ED-25A5-4FEA-AE73-14CDC787195A', N'Parameter1', '26976642-12B6-462A-982B-74448DDA33B6')
INSERT INTO [dbo].[Parameter] ([ParameterGUID], [VersionGUID], [ParameterName], [ConstantGUID]) VALUES ('5AD70E77-E377-4661-A525-6711A1992217', '8B00E5ED-25A5-4FEA-AE73-14CDC7871953', N'Parameter1', '55D724C5-51A6-48B7-9161-2364F004BB7F')
INSERT INTO [dbo].[Parameter] ([ParameterGUID], [VersionGUID], [ParameterName], [ConstantGUID]) VALUES ('A0142B6A-52F6-4A49-ADBA-DC5D4C2BEF7A', '20142B6A-52F6-4A49-ADBA-DC5D4C2BEF71', N'Parameter2', 'B5903A3D-B606-49DD-A75C-A1B29740EBEB')
INSERT INTO [dbo].[Parameter] ([ParameterGUID], [VersionGUID], [ParameterName], [ConstantGUID]) VALUES ('A96113F9-1046-4E35-B320-BE3126D522CF', '20142B6A-52F6-4A49-ADBA-DC5D4C2BEF71', N'Parameter1', '108197E8-47CA-477C-B97F-4BF0321C1D91')
INSERT INTO [dbo].[Parameter] ([ParameterGUID], [VersionGUID], [ParameterName], [ConstantGUID]) VALUES ('AB00E5ED-25A5-4FEA-AE73-14CDC787195A', '8B00E5ED-25A5-4FEA-AE73-14CDC7871953', N'Parameter2', 'B3AC6268-6257-4E3B-B0C9-4418E4A09E40')

-- Add 3 rows to [dbo].[Recipe] with non-unique comparison key
SET ROWCOUNT 1
INSERT INTO [dbo].[Recipe] ([VersionGUID], [Name]) VALUES ('20142B6A-52F6-4A49-ADBA-DC5D4C2BEF71', N'Recipe1')
INSERT INTO [dbo].[Recipe] ([VersionGUID], [Name]) VALUES ('8B00E5ED-25A5-4FEA-AE73-14CDC7871953', N'Recipe1')
INSERT INTO [dbo].[Recipe] ([VersionGUID], [Name]) VALUES ('AB00E5ED-25A5-4FEA-AE73-14CDC787195A', N'Recipe2')
SET ROWCOUNT 0

如果您运行以下查询,我希望108197E8-47CA-477C-B97F-4BF0321C1D9155D724C5-51A6-48B7-9161-2364F004BB7F相同。 B3AC6268-6257-4E3B-B0C9-4418E4A09E40B5903A3D-B606-49DD-A75C-A1B29740EBEB相同,等等。我有5密耳的记录要更新。

select name+parametername as name_parametername,name as RecipeName,parametername,constantguid,Parameter.parameterguid
from dbo.Recipe,dbo.Parameter
where dbo.Recipe.versionguid=dbo.Parameter.versionguid
order by name_parametername

提前致谢。

2 个答案:

答案 0 :(得分:0)

注1:在更新生产数据库之前,请进行备份。恢复此备份并进行一些测试,首先是几行,然后是更多行。

注意2:检查是否有足够的可用空间用于数据库日志文件。

注意3:如果取消注释EXEC sp_trace_generateevent行,然后创建跟踪以捕获UserConfigurable0事件,您可以看到使用新ID GUID更新了多少行(服务器跟踪的开销比SQL Profiler少)迹线)。

注4:每次只执行一步。

注意5:在更新生产数据库之前,请进行另一次备份。本说明是故意重复的。

-- dbo.NewGUIDs is used to generate the new ConstantGUIDs 
-- AnotherDB.dbo.NewGUIDs: this table could be created in another DB to minimize DB log increase for current DB
-- Note: This script assumes that Parameter.ParameterGUID column has an unique index (ex. is PRIMARY KEY or UNIQUE [KEY])

-- Step 1: It creates the NewGUIDs table, it insert current ParameterGUID guids and 
-- it generates some kind of group ID (column Rnk) for every (Receipt.Name and Parameter.ParameterName) pair
    /*
    CREATE AnotherDB;
    GO
    USE AnotherDB;
    GO
    */
    CREATE TABLE /*AnotherDB.*/dbo.NewGUIDs( 
        ParameterGUID UNIQUEIDENTIFIER PRIMARY KEY,
        Rnk INT NOT NULL,
        NewConstantGUID UNIQUEIDENTIFIER NULL
    );

    INSERT INTO /*AnotherDB.*/dbo.NewGUIDs(ParameterGUID,Rnk) -- Ar  INSERT INTO AnotherDB.dbo.NewGUIDs
    SELECT  p.ParameterGUID,
            -- r.Name,p.ParameterName,
            DENSE_RANK() OVER(ORDER BY r.Name,p.ParameterName) AS Rnk
    FROM    dbo.Recipe r JOIN dbo.Parameter p ON r.VersionGUID=p.VersionGUID;
    GO

    -- This index is useful for the next queries
    CREATE INDEX IN_NewGUIDs_Rnk
    ON /*AnotherDB.*/dbo.NewGUIDs(Rnk);
    GO
-- End of Step 1

-- Step 2: It generates the new GUIDs for ConstantGUID column
    DECLARE @GUIDs TABLE(
        Rnk INT PRIMARY KEY,
        NewGUID UNIQUEIDENTIFIER DEFAULT NEWSEQUENTIALID() -- or NEWID() 
    );
    INSERT  @GUIDs(Rnk)
    SELECT  Rnk
    FROM    (SELECT DISTINCT Rnk FROM /*AnotherDB.*/dbo.NewGUIDs)a;

    UPDATE  x 
    SET     NewConstantGUID=y.NewGUID
    FROM    /*AnotherDB.*/dbo.NewGUIDs x 
    INNER JOIN @GUIDs y ON x.Rnk=y.Rnk;

    -- Check: this query should return 0 rows
    SELECT * FROM dbo.NewGUIDs n WHERE NewConstantGUID IS NULL;
    GO
-- End of Step 2

-- Step 3: It adds a new column (IsUpdated) to dbo.Parameter. 
-- This column will be updated (SET IsUpdated=1) when ConstantGUID is updated with the new value
ALTER TABLE dbo.Parameter
ADD IsUpdated BIT NULL;
GO
CREATE INDEX IN_Parameter_IsUpdated
ON dbo.Parameter(IsUpdated);
GO
-- End of Step 3

-- Step 4 (final step): It updates ConstantGUID with new values from /*AnotherDB.*/dbo.NewGUIDs
SET XACT_ABORT ON;
DECLARE @AffectedRowsCount INT,
    @TotalAffectedRowsCount INT,
    @EventClass INT, 
    @UserInfo NVARCHAR(128);

SELECT @EventClass=82,
    @AffectedRowsCount=0,
    @TotalAffectedRowsCount=0;

WHILE 1=1
BEGIN
    BEGIN TRANSACTION;
    UPDATE  TOP(4000) p -- TOP(4000) to prevent lock escalation; See http://msdn.microsoft.com/en-us/library/ms184286(v=sql.105).aspx
    SET     ConstantGUID=n.NewConstantGUID,
            IsUpdated=1 -- It marks current so I know that it was updated and I don't have to update the same row again
    FROM    dbo.Parameter p INNER JOIN /*AnotherDB.*/dbo.NewGUIDs n ON p.ParameterGUID=n.ParameterGUID
    WHERE   p.IsUpdated IS NULL -- Wasn't updated
    SET @AffectedRowsCount=@@ROWCOUNT;
    SET @TotalAffectedRowsCount=@TotalAffectedRowsCount+@AffectedRowsCount;
    IF @AffectedRowsCount=0
    BEGIN
        SET @UserInfo='Finish! > '+CONVERT(VARCHAR(11),@TotalAffectedRowsCount);
        -- EXEC sp_trace_generateevent @EventClass,@UserInfo;
        BREAK;
    END
    ELSE
    BEGIN
        SET @UserInfo='Processing > '+CONVERT(VARCHAR(11),@TotalAffectedRowsCount); 
        -- It generates a custom event (82). You may use SQL Profiler to catch this event (UserConfigurable0)
        -- EXEC sp_trace_generateevent @EventClass,@UserInfo;
    END

    COMMIT;
END;
GO
-- End of Step 4


-- Cleanup
/*
ALTER TABLE dbo.Parameter
DROP COLUMN IsUpdated;
GO

DROP INDEX IN_Parameter_IsUpdated
ON dbo.Parameter

DROP TABLE /*AnotherDB.*/dbo.NewGUIDs
-- DROP DATABASE AnotherDB
*/

答案 1 :(得分:0)

谢谢波格丹。你是一个很好的帮助。

我在他现有的Parameter表中添加了一个UniqueRcipeParameterName。由于配方名称和参数名称的组合在所有版本中都是相同的。

PRINT N'Updating Recipe Parameters ConstantGUID...'

ALTER TABLE Parameter
ADD UniqueRecipeParameter NVARCHAR(600) NULL;
GO

ALTER TABLE Parameter
ADD IsPhase bit NULL ;
GO

UPDATE  Parameters
SET     UniqueRecipeParameter = Recipe.Name + '-' + Parameter.Name,
        IsPhase = 0
FROM    Recipe,
        Parameter
WHERE   Recipe.VersionGUID = Parameter.VersionGUID

DECLARE @GUIDs TABLE
    (
      --Assuming Recipe Name and Parameter Name will not exit 450 characters. The maximum key length is 900 bytes which is 450
      UniqueRecipeParameter NVARCHAR(450) PRIMARY KEY ,
      NewGUID UNIQUEIDENTIFIER DEFAULT NEWID()
    ) ;

INSERT  @GUIDs
        ( UniqueRecipeParameter
        )
        SELECT  UniqueRecipeParameter
        FROM    ( SELECT DISTINCT
                            UniqueRecipeParameter
                  FROM      Parameter
                  WHERE     Parameter.IsPhase = 0  
                ) a ;

UPDATE  x
SET     ConstantGUID = y.NewGUID
FROM    dbo.Parameter x
        INNER JOIN @GUIDs y ON x.UniqueRecipeParameter = y.UniqueRecipeParameter ;

PRINT N'Cleaning up...'
ALTER TABLE Parameter
DROP COLUMN UniqueRecipeParameter;
GO

ALTER TABLE Parameter
DROP COLUMN IsPhase;
GO

由于它是离线升级,我可以跳过3和4。

再次感谢。它工作。