SQL Server logbestand van een database leegmaken

Wellicht dat u dit probleem herkent: u heeft een testdatabase waarvan door het testen de logfile behoorlijk groot is geworden. Op een cursus heeft u wel eens gehoord van Transact-SQL functie “DBCC SHRINKFILE”. Via Microsoft Developer Network leest u vervolgens hoe DBCC SHRINKFILE moet worden gebruikt. Vervolgens past u dit op uw eigen omgeving toe en u merkt dat het logbestand na het gebruik van de functie DBCC SHRINKFILE (‘logical name’,1) nog steeds erg groot is. Wat nu?

Een alternatieve manier om het logbestand kleiner te maken
Er is een alternatieve manier om een logbestand kleiner te maken. Het proces is iets bewerkelijker en geeft het gewenste resultaat.

Methode
Deze procedure mag NOOIT op een productieomgeving worden uitgevoerd!
De werkwijze om de logfile op een alternatieve manier te verkleinen, is als volgt:

– Detach de database van SQL Server
– Verplaats de logfile van de database naar een tijdelijke andere locatie
– Attach de database, zonder logfile (bij melding ‘not found’ regel verwijderen)

Verplaatsen logbestand
Mocht de locatie van de logfile vervolgens anders moeten zijn, dan dienen
aanvullend de volgende stappen te worden toegevoegd:

– Detach de database opnieuw
– Verplaats de logfile naar de nieuw locatie
– Voer onderstaande query uit

CREATE DATABASE [Naam van de database]
ON (FILENAME = '[schijf/locatie]\[Naam mdf bestand].mdf'),
(FILENAME = '[schijf/locatie]\[Naam log bestand].ldf')
FOR ATTACH;

Het verplaatsen van een bestand binnen een database wordt door Microsoft
op deze locatie behandeld.

Nonclustered index soms sneller dan clustered index (performance)

Over het algemeen zorgt het toevoegen van een clustered index aan tabel ervoor dat query’s op deze tabel een betere performance hebben. Er zijn echter situaties waarbij het toevoegen van een nonclustered index een nog beter resultaat in performance geeft. Om te begrijpen waarom een nonclustered index soms sneller is, is het nodig te begrijpen hoe SQL Server fysiek werkt. Een clustered index is een index op een volledig record. Alle velden van een record zitten in de clustered index. Bij een nonclustered index is dat niet het geval. In deze index zit een selectie van velden. SQL Server slaat data op in zogenaamde 8K pagina’s (pagina’s van 8 kilobyte). Omdat bij een nonclustered index een selectie van velden wordt gebruikt, passen er veel meer rijen op een 8K pagina en hoeft SQL Server bij gebruik van een nonclustered index minder pagina’s te lezen (dus minder i/o). Hierdoor kan het gebruik van een nonclustered index voordeliger zijn dan het gebruik van een clustered index. In onderstaande reproductie wordt zichtbaar dat een nonclustered index voordeliger blijkt dan een custered index, waardoor de nonclustered index sneller is.

Downloads
Het volledige script is via deze link te downloaden.

We beginnen met het opzetten van een database en het aanmaken en vullen van tabellen

/*
Auteur : Tim Wiseman
Datum : 30 September 2013
Bron : https://www.mssqltips.com/sqlservertip/3041/when-sql-server-nonclustered-indexes-are-faster-than-clustered-indexes/

Bewerkt door : Mickel Reemer
Bewerkingsdatum : 24 juli 2016
Bewerkingsreden : Vertaling en verduidelijking
Testomgeving : SQL Server 2016, Developer Edition

Doel van het script
Dit script maakt de verschillen in performance met diverse soorten tabellen en
diverse indexen inzichtelijk. Het script maakt duidelijk dat clustered indexen
niet altijd sneller dan nonclustered indexen zijn.

Doorlooptijden
Aanmaken van de vier tabellen : 11m22s
*/

-- Maak een testdatabase aan; de gebruikte naam voor deze testdatabase is Demolition
USE Demolition
GO

-- We starten met het aanmaken van een tabel zonder indexen als referentie
if OBJECT_ID('noIndex', 'U') is not NULL
drop table dbo.noIndex

CREATE TABLE dbo.noIndex(
n int NOT NULL, -- Wordt onderdeel van de index
singleChar char(1) NOT NULL, -- Wordt onderdeel van de index
stringData char(2000) NOT NULL,
bigIntData bigint NOT NULL,
decimalData decimal(18, 0)NOT NULL
)
GO

-- De tabel met 1 miljoen records vullen
INSERT INTO dbo.noIndex(n, singleChar, stringData, bigIntData, decimalData)
SELECT TOP 1000000
ROW_NUMBER() OVER (ORDER BY s1.name) AS n,
CHAR((ROW_NUMBER() OVER (ORDER BY s1.name) % 89) + 33) AS singleChar,
REPLICATE(CHAR((ROW_NUMBER() OVER (ORDER BY s1.name) % 89) + 33), 2000) AS stringData,
ROW_NUMBER() OVER (ORDER BY s1.name) * 1000000000 AS bigIntData,
ROW_NUMBER() OVER (ORDER BY s1.name) *1.1 AS decimalData
FROM master.dbo.syscolumns s1,
master.dbo.syscolumns s2
GO

-- Vervolgens wordt er een tabel met een primary key aangemaakt.
-- De primary key zorgt voor een clustered index op de kolommen 'n' en 'singleChar'
IF OBJECT_ID('pkIndex', 'U') is not NULL
DROP TABLE dbo.pkIndex

CREATE TABLE dbo.pkIndex(
n int NOT NULL,
singleChar char(1) NOT NULL,
stringData char(2000) NOT NULL,
bigIntData bigint NOT NULL,
decimalData decimal(18, 0) NOT NULL,
CONSTRAINT PK_pkIndextable PRIMARY KEY CLUSTERED (n, singleChar)
)
GO

-- Ook deze tabel vullen we met data
INSERT INTO
dbo.pkIndex(n, singleChar, stringData, bigIntData, decimalData)
SELECT
n, singleChar, stringData, bigIntData, decimalData
FROM
dbo.noIndex
GO

-- De volgende tabel die wordt aangemaakt is een tabel vooor het testen
-- van de nonclustered index
IF OBJECT_ID('nonclusteredIdx', 'U') IS NOT NULL
DROP TABLE dbo.nonclusteredIdx
GO

CREATE TABLE dbo.nonclusteredIdx(
n int NOT NULL,
singleChar char(1) NOT NULL,
stringData char(2000) NOT NULL,
bigIntData bigint NOT NULL,
decimalData decimal(18, 0) NOT NULL
)
GO

-- Ook deze tabel wordt met dezelfde data gevuld
INSERT INTO
dbo.nonclusteredIdx(n, singleChar, stringData, bigIntData, decimalData)
SELECT
n, singleChar, stringData, bigIntData, decimalData
FROM
dbo.pkIndex
GO

-- Vervolgens wordt daar een nonclustered index op gelegd
CREATE UNIQUE NONCLUSTERED INDEX nonclusteredIdx_n
ON dbo.nonclusteredIdx (n, singleChar)
GO

-- Tenslotte wordt er een tabel met beide indexen aangemaakt
IF OBJECT_ID('bothIdx', 'U') IS NOT NULL
DROP TABLE dbo.bothIdx
GO

CREATE TABLE dbo.bothIdx(
n int NOT NULL,
singleChar char(1) NOT NULL,
stringData char(2000) NOT NULL,
bigIntData bigint NOT NULL,
decimalData decimal(18, 0) NOT NULL,
CONSTRAINT PK_bothIdx PRIMARY KEY CLUSTERED (n, singleChar)
)

-- Ook deze tabel wordt met dezelfde data gevuld
INSERT INTO
dbo.bothIdx(n, singleChar, stringData, bigIntData, decimalData)
SELECT
n, singleChar, stringData, bigIntData, decimalData
FROM
dbo.pkIndex
GO

-- De clustered index wordt door de primary key gemaakt
-- Hieronder
CREATE UNIQUE NONCLUSTERED INDEX both_nonclusteredIdx_n
ON dbo.bothIdx (n, singleChar)
GO

Om ervoor te zorgen dat de test goed zichtbaar is, worden de volgende componenten geactiveerd
– Include actual query plan
– Live query statistics
– IO en TIME STATISTICS
– De cache leegmaken (doe dit NIET in productie-omgevingen!!)

Het aanzetten van statistics en het leeggooien van de cache hebben we in opmerkingen blokken geplaatst, om te voorkomen dat deze acties per ongeluk in productie worden uitgevoerd.

/*
SET STATISTICS TIME ON
SET STATISTICS IO ON

DBCC FREEPROCCACHE -- Niet op productie-omgevingen uitvoeren
DBCC DROPCLEANBUFFERS -- Niet op productie-omgevingen uitvoeren
*/

Vervolgens starten we een aantal query’s. Het is belangrijk de resultaten per query te zien, dus de query’s ook los van elkaar te starten. Voor de query worden dekkende indexen (covering indexes) gebruikt, zodat het resultaat zo optimaal mogelijk is. Let op: na elke uitgevoerde query moeten cache en buffers met het DBCC commando worden leeggemaakt om ervoor te zorgen dat de test zo zuiver mogelijk wordt uitgevoerd.

-- Eerst ophalen van data uit de tabel zonder index
select n, singleChar
from dbo.noIndex -- Table 'noIndex'. Scan count 9, logical reads 333334 // CPU time = 0 ms, elapsed time = 0 ms.
where n % 10 = 0

Het resultaat van bovenstaande query is:
– Doorlooptijd is 22 seconden
– Er wordt (uiteraard) een Table Scan gebruikt

-- Vervolgens wordt een test met clustered index gedaan (eerst cache leeggooien!)
select n, singleChar
from dbo.pkIndex -- Table 'pkIndex'. Scan count 1, logical reads 334654 // CPU time = 673 ms, elapsed time = 441 ms
where n % 10 = 0
order by n desc

Het resultaat van bovenstaande query is:
– Doorlooptijd is 36 seconden
– De query wordt met een table scan uitgevoerd

select n, singleChar
from dbo.nonclusteredIdx with(index(nonclusteredIdx_n)) -- Table 'nonclusteredIdx'. Scan count 9, logical reads 333334 // CPU time = 531 ms, elapsed time = 655 ms
where n % 10 = 0

Het resultaat van bovenstaande query is:
– Doorlooptijd is 1 seconde
– De query wordt met de nonclustered index scan uitgevoerd

select n, singleChar
from dbo.bothIdx -- Table 'bothIdx'. Scan count 9, logical reads 340428 // CPU time = 656 ms, elapsed time = 479 ms
where n % 10 = 0

Het resultaat van bovenstaande query is:
– Doorlooptijd is 1 seconde
– De query wordt met de nonclustered index scan uitgevoerd

Conclusie
Het gebruik van nonclustered indexen op bovenstaande tabellen geeft een significante betere performance dan wanneer de clustered index wordt gebruikt. Wanneer u met query performance tuning bezig bent en u heeft last van lange doorlooptijden van query’s, kijk dan eens of de tabel waarop de query draait uitsluitend een clustered index heeft. Is dat het geval, dan is het toevoegen van een nonclustered index een overweging waard. Let er wel op dat het toevoegen van een nonclustered index een prijs kent: bij het UPDATE, DELETE en INSERT query’s moet de extra index wel worden bijgewerkt en dat kost extra tijd. De afweging die u maakt, is of de extra tijd die het toevoegen van data kost, opweegt tegen de extra performance die de nonclustered index oplevert.

SQL Server 2016 “Phones home”

ET Phone Home

Microsoft heeft een nieuwe feature in SQL Server 2016 ingebouwd. Standaard verstuurd SQL Server 2016 rapporten over foutmeldingen en statistieken over het gebruik van SQL Server. Deze optie kunt u voor betaalde versies van SQL Server prima uitzetten. Voor onbetaalde versies zoals de Express en Developer versies van SQL Server is het uitzetten van deze optie er niet.

Meer informatie (helaas alleen in het Engels) via KB item 3153756. Op deze pagina wordt ook beschreven hoe deze optie uitgezet kan worden. Brent Ozar heeft dit Microsoft Connect item aangemaakt om te vragen of de optie ook voor de niet-betaalde versies kan worden uitgezet.

SQL Server Data Tools is gratis!

Wist u dat SQL Server Data Tools gratis is te downloaden en is te gebruiken? Voor de laatste versie is het niet nodig om eerst een licentie te hebben voor de laatste versie van SQL Server. Met andere woorden: om SQL Server Management Studio 2016 of SQL Server Data Tools voor Visual Studio 2015 te gebruiken hoeft u niet over een licentie van SQL Server 2016 te beschikken.

Met de laatste versie van SQL Server Data Tools maakt u net zo makkelijk verbinding met uw eigen versie van SQL Server als met de versie die bij uw SQL Server versie werd geleverd. U maakt met het gebruik van de laatste versie van SQL Server Data Tools gebruik van de laatste Microsoft inzichten. Eén van de nieuwe features van SQL Server Management Studio 2016 die ik heel nuttig vind, is het eenvoudig naast elkaar leggen van query plannen.

Download nu uw laatste versies van SQL Server Data Tools:
SQL Server Management Studio
SQL Server Data Tools

SQL Server: welke editie ondersteunt welke features?

SQL Server biedt een scala aan features. Daarnaast brengt Microsoft niet één SQL Server uit, maar zijn er verschillende edities van SQL Server. Zo kennen we onder andere de ‘standard edition’, de ‘enterprise edition’ en een ‘developers edition’. De ‘standard edition’ heeft ten opzichte van de ‘enterprise edition’ beperkingen. Het is aan u en uw software-ontwikkelaars (of leveranciers) te bepalen welke versie het best bij uw organisatie past. Hieronder volgen links die per SQL Server versie laat zien welke features in welke edities worden ondersteund.

2008R2: Microsoft Technet
2012: Microsoft Technet
2014: Microsoft Technet
2016: Microsoft Technet

Let op: In de SQL Server 2016 lijn geeft Microsoft geen Business Intelligence meer uit.