Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Basculement des partitions de table dans SQL Server :procédure pas à pas

Présentation

Il y a quelques années, nous avons été chargés d'une exigence commerciale pour les données de carte dans un format spécifique à des fins de quelque chose appelé "rapprochement". L'idée était de présenter les données dans un tableau à une application qui consommerait et traiterait les données qui auraient une durée de conservation de six mois. Nous avons dû créer une nouvelle base de données pour ce besoin métier, puis créer la table principale en tant que table partitionnée. Le processus décrit ici est le processus que nous utilisons pour nous assurer que les données de plus de six mois sont retirées du tableau de manière propre.

Un peu sur le partitionnement

Le partitionnement de table est une technologie de base de données qui vous permet de stocker des données appartenant à une unité logique (la table) sous la forme d'un ensemble de partitions qui reposeront sur une structure physique distincte - des fichiers de données - via une couche d'abstraction appelée groupes de fichiers dans SQL Server. Le processus de création de cette table partitionnée implique deux objets clés :

Une fonction de partition :Une fonction de partition définit la manière dont les lignes d'une table partitionnée sont mappées en fonction des valeurs d'une colonne spécifiée (la colonne de partition). Une table partitionnée peut être basée soit sur une liste ou une plage. Pour les besoins de notre cas d'utilisation (conserver seulement six mois de données), nous avons utilisé une partition de plage . Une fonction de partition peut être définie comme PLAGE DROITE ou PLAGE GAUCHE. Nous avons utilisé RANGE RIGHT comme indiqué dans le code de la liste 1, ce qui signifie que la valeur limite appartiendra au côté droit de l'intervalle de valeur limite lorsque les valeurs sont triées par ordre croissant de gauche à droite.

-- Listing 1: Create a Partition Function
USE [post_office_history]
GO
CREATE PARTITION FUNCTION
PostTranPartFunc (datetime)
AS RANGE RIGHT
FOR VALUES 
('20190201'
,'20190301'
,'20190401'
,'20190501'
,'20190601'
,'20190701'
,'20190801'
,'20190901'
,'20191001'
,'20191101'
,'20191201'
)
GO

Un schéma de partition :Un schéma de partition est basé sur la fonction de partition et détermine sur quelles structures physiques les rangées appartenant à chaque partition seront placées. Ceci est réalisé en mappant ces lignes à des groupes de fichiers. Le Listing 2 montre le code pour créer un schéma de partition. Avant de créer le schéma de partition, les groupes de fichiers auxquels il fera référence doivent exister.

-- Listing 2: Create Partition Scheme --

-- Step 1: Create Filegroups --

USE [master]
GO
ALTER DATABASE [post_office_history] ADD FILEGROUP [JAN]
ALTER DATABASE [post_office_history] ADD FILEGROUP [FEB]
ALTER DATABASE [post_office_history] ADD FILEGROUP [MAR]
ALTER DATABASE [post_office_history] ADD FILEGROUP [APR]
ALTER DATABASE [post_office_history] ADD FILEGROUP [MAY]
ALTER DATABASE [post_office_history] ADD FILEGROUP [JUN]
ALTER DATABASE [post_office_history] ADD FILEGROUP [JUL]
ALTER DATABASE [post_office_history] ADD FILEGROUP [AUG]
ALTER DATABASE [post_office_history] ADD FILEGROUP [SEP]
ALTER DATABASE [post_office_history] ADD FILEGROUP [OCT]
ALTER DATABASE [post_office_history] ADD FILEGROUP [NOV]
ALTER DATABASE [post_office_history] ADD FILEGROUP [DEC]
GO


-- Step 2: Add Data Files to each Filegroup --

USE [master]
GO
ALTER DATABASE [post_office_history] ADD FILE (NAME = N'post_office_history_part_01', FILENAME = N'E:\MSSQL\DATA\post_office_history_part_01.ndf', SIZE = 2097152KB, FILEGROWTH = 1048576KB) TO FILEGROUP [JAN]
ALTER DATABASE [post_office_history] ADD FILE (NAME = N'post_office_history_part_02', FILENAME = N'E:\MSSQL\DATA\post_office_history_part_02.ndf', SIZE = 2097152KB, FILEGROWTH = 1048576KB) TO FILEGROUP [FEB]
ALTER DATABASE [post_office_history] ADD FILE (NAME = N'post_office_history_part_03', FILENAME = N'E:\MSSQL\DATA\post_office_history_part_03.ndf', SIZE = 2097152KB, FILEGROWTH = 1048576KB) TO FILEGROUP [MAR]
ALTER DATABASE [post_office_history] ADD FILE (NAME = N'post_office_history_part_04', FILENAME = N'E:\MSSQL\DATA\post_office_history_part_04.ndf', SIZE = 2097152KB, FILEGROWTH = 1048576KB) TO FILEGROUP [APR]
ALTER DATABASE [post_office_history] ADD FILE (NAME = N'post_office_history_part_05', FILENAME = N'E:\MSSQL\DATA\post_office_history_part_05.ndf', SIZE = 2097152KB, FILEGROWTH = 1048576KB) TO FILEGROUP [MAY]
ALTER DATABASE [post_office_history] ADD FILE (NAME = N'post_office_history_part_06', FILENAME = N'G:\MSSQL\DATA\post_office_history_part_06.ndf', SIZE = 2097152KB, FILEGROWTH = 1048576KB) TO FILEGROUP [JUN]
ALTER DATABASE [post_office_history] ADD FILE (NAME = N'post_office_history_part_07', FILENAME = N'G:\MSSQL\DATA\post_office_history_part_07.ndf', SIZE = 2097152KB, FILEGROWTH = 1048576KB) TO FILEGROUP [JUL]
ALTER DATABASE [post_office_history] ADD FILE (NAME = N'post_office_history_part_08', FILENAME = N'G:\MSSQL\DATA\post_office_history_part_08.ndf', SIZE = 2097152KB, FILEGROWTH = 1048576KB) TO FILEGROUP [AUG]
ALTER DATABASE [post_office_history] ADD FILE (NAME = N'post_office_history_part_09', FILENAME = N'G:\MSSQL\DATA\post_office_history_part_09.ndf', SIZE = 2097152KB, FILEGROWTH = 1048576KB) TO FILEGROUP [SEP]
ALTER DATABASE [post_office_history] ADD FILE (NAME = N'post_office_history_part_10', FILENAME = N'G:\MSSQL\DATA\post_office_history_part_10.ndf', SIZE = 2097152KB, FILEGROWTH = 1048576KB) TO FILEGROUP [OCT]
GO
ALTER DATABASE [post_office_history] ADD FILE (NAME = N'post_office_history_part_09', FILENAME = N'G:\MSSQL\DATA\post_office_history_part_11.ndf', SIZE = 2097152KB, FILEGROWTH = 1048576KB) TO FILEGROUP [NOV]
ALTER DATABASE [post_office_history] ADD FILE (NAME = N'post_office_history_part_10', FILENAME = N'G:\MSSQL\DATA\post_office_history_part_12.ndf', SIZE = 2097152KB, FILEGROWTH = 1048576KB) TO FILEGROUP [DEC]
GO


-- Step 3: Create Partition Scheme --

PRINT 'creating partition scheme ...'
GO

USE [post_office_history]
GO
CREATE PARTITION SCHEME PostTranPartSch 
AS PARTITION PostTranPartFunc TO
(
JAN,
FEB,
MAR,
APR,
MAY,
JUN,
JUL,
AUG,
SEP,
OCT,
NOV,
DEC
)
GO

Notez que pour N partitions, il y aura toujours N-1 limites. Des précautions doivent être prises lors de la définition du premier groupe de fichiers dans le schéma de partition. La première limite répertoriée dans la fonction de partition se situera entre les premier et deuxième groupes de fichiers. Ainsi, cette valeur limite (20190201) figurera dans la deuxième partition (FEB). De plus, il est en fait possible de placer toutes les partitions dans un seul groupe de fichiers, mais nous avons choisi des groupes de fichiers séparés dans ce cas.

Se salir les mains

Alors plongeons-nous dans la tâche de changer les partitions !

La première chose que nous devons faire est de déterminer exactement comment nos données sont réparties entre les partitions afin que nous puissions savoir quelle partition nous aimerions désactiver. En règle générale, nous désactiverons la partition la plus ancienne.

-- Listing 3: Check Data Distribution in Partitions --

USE POST_OFFICE_HISTORY
GO
SELECT $PARTITION.POSTTRANPARTFUNC(DATETIME_TRAN_LOCAL)
			AS [PARTITION NUMBER]
	, MIN(DATETIME_TRAN_LOCAL) AS [MIN DATE]
	, MAX(DATETIME_TRAN_LOCAL) AS [MAX DATE]
	, COUNT(*) AS [ROWS IN PARTITION]
FROM DBO.POST_TRAN_TAB -- PARTITIONED TABLE
GROUP BY $PARTITION.POSTTRANPARTFUNC(DATETIME_TRAN_LOCAL)
ORDER BY [PARTITION NUMBER]
GO

Fig. 1 Sortie du Listing 3

La figure 1 nous montre la sortie de la requête dans la liste 3. La partition la plus ancienne est la partition 2 qui contient des lignes de l'année 2017. Nous vérifions cela avec la requête dans la liste 4. La liste 4 nous montre également quel groupe de fichiers contient les données dans la partition. 2.

-- Listing 4: Check Filegroup Associated with Partition --

USE POST_OFFICE_HISTORY
GO
SELECT PS.NAME AS PSNAME, 
		DDS.DESTINATION_ID AS PARTITIONNUMBER, 
		FG.NAME AS FILEGROUPNAME
FROM (((SYS.TABLES AS T 
	INNER JOIN SYS.INDEXES AS I 
		ON (T.OBJECT_ID = I.OBJECT_ID))
	INNER JOIN SYS.PARTITION_SCHEMES AS PS 
		ON (I.DATA_SPACE_ID = PS.DATA_SPACE_ID))
	INNER JOIN SYS.DESTINATION_DATA_SPACES AS DDS 
		ON (PS.DATA_SPACE_ID = DDS.PARTITION_SCHEME_ID))
	INNER JOIN SYS.FILEGROUPS AS FG
		ON DDS.DATA_SPACE_ID = FG.DATA_SPACE_ID
WHERE (T.NAME = 'POST_TRAN_TAB') AND (I.INDEX_ID IN (0,1))
	AND DDS.DESTINATION_ID = $PARTITION.POSTTRANPARTFUNC('20171108') ;

Fig. 1 Sortie du Listing 3

Fig. 2 Sortie du Listing 4

Le Listing 4 nous montre que le groupe de fichiers associé à la Partition 2 est NOV . Pour désactiver la partition 2, nous avons besoin d'une table d'historique qui est une réplique de la table en direct mais qui se trouve sur le même groupe de fichiers que la partition que nous avons l'intention de désactiver. Puisque nous avons déjà cette table, tout ce dont nous avons besoin est de la recréer sur le groupe de fichiers souhaité. Vous devez également recréer l'index clusterisé. Notez que cet index clusterisé a la même définition que l'index clusterisé sur la table post_tran_tab et se trouve également sur le même groupe de fichiers que post_tran_tab_hist tableau.

-- Listing 5: Re-create the History Table 
-- Re-create the History Table --

USE [post_office_history]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

SET ANSI_PADDING ON
GO

DROP TABLE [dbo].[post_tran_tab_hist]
GO

CREATE TABLE [dbo].[post_tran_tab_hist](
	[tran_nr] [bigint] NOT NULL,
	[tran_type] [char](2) NULL,
	[tran_reversed] [char](2) NULL,
	[batch_nr] [int] NULL,
	[message_type] [char](4) NULL,
	[source_node_name] [varchar](12) NULL,
	[system_trace_audit_nr] [char](6) NULL,
	[settle_currency_code] [char](3) NULL,
	[sink_node_name] [varchar](30) NULL,
	[sink_node_currency_code] [char](3) NULL,
	[to_account_id] [varchar](30) NULL,
	[pan] [varchar](19) NOT NULL,
	[pan_encrypted] [char](18) NULL,
	[pan_reference] [char](70) NULL,
	[datetime_tran_local] [datetime] NOT NULL,
	[tran_amount_req] [float] NOT NULL,
	[tran_amount_rsp] [float] NOT NULL,
	[tran_cash_req] [float] NOT NULL,
	[tran_cash_rsp] [float] NOT NULL,
	[datetime_tran_gmt] [char](10) NULL,
	[merchant_type] [char](4) NULL,
	[pos_entry_mode] [char](3) NULL,
	[pos_condition_code] [char](2) NULL,
	[acquiring_inst_id_code] [varchar](11) NULL,
	[retrieval_reference_nr] [char](12) NULL,
	[auth_id_rsp] [char](6) NULL,
	[rsp_code_rsp] [char](2) NULL,
	[service_restriction_code] [char](3) NULL,
	[terminal_id] [char](8) NULL,
	[terminal_owner] [varchar](25) NULL,
	[card_acceptor_id_code] [char](15) NULL,
	[card_acceptor_name_loc] [char](40) NULL,
	[from_account_id] [varchar](28) NULL,
	[auth_reason] [char](1) NULL,
	[auth_type] [char](1) NULL,
	[message_reason_code] [char](4) NULL,
	[datetime_req] [datetime] NULL,
	[datetime_rsp] [datetime] NULL,
	[from_account_type] [char](2) NULL,
	[to_account_type] [char](2) NULL,
	[insert_date] [datetime] NOT NULL,
	[tran_postilion_originated] [int] NOT NULL,
	[card_product] [varchar](20) NULL,
	[card_seq_nr] [char](3) NULL,
	[expiry_date] [char](4) NULL,
	[srcnode_cash_approved] [float] NOT NULL,
	[tran_completed] [char](2) NULL
) ON [NOV] 

GO

SET ANSI_PADDING OFF
GO

-- Re-create the Clustered Index --
USE [post_office_history]
GO

CREATE CLUSTERED INDEX [IX_Datetime_Local] ON [dbo].[post_tran_tab_hist] 
(
	[datetime_tran_local] ASC,
	[tran_nr] ASC
	
) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [NOV]
GO

La désactivation de la dernière partition est désormais une commande d'une seule ligne. Compter les deux tables avant et après l'exécution de cette commande d'une ligne donnera l'assurance que nous avons toutes les données souhaitées.

Fig. 3 La table post_tran_tab_hist se trouve sur le groupe de fichiers NOV

-- Listing 6: Switching Out the Last Partition
SELECT COUNT(*) FROM 'POST_TRAN_TAB';
SELECT COUNT(*) FROM 'POST_TRAN_TAB_HIST';

USE [POST_OFFICE_HISTORY]
GO
ALTER TABLE POST_TRAN_TAB SWITCH PARTITION 2 TO POST_TRAN_TAB_HIST
GO

SELECT COUNT(*) FROM 'POST_TRAN_TAB';
SELECT COUNT(*) FROM 'POST_TRAN_TAB_HIST';

Puisque nous avons désactivé la dernière partition, nous n'avons plus besoin de la frontière. Nous fusionnons les deux plages précédemment séparées par cette limite à l'aide de la commande du Listing 7. Nous tronquons davantage la table d'historique comme indiqué dans le Listing 8. Nous faisons cela parce que c'est tout l'intérêt :supprimer les anciennes données dont nous n'avons plus besoin.

-- Listing 7: Merging Partition Ranges
-- Merge Range

USE [POST_OFFICE_HISTORY]
GO
ALTER PARTITION FUNCTION POSTTRANPARTFUNC() MERGE RANGE ('20171101');


-- Confirm Range Is Merged

USE [POST_OFFICE_HISTORY]
GO
SELECT * FROM SYS.PARTITION_RANGE_VALUES
GO

Fig. 4 Frontière fusionnée

-- Listing 8: Truncate the History Table


USE [post_office_history]
GO
TRUNCATE TABLE post_tran_tab_hist;
GO

Fig. 5 Nombre de lignes pour les deux tables avant la troncation

Notez que le nombre de lignes dans la table d'historique est exactement le même que le nombre de lignes précédemment dans la partition 2, comme illustré à la Fig. 1. Vous pouvez également aller plus loin en récupérant l'espace vide dans le groupe de fichiers appartenant au dernier cloison. Cela sera utile si vous avez besoin de cet espace pour les nouvelles données qui seront placées sur la partition précédente. Cette étape peut ne pas être nécessaire si vous pensez avoir suffisamment d'espace dans votre environnement.

-- Listing 9: Recover Space on Operating System
-- Determine that File has been emptied

USE [post_office_history]
GO
SELECT DF.FILE_ID, DF.NAME, DF.PHYSICAL_NAME, DS.NAME, DS.TYPE, DF.STATE_DESC FROM SYS.DATABASE_FILES DF
JOIN SYS.DATA_SPACES DS ON DF.DATA_SPACE_ID = DS.DATA_SPACE_ID;

Fig. 7 Mappages de fichiers à groupes de fichiers

-- Shrink the file to 2GB

USE [post_office_history]
GO
DBCC SHRINKFILE (N'post_office_history_part_11’, 2048)
GO


-- From the OS confirm free space on disks

SELECT DISTINCT DB_NAME (S.DATABASE_ID) AS DATABASE_NAME,
S.DATABASE_ID, S.VOLUME_MOUNT_POINT
--, S.VOLUME_ID
, S.LOGICAL_VOLUME_NAME
, S.FILE_SYSTEM_TYPE
, S.TOTAL_BYTES/1024/1024/1024 AS [TOTAL_SIZE (GB)]
, S.AVAILABLE_BYTES/1024/1024/1024 AS [FREE_SPACE (GB)]
, LEFT ((ROUND (((S.AVAILABLE_BYTES*1.0)/S.TOTAL_BYTES), 4)*100),4) AS PERCENT_FREE
FROM SYS.MASTER_FILES AS F
CROSS APPLY SYS.DM_OS_VOLUME_STATS (F.DATABASE_ID, F.FILE_ID) AS S
WHERE DB_NAME (S.DATABASE_ID) = 'POST_OFFICE_HISTORY';

Fig. 8 Espace libre sur le système d'exploitation

Conclusion

Dans cet article, nous avons fait une présentation du processus de basculement des partitions d'une table partitionnée. Il s'agit d'un moyen très efficace de gérer la croissance des données de manière native dans SQL Server. Des technologies plus avancées telles que Stretch Database sont disponibles dans les versions actuelles de SQL Server.

Références

En ligneIsakov, V. (2018). Examen Réf 70-764 Administration d'une infrastructure de base de données SQL. Éducation Pearson

Tables et index partitionnés dans SQL Server