【问题标题】:SQL Server Update group by names from multiple tables?SQL Server 按多个表中的名称更新组?
【发布时间】:2013-07-28 06:53:32
【问题描述】:

我有两个故事配方和参数。配方表包含具有相同配方名称的多个版本。参数表包含一个配方的多个参数名称。如何通过配方名称和参数名称将 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 相同等等。我有 500 万条记录要更新。

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

提前致谢。

【问题讨论】:

  • 你怎么知道更新的对是什么?
  • @JackSquare:你试过我的解决方案了吗?

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


【解决方案1】:

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

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

注意 3:如果取消注释 EXEC sp_trace_generateevent 行,然后创建跟踪以捕获 UserConfigurable0 事件(服务器跟踪比 SQL 事件探查器跟踪的开销更少),您可以看到有多少行更新为新的 id GUID。

注意 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
*/

【讨论】:

    【解决方案2】:

    谢谢博格丹。你帮了大忙。

    我在现有的参数表中添加了一个 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。

    再次感谢。它工作正常。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-07-20
      • 2011-02-22
      • 1970-01-01
      • 1970-01-01
      • 2013-01-09
      • 1970-01-01
      • 1970-01-01
      • 2013-03-11
      相关资源
      最近更新 更多