Sebagai SQL Server DBA, kami selalu menangani salah satu hal terpenting untuk bisnis, data. Dalam beberapa kasus, aplikasi bisa menjadi sangat kompleks, dan Anda berakhir dengan banyak tabel database yang tersebar di sekitar instance SQL Server Anda. Hal ini dapat menyebabkan beberapa ketidaknyamanan, seperti:
- Mengetahui bagaimana data Anda berperilaku setiap hari, dalam hal tren pertumbuhan (ruang dan/atau jumlah baris).
- Mengetahui tabel database apa yang memerlukan (atau akan memerlukan) strategi tertentu/berbeda untuk menyimpan data karena pertumbuhannya terlalu cepat.
- Mengetahui tabel database mana yang memakan terlalu banyak ruang, yang mungkin menyebabkan kendala penyimpanan.
Karena pentingnya detail ini, saya telah membuat beberapa Prosedur Tersimpan yang dapat sangat membantu untuk DBA SQL Server mana pun yang ingin melacak informasi mengenai tabel database di lingkungannya. Percayalah, salah satunya sangat keren.
Pertimbangan Awal
- Pastikan bahwa akun yang menjalankan Prosedur Tersimpan ini memiliki cukup hak istimewa. Anda mungkin bisa mulai dengan sysadmin dan kemudian pergi sedetail mungkin untuk memastikan pengguna memiliki hak minimum yang diperlukan agar SP berfungsi dengan baik.
- Objek database (tabel database dan prosedur tersimpan) akan dibuat di dalam database yang dipilih pada saat skrip dijalankan, jadi pilihlah dengan cermat.
- Skrip dibuat sedemikian rupa sehingga dapat dieksekusi beberapa kali tanpa membuat Anda error. Untuk Prosedur Tersimpan, saya menggunakan pernyataan "CREATE OR ALTER PROCEDURE", tersedia sejak SQL Server 2016 SP1. Itu sebabnya jangan heran jika tidak bekerja dengan lancar di versi sebelumnya.
- Silakan mengubah nama objek database yang dibuat.
- Perhatikan parameter Prosedur Tersimpan yang mengumpulkan data mentah. Mereka bisa menjadi sangat penting dalam strategi pengumpulan data yang kuat untuk memvisualisasikan tren.
Bagaimana Cara Menggunakan Prosedur Tersimpan?
- Copy &paste Kode T-SQL (tersedia dalam artikel ini).
- SP pertama mengharapkan 2 parameter:
- @persistData:‘Y’ jika DBA ingin menyimpan output dalam tabel target, dan ‘N’ jika DBA ingin melihat output secara langsung.
- @truncateTable:'Y' untuk memotong tabel terlebih dahulu sebelum menyimpan data yang diambil, dan 'N' jika data saat ini disimpan dalam tabel. Perlu diingat bahwa nilai parameter ini tidak relevan jika nilai parameter @persistData adalah ‘N’.
- SP kedua mengharapkan 1 parameter:
- @targetParameter:Nama kolom yang akan digunakan untuk mengubah urutan informasi yang dikumpulkan.
Bidang yang Disajikan dan Maknanya
- nama_database: nama database tempat tabel berada.
- skema: nama skema tempat tabel berada.
- nama_tabel: placeholder untuk nama tabel.
- jumlah_baris: jumlah baris yang dimiliki tabel saat ini.
- total_spasi_mb: jumlah MegaBytes yang dialokasikan untuk tabel.
- bekas_spasi_mb: jumlah MegaBytes yang sebenarnya digunakan oleh tabel.
- unused_space_mb: jumlah MegaBytes yang tidak digunakan tabel.
- tanggal_dibuat: tanggal/waktu tabel dibuat.
- data_collection_timestamp: hanya terlihat jika 'Y' diteruskan ke parameter @persistData. Ini digunakan untuk mengetahui kapan SP dieksekusi dan informasi berhasil disimpan di tabel DBA_Tables.
Tes Eksekusi
Saya akan mendemonstrasikan beberapa eksekusi Stored Procedures:
/* Menampilkan informasi tabel untuk semua database pengguna */
EXEC GetTablesData @persistData = 'N',@truncateTable = 'N'
/* Pertahankan informasi tabel database dan kueri tabel target, potong tabel target terlebih dahulu */
EXEC GetTablesData @persistData = 'Y',@truncateTable = 'Y'
SELECT * FROM DBA_Tables
Kueri Samping
*Kueri untuk melihat tabel database yang diurutkan dari jumlah baris terbesar hingga terendah.
SELECT * FROM DBA_Tables ORDER BY row_count DESC;
*Kueri untuk melihat tabel database yang diurutkan dari total ruang terbesar hingga terendah.
SELECT * FROM DBA_Tables ORDER BY total_space_mb DESC;
*Kueri untuk melihat tabel database yang diurutkan dari ruang yang digunakan terbesar hingga yang terendah.
SELECT * FROM DBA_Tables ORDER BY used_space_mb DESC;
*Kueri untuk melihat tabel database yang diurutkan dari ruang terbesar yang tidak terpakai hingga yang terendah.
SELECT * FROM DBA_Tables ORDER BY unused_space_mb DESC;
*Kueri untuk melihat tabel database yang diurutkan berdasarkan tanggal pembuatan, dari yang terbaru hingga yang terlama.
SELECT * FROM DBA_Tables ORDER BY created_date DESC;
Berikut kode lengkap Stored Procedure yang menangkap informasi tabel database:
*Di awal skrip, Anda akan melihat nilai default yang diasumsikan oleh Prosedur Tersimpan jika tidak ada nilai yang diteruskan untuk setiap parameter.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE OR ALTER PROCEDURE [dbo].[GetTablesData]
@persistData CHAR(1) = 'Y',
@truncateTable CHAR(1) = 'Y'
AS
BEGIN
SET NOCOUNT ON
DECLARE @command NVARCHAR(MAX)
DECLARE @Tmp_TablesInformation TABLE(
[database] [VARCHAR](255) NOT NULL,
[schema] [VARCHAR](64) NOT NULL,
[table] [VARCHAR](255) NOT NULL,
[row_count] [BIGINT]NOT NULL,
[total_space_mb] [DECIMAL](15,2) NOT NULL,
[used_space_mb] [DECIMAL](15,2) NOT NULL,
[unused_space_mb] [DECIMAL](15,2) NOT NULL,
[created_date] [DATETIME] NOT NULL
)
SELECT @command = '
USE [?]
IF DB_ID(''?'') > 4
BEGIN
SELECT
''?'',
s.Name AS [schema],
t.NAME AS [table],
p.rows AS row_count,
CAST(ROUND(((SUM(a.total_pages) * 8) / 1024.00), 2) AS DECIMAL(15, 2)) AS total_space_mb,
CAST(ROUND(((SUM(a.used_pages) * 8) / 1024.00), 2) AS DECIMAL(15, 2)) AS used_space_mb,
CAST(ROUND(((SUM(a.total_pages) - SUM(a.used_pages)) * 8) / 1024.00, 2) AS DECIMAL(15, 2)) AS unused_space_mb,
t.create_date as created_date
FROM sys.tables t
INNER JOIN sys.indexes i ON t.OBJECT_ID = i.object_id
INNER JOIN sys.partitions p ON i.object_id = p.OBJECT_ID AND i.index_id = p.index_id
INNER JOIN sys.allocation_units a ON p.partition_id = a.container_id
LEFT OUTER JOIN sys.schemas s ON t.schema_id = s.schema_id
WHERE t.NAME NOT LIKE ''dt%''
AND t.is_ms_shipped = 0
AND i.OBJECT_ID > 255
GROUP BY t.Name, s.Name, p.Rows,t.create_date
ORDER BY total_space_mb DESC, t.Name
END'
INSERT INTO @Tmp_TablesInformation
EXEC sp_MSForEachDB @command
IF @persistData = 'N'
SELECT * FROM @Tmp_TablesInformation
ELSE
BEGIN
IF(@truncateTable = 'Y')
TRUNCATE TABLE DBA_Tables
INSERT INTO DBA_Tables
SELECT *,GETDATE() FROM @Tmp_TablesInformation ORDER BY [database],[schema],[table]
END
END
GO
Sampai saat ini, informasinya tampak agak kering, tetapi izinkan saya mengubah persepsi itu dengan penyajian Prosedur Tersimpan pelengkap. Tujuan utamanya adalah untuk mentranspos informasi yang dikumpulkan dalam tabel target yang berfungsi sebagai sumber untuk laporan tren.
Berikut cara menjalankan Stored Procedure:
*Untuk tujuan demonstrasi, saya telah memasukkan catatan manual ke dalam tabel target bernama t1 untuk mensimulasikan eksekusi Prosedur Tersimpan saya yang biasa.
*Hasil set agak lebar, jadi saya akan mengambil beberapa tangkapan layar untuk menampilkan hasil lengkapnya.
EXEC TransposeTablesInformation @targetParmeter = 'row_count'
Pengambilan Penting
- Jika Anda mengotomatiskan eksekusi skrip yang mengisi tabel target, Anda dapat segera melihat jika ada yang tidak beres dengannya atau dengan data Anda. Perhatikan data untuk tabel 't1' dan kolom '15'. Anda dapat melihat NULL di sana yang dilakukan dengan sengaja untuk menunjukkan kepada Anda sesuatu yang mungkin terjadi.
- Dengan tampilan seperti ini, Anda dapat melihat perilaku aneh untuk tabel database yang paling penting/kritis.
- Saya contoh yang diberikan, saya telah memilih bidang 'jumlah_baris' dari tabel target, tetapi Anda dapat memilih bidang numerik lainnya sebagai parameter dan mendapatkan format tabel yang sama, tetapi dengan data yang berbeda.
- Jangan khawatir, jika Anda menetapkan parameter yang tidak valid, Prosedur Tersimpan akan memperingatkan Anda dan menghentikan eksekusinya.
Berikut kode lengkap Stored Procedure yang mengubah informasi tabel target:
*Di awal skrip, Anda akan melihat nilai default yang diasumsikan oleh Prosedur Tersimpan jika tidak ada nilai yang diteruskan untuk setiap parameter.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE OR ALTER PROCEDURE [dbo].[TransposeTablesInformation]
@targetParameter NVARCHAR(15) = 'row_count'
AS
BEGIN
SET NOCOUNT ON;
IF (@targetParameter <> 'row_count' AND @targetParameter <> 'total_space_mb' AND @targetParameter <> 'used_space_mb' AND @targetParameter <> 'unused_space_mb')
BEGIN
PRINT 'Please specify a valid parameter!'
PRINT 'i.e. row_count | total_space_mb | used_space_mb | unused_space_mb'
RETURN
END
ELSE
BEGIN
CREATE TABLE #TablesInformation(
[database] [VARCHAR](255) NOT NULL,
[schema] [VARCHAR](64) NOT NULL,
[table] [VARCHAR](255) NOT NULL,
[1] [DECIMAL](10,2) NULL,
[2] [DECIMAL](10,2) NULL,
[3] [DECIMAL](10,2) NULL,
[4] [DECIMAL](10,2) NULL,
[5] [DECIMAL](10,2) NULL,
[6] [DECIMAL](10,2) NULL,
[7] [DECIMAL](10,2) NULL,
[8] [DECIMAL](10,2) NULL,
[9] [DECIMAL](10,2) NULL,
[10] [DECIMAL](10,2) NULL,
[11] [DECIMAL](10,2) NULL,
[12] [DECIMAL](10,2) NULL,
[13] [DECIMAL](10,2) NULL,
[14] [DECIMAL](10,2) NULL,
[15] [DECIMAL](10,2) NULL,
[16] [DECIMAL](10,2) NULL,
[17] [DECIMAL](10,2) NULL,
[18] [DECIMAL](10,2) NULL,
[19] [DECIMAL](10,2) NULL,
[20] [DECIMAL](10,2) NULL,
[21] [DECIMAL](10,2) NULL,
[22] [DECIMAL](10,2) NULL,
[23] [DECIMAL](10,2) NULL,
[24] [DECIMAL](10,2) NULL,
[25] [DECIMAL](10,2) NULL,
[26] [DECIMAL](10,2) NULL,
[27] [DECIMAL](10,2) NULL,
[28] [DECIMAL](10,2) NULL,
[29] [DECIMAL](10,2) NULL,
[30] [DECIMAL](10,2) NULL,
[31] [DECIMAL](10,2) NULL
)
INSERT INTO #TablesInformation([database],[schema],[table])
SELECT DISTINCT [database_name],[schema],[table_name]
FROM DBA_Tables
ORDER BY [database_name],[schema],table_name
DECLARE @databaseName NVARCHAR(255)
DECLARE @schemaName NVARCHAR(64)
DECLARE @tableName NVARCHAR(255)
DECLARE @value DECIMAL(10,2)
DECLARE @dataTimestamp DATETIME
DECLARE @sqlCommand NVARCHAR(MAX)
IF(@targetParameter = 'row_count')
BEGIN
DECLARE TablesCursor CURSOR FOR
SELECT
[database_name],
[schema],
[table_name],
[row_count],
[data_collection_timestamp]
FROM DBA_Tables
ORDER BY [database_name],[schema],table_name
END
IF(@targetParameter = 'total_space_mb')
BEGIN
DECLARE TablesCursor CURSOR FOR
SELECT
[database_name],
[schema],
[table_name],
[total_space_mb],
[data_collection_timestamp]
FROM DBA_Tables
ORDER BY [database_name],[schema],table_name
END
IF(@targetParameter = 'used_space_mb')
BEGIN
DECLARE TablesCursor CURSOR FOR
SELECT
[database_name],
[schema],
[table_name],
[used_space_mb],
[data_collection_timestamp]
FROM DBA_Tables
ORDER BY [database_name],[schema],table_name
END
IF(@targetParameter = 'unused_space_mb')
BEGIN
DECLARE TablesCursor CURSOR FOR
SELECT
[database_name],
[schema],
[table_name],
[unused_space_mb],
[data_collection_timestamp]
FROM DBA_Tables
ORDER BY [database_name],[schema],table_name
END
OPEN TablesCursor
FETCH NEXT FROM TablesCursor INTO @databaseName,@schemaName,@tableName,@value,@dataTimestamp
WHILE(@@FETCH_STATUS = 0)
BEGIN
SET @sqlCommand = CONCAT('
UPDATE #TablesInformation
SET [',DAY(@dataTimestamp),'] = ',@value,'
WHERE [database] = ',CHAR(39),@databaseName,CHAR(39),'
AND [schema] = ',CHAR(39),@schemaName+CHAR(39),'
AND [table] = ',CHAR(39),@tableName+CHAR(39),'
')
EXEC(@sqlCommand)
FETCH NEXT FROM TablesCursor INTO @databaseName,@schemaName,@tableName,@value,@dataTimestamp
END
CLOSE TablesCursor
DEALLOCATE TablesCursor
IF(@targetParameter = 'row_count')
SELECT [database],
[schema],
[table],
CONVERT(INT,[1]) AS [1],
CONVERT(INT,[2]) AS [2],
CONVERT(INT,[3]) AS [3],
CONVERT(INT,[4]) AS [4],
CONVERT(INT,[5]) AS [5],
CONVERT(INT,[6]) AS [6],
CONVERT(INT,[7]) AS [7],
CONVERT(INT,[8]) AS [8],
CONVERT(INT,[9]) AS [9],
CONVERT(INT,[10]) AS [10],
CONVERT(INT,[11]) AS [11],
CONVERT(INT,[12]) AS [12],
CONVERT(INT,[13]) AS [13],
CONVERT(INT,[14]) AS [14],
CONVERT(INT,[15]) AS [15],
CONVERT(INT,[16]) AS [16],
CONVERT(INT,[17]) AS [17],
CONVERT(INT,[18]) AS [18],
CONVERT(INT,[19]) AS [19],
CONVERT(INT,[20]) AS [20],
CONVERT(INT,[21]) AS [21],
CONVERT(INT,[22]) AS [22],
CONVERT(INT,[23]) AS [23],
CONVERT(INT,[24]) AS [24],
CONVERT(INT,[25]) AS [25],
CONVERT(INT,[26]) AS [26],
CONVERT(INT,[27]) AS [27],
CONVERT(INT,[28]) AS [28],
CONVERT(INT,[29]) AS [29],
CONVERT(INT,[30]) AS [30],
CONVERT(INT,[31]) AS [31]
FROM #TablesInformation
ELSE
SELECT * FROM #TablesInformation
END
END
GO
Kesimpulan
- Anda dapat menerapkan SP pengumpulan data di setiap instans SQL Server di bawah dukungan Anda dan menerapkan mekanisme peringatan di seluruh tumpukan instans yang didukung.
- Jika Anda menerapkan pekerjaan agen yang menanyakan informasi ini secara relatif sering, Anda dapat tetap menjadi yang teratas dalam hal mengetahui bagaimana data Anda berperilaku selama bulan tersebut. Tentu saja, Anda dapat melangkah lebih jauh dan menyimpan data yang dikumpulkan setiap bulan untuk mendapatkan gambaran yang lebih besar; Anda harus membuat beberapa penyesuaian pada kode, tetapi itu akan sangat berguna.
- Pastikan untuk menguji mekanisme ini dengan benar di lingkungan sandbox dan, saat Anda merencanakan penerapan produksi, pastikan untuk memilih periode aktivitas rendah.
- Mengumpulkan informasi jenis ini dapat membantu membedakan DBA satu sama lain. Mungkin ada 3 alat pihak yang dapat melakukan hal yang sama, dan bahkan lebih, tetapi tidak semua orang memiliki anggaran untuk membelinya. Saya harap ini dapat membantu siapa saja yang memutuskan untuk menggunakannya di lingkungan mereka.