Поддръжката на дял на плъзгащ се прозорец обикновено се извършва с помощта на планиран скрипт или съхранена процедура (задача на SQL Server Agent или друга система за планиране). Поддръжката трябва да се планира, за да се избегне скъпоструващо преместване на данни по време на SPLIT
и MERGE
защото това изисква около 4 пъти повече регистриране в сравнение с нормалните DML операции. За тази цел се уверете, че дялът, който включва граничната стойност, е празен преди MERGE
и нито един ред не е по-голям от определената граница, когато SPLIT
. Предлагам да създадете няколко допълнителни бъдещи дяла като буфер, за да избегнете движението на данни, в случай че поддръжката не се изпълнява по план.
По-долу е даден примерен скрипт за ежедневно плъзгане на прозорец за поддръжка. Това използва подобно разделена етапна таблица за изчистването, тъй като използвате SQL Server 2005 и TRUNCATE
на ниво дял беше представен в SQL Server 2016. Имайте предвид, че SQL Server 2005 не се поддържа.
Виждам от коментара ви, че смятате, че отделна файлова група/файл на дял може да е полезно за премахване на дялове, но това не е така. Този пример използва една файлова група за всички дялове.
--example setup
CREATE PARTITION FUNCTION PF_Date (datetime) AS
RANGE RIGHT FOR VALUES();
CREATE PARTITION SCHEME PS_LogTable AS
PARTITION PF_Date ALL TO ([PRIMARY]);
DECLARE @PartitionBoundaryDate datetime = DATEADD(day, -31, DATEADD(day, DATEDIFF(day, '', GETDATE()), ''));
WHILE @PartitionBoundaryDate < DATEADD(day, 1, GETDATE())
BEGIN
ALTER PARTITION SCHEME PS_LogTable NEXT USED [PRIMARY];
ALTER PARTITION FUNCTION PF_Date() SPLIT RANGE(@PartitionBoundaryDate);
SET @PartitionBoundaryDate = DATEADD(day, 1, @PartitionBoundaryDate)
END;
CREATE TABLE dbo.LogTable(DateColumn datetime INDEX cdx CLUSTERED) ON PS_LogTable(DateColumn);
CREATE TABLE dbo.LogTable_Staging(DateColumn datetime INDEX cdx CLUSTERED) ON PS_LogTable(DateColumn);
GO
--example partition maintenance scheduled nightly after midnight
BEGIN TRY
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE @RetentionDays int = 31;
DECLARE @FutureDays int = 7;
DECLARE @OldestRetainedDate datetime = DATEADD(day, [email protected], DATEADD(day, DATEDIFF(day, '', GETDATE()), ''));
DECLARE @LatestRetainedDate datetime = DATEADD(day, DATEDIFF(day, '', GETDATE()), '');
DECLARE @LatestFutureBoundaryDate datetime = DATEADD(day, @FutureDays, @LatestRetainedDate);
DECLARE @PartitionBoundaryDate datetime;
DECLARE @Message nvarchar(2048);
--make sure staging table is empty
TRUNCATE TABLE dbo.LogTable_Staging;
BEGIN TRAN;
--aquire exclusive table lock to avoid deadlocking during maintenance
SELECT TOP(0) @PartitionBoundaryDate = DateColumn FROM dbo.LogTable WITH(TABLOCKX);
--purge partition 1 in case data older than the first boundary was inserted
SET @Message = 'Purging partition 1';
PRINT @Message;
ALTER TABLE dbo.LogTable SWITCH
PARTITION 1 TO
dbo.LogTable_Staging PARTITION 1;
TRUNCATE TABLE dbo.LogTable_Staging;
--purge and remove expired partitions
DECLARE @PartitionBoundaries TABLE(PartitionBoundaryDate datetime NOT NULL PRIMARY KEY);
INSERT INTO @PartitionBoundaries(PartitionBoundaryDate)
SELECT CAST(prv.value AS datetime)
FROM sys.partition_functions AS pf
JOIN sys.partition_range_values AS prv ON prv.function_id = pf.function_id
WHERE
pf.name = N'PF_Date'
AND CAST(prv.value AS datetime) < @OldestRetainedDate;
DECLARE ExpiredPartitionBoundaries CURSOR LOCAL FAST_FORWARD FOR
SELECT PartitionBoundaryDate
FROM @PartitionBoundaries;
OPEN ExpiredPartitionBoundaries;
WHILE 1 = 1
BEGIN
FETCH NEXT FROM ExpiredPartitionBoundaries INTO @PartitionBoundaryDate;
IF @@FETCH_STATUS = -1 BREAK;
SET @Message = 'Purging data for ' + CONVERT(char(10), @PartitionBoundaryDate, 120);
PRINT @Message;
ALTER TABLE dbo.LogTable SWITCH
PARTITION $PARTITION.PF_Date(@PartitionBoundaryDate) TO
dbo.LogTable_Staging PARTITION $PARTITION.PF_Date(@PartitionBoundaryDate);
TRUNCATE TABLE dbo.LogTable_Staging;
ALTER PARTITION FUNCTION PF_Date() MERGE RANGE(@PartitionBoundaryDate);
END;
CLOSE ExpiredPartitionBoundaries;
DEALLOCATE ExpiredPartitionBoundaries;
--create partitions for future days
SET @PartitionBoundaryDate = DATEADD(day, 1, @LatestRetainedDate);
WHILE @PartitionBoundaryDate < = @LatestFutureBoundaryDate
BEGIN
IF NOT EXISTS(SELECT 1
FROM sys.partition_functions AS pf
JOIN sys.partition_range_values AS prv ON prv.function_id = pf.function_id
WHERE
pf.name = N'PF_Date'
AND CAST(prv.value AS datetime) = @PartitionBoundaryDate
)
BEGIN
SET @Message = 'Creating partition for ' + CONVERT(char(10), @PartitionBoundaryDate, 120);
PRINT @Message;
ALTER PARTITION SCHEME PS_LogTable NEXT USED [PRIMARY];
ALTER PARTITION FUNCTION PF_Date() SPLIT RANGE(@PartitionBoundaryDate);
END;
SET @PartitionBoundaryDate = DATEADD(day, 1, @PartitionBoundaryDate);
END;
COMMIT;
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0 ROLLBACK;
--better to use THROW in SQL 2012 and later
DECLARE
@ErrorNumber int
,@ErrorMessage nvarchar(2048)
,@ErrorSeverity int
,@ErrorState int
,@ErrorLine int;
SELECT
@ErrorNumber =ERROR_NUMBER()
,@ErrorMessage =ERROR_MESSAGE()
,@ErrorSeverity = ERROR_SEVERITY()
,@ErrorState =ERROR_STATE()
,@ErrorLine =ERROR_LINE();
RAISERROR('Error %d caught at line %d: %s'
,@ErrorSeverity
,@ErrorState
,@ErrorNumber
,@ErrorLine
,@ErrorMessage);
END CATCH;
GO