Ich wurde beauftragt, eine Aktualisierungsabfrage zu schreiben, um eine Tabelle mit mehr als 850 Millionen Datenzeilen zu aktualisieren. Hier sind die Tabellenstrukturen:
Quellentabellen:
CREATE TABLE [dbo].[SourceTable1](
[ProdClassID] [varchar](10) NOT NULL,
[PriceListDate] [varchar](8) NOT NULL,
[PriceListVersion] [smallint] NOT NULL,
[MarketID] [varchar](10) NOT NULL,
[ModelID] [varchar](20) NOT NULL,
[VariantId] [varchar](20) NOT NULL,
[VariantType] [tinyint] NULL,
[Visibility] [tinyint] NULL,
CONSTRAINT [PK_SourceTable1] PRIMARY KEY CLUSTERED
(
[VariantId] ASC,
[ModelID] ASC,
[MarketID] ASC,
[ProdClassID] ASC,
[PriceListDate] ASC,
[PriceListVersion] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90)
)
CREATE TABLE [dbo].[SourceTable2](
[Id] [uniqueidentifier] NOT NULL,
[ProdClassID] [varchar](10) NULL,
[PriceListDate] [varchar](8) NULL,
[PriceListVersion] [smallint] NULL,
[MarketID] [varchar](10) NULL,
[ModelID] [varchar](20) NULL,
CONSTRAINT [PK_SourceTable2] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 91) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
SourceTable1enthält 52 Millionen Datenzeilen und SourceTable2enthält 400.000 Datenzeilen.
Hier ist die TargetTableStruktur
CREATE TABLE [dbo].[TargetTable](
[ChassisSpecificationId] [uniqueidentifier] NOT NULL,
[VariantId] [varchar](20) NOT NULL,
[VariantType] [tinyint] NULL,
[Visibility] [tinyint] NULL,
CONSTRAINT [PK_TargetTable] PRIMARY KEY CLUSTERED
(
[ChassisSpecificationId] ASC,
[VariantId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 71) ON [PRIMARY]
) ON [PRIMARY]
Die Beziehung zwischen diesen Tabellen ist wie folgt:
SourceTable1.VariantIDbezieht sich aufTargetTable.VariantIDSourceTable2.IDbezieht sich aufTargetTable.ChassisSpecificationId
Die Aktualisierungsanforderung lautet wie folgt:
- Holen Sie sich die Werte für
VariantTypeundVisibilityvonSourceTable1jedemVariantIDmit dem Maximalwert in derPriceListVersionSpalte. - Holen Sie sich den Wert der
IDSpalte vonSourceTable2wo die Werte vonModelID,ProdClassID,PriceListDateundMarketIDÜbereinstimmung mit derSourceTable1. - Aktualisieren Sie nun die
TargetTablemit den Werten fürVariantTypeundVisibilitywo dieChassisspecificationIDÜbereinstimmungenSourceTable2.IDundVariantIDÜbereinstimmungenSourceTable1.VariantID
Die Herausforderung besteht darin, dieses Update für die Live-Produktion mit minimaler Sperrung durchzuführen. Hier ist die Abfrage, die ich zusammengestellt habe.
-- Check if Temp table already exists and drop if it does
IF EXISTS(
SELECT NULL
FROM tempdb.sys.tables
WHERE name LIKE '#CSpec%'
)
BEGIN
DROP TABLE #CSpec;
END;
-- Create Temp table to assign sequence numbers
CREATE Table #CSpec
(
RowID int,
ID uniqueidentifier,
PriceListDate VarChar(8),
ProdClassID VarChar(10),
ModelID VarChar(20),
MarketID Varchar(10)
);
-- Populate temp table
INSERT INTO #CSpec
SELECT ROW_NUMBER() OVER (ORDER BY MarketID) RowID,
CS.id,
CS.pricelistdate,
CS.prodclassid,
CS.modelid,
CS.marketid
FROM dbo.SourceTable2 CS
WHERE CS.MarketID IS NOT NULL;
-- Declare variables to hold values used for updates
DECLARE @min int,
@max int,
@ID uniqueidentifier,
@PriceListDate varchar(8),
@ProdClassID varchar(10),
@ModelID varchar(20),
@MarketID varchar(10);
-- Set minimum and maximum values for looping
SET @min = 1;
SET @max = (SELECT MAX(RowID) From #CSpec);
-- Populate other variables in a loop
WHILE @min <= @max
BEGIN
SELECT
@ID = ID,
@PriceListDate = PriceListDate,
@ProdClassID = ProdClassID,
@ModelID = ModelID,
@MarketID = MarketID
FROM #CSpec
WHERE RowID = @min;
-- Use CTE to get relevant values from SourceTable1
;WITH Variant_CTE AS
(
SELECT V.variantid,
V.varianttype,
V.visibility,
MAX(V.PriceListVersion) LatestPriceVersion
FROM SourceTable1 V
WHERE V.ModelID = @ModelID
AND V.ProdClassID = @ProdClassID
AND V.PriceListDate = @PriceListDate
AND V.MarketID = @MarketID
GROUP BY
V.variantid,
V.varianttype,
V.visibility
)
-- Update the TargetTable with the values obtained in the CTE
UPDATE SV
SET SV.VariantType = VC.VariantType,
SV.Visibility = VC.Visibility
FROM spec_variant SV
INNER JOIN TargetTable VC
ON SV.VariantId = VC.VariantId
WHERE SV.ChassisSpecificationId = @ID
AND SV.VariantType IS NULL
AND SV.Visibility IS NULL;
-- Increment the value of loop variable
SET @min = @min+1;
END
-- Clean up
DROP TABLE #CSpec
Es dauert ungefähr 30 Sekunden, wenn ich das Limit der Iterationen auf 10 setze, indem ich den Wert der @maxVariablen fest codiere . Wenn ich jedoch das Limit auf 50 Iterationen erhöhe, dauert der Abschluss fast 4 Minuten. Ich befürchte, dass die Ausführungszeit für 400.000 Iterationen bei der Produktion mehrere Tage betragen wird. Dies ist jedoch möglicherweise noch akzeptabel, wenn das TargetTablenicht gesperrt wird und Benutzer nicht darauf zugreifen können.
Alle Eingaben sind willkommen.
Danke, Raj