<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>DE | DBA von Nebenan</title>
	<atom:link href="https://dbavonnebenan.de/tag/de/feed/" rel="self" type="application/rss+xml" />
	<link>https://dbavonnebenan.de</link>
	<description>Sql Server, Performance &#38; PowerShell Automation</description>
	<lastBuildDate>Sun, 14 Jun 2026 20:02:35 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.2</generator>

<image>
	<url>https://dbavonnebenan.de/wp-content/uploads/2025/04/cropped-www_logo-32x32.png</url>
	<title>DE | DBA von Nebenan</title>
	<link>https://dbavonnebenan.de</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>psTerminalPerfCounter &#8211; Performance Counter im Terminal &#8211; HowTo</title>
		<link>https://dbavonnebenan.de/psterminalperfcounter-counter-in-terminal-handson_de/</link>
					<comments>https://dbavonnebenan.de/psterminalperfcounter-counter-in-terminal-handson_de/#respond</comments>
		
		<dc:creator><![CDATA[Gabriel]]></dc:creator>
		<pubDate>Sun, 14 Jun 2026 20:02:34 +0000</pubDate>
				<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[counter]]></category>
		<category><![CDATA[DE]]></category>
		<category><![CDATA[PERFORMANCE]]></category>
		<category><![CDATA[TERMINAL]]></category>
		<guid isPermaLink="false">https://dbavonnebenan.de/?p=714</guid>

					<description><![CDATA[<p>Hands-on zu psTerminalPerfCounter: Windows Performance Counter im Terminal überwachen, inklusive TUI, Multiserver-Monitoring sowie CSV- und HTML-Export.</p>
<p>The post <a href="https://dbavonnebenan.de/psterminalperfcounter-counter-in-terminal-handson_de/">psTerminalPerfCounter – Performance Counter im Terminal – HowTo</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></description>
										<content:encoded><![CDATA[<h1 class="wp-block-heading" style="margin-top:0;margin-bottom:var(--wp--preset--spacing--40)">Intro</h1>



<div class="wp-block-columns are-vertically-aligned-center is-layout-flex wp-container-core-columns-is-layout-461897f4 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50px">
<figure class="wp-block-image size-full is-resized"><img fetchpriority="high" decoding="async" width="294" height="288" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/GitHub_Invertocat_Black.png" alt="Solid black square placeholder with no visible content." class="wp-image-720" style="aspect-ratio:1.0208344636753297;width:51px;height:auto"/></figure>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow">
<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--60)"><strong><a href="https://github.com/gabrielkoehl/psTerminalPerfCounter" target="_blank" rel="noopener" title="">psTerminalPerfCounter</a></strong></p>
</div>
</div>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--40)">Im vorherigen Artikel habe ich einen kurzen Abriss in die <a href="https://dbavonnebenan.de/windows-performance-counters-how-the-os-monitors-itself_de/" target="_blank" rel="noopener" title="">Geschichte und Funktionsweise</a> der Windows Performance Counter gemacht, jetzt folgt das Hands-on. Grundlage ist Release 0.4.1, das ganz im Zeichen von Visualisierung und Datenexport steht. Neu dabei: CSV-Export, eine Terminal GUI als Ablösung der textbasierten Ansicht und der HTML-Export via PSWriteHTML. Klingt spannend&#8230; ist es auch 🙂 Ich denke das siehst du genau so, sonst würdest du nicht mehr lesen. 🙂</p>



<h1 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">Installation</h1>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--40)">Installation eines PowerShell Moduls, nichts besonderes</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# Install psTerminalPerfcounter
Install-Module psTerminalPerfCounter

# List Cmdlets
Get-Command -Module psTerminalPerfCounter

# Install PSWriteHtml, required for HTML Export
Install-Module PSWriteHtml
</pre></div>


<h1 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">Grundlegender Aufbau, Counterconfigs</h1>



<p class="wp-block-paragraph">psTerminalPerfCounter arbeitet mit Konfigurationsdateien, in denen die Counter-Sets und deren Konfiguration beschrieben sind: Counter-IDs, Instanzen (ob zum Beispiel alle CPU-Cores oder nur Core 3 und 4 überwacht werden sollen), Wertkonvertierung, Units und die farbliche Kodierung der Graphen über die sogenannte ColorMap, die allerdings nur die Legacy-Visualisierung unterstützt.</p>



<p class="wp-block-paragraph" style="margin-bottom:var(--wp--preset--spacing--50)">Counterconfigs können in beliebigen Pfaden gespeichert und von dort aufgerufen werden. Alternativ lassen sich Pfade registrieren, was den Aufruf einfach über denConfignamen ermöglicht. Der Default-Pfad ist der Config-Ordner im Modul selbst, der bereits einige vordefinierte Configs mitbringt.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# List all registered paths
Get-tpcConfigPaths

# Add custom path
Add-tpcConfigPath -Path &#039;C:\tpcConfig&#039;
</pre></div>


<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img decoding="async" width="998" height="271" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611184010109.png" alt="" class="wp-image-722" srcset="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611184010109.png 998w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611184010109-300x81.png 300w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611184010109-768x209.png 768w" sizes="(max-width: 998px) 100vw, 998px" /></figure>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# List all Counterconfigs and details in all paths
Test-tpcAvailableCounterConfig
</pre></div>


<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="420" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611184610947-1024x420.png" alt="" class="wp-image-723" srcset="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611184610947-1024x420.png 1024w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611184610947-300x123.png 300w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611184610947-768x315.png 768w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611184610947.png 1273w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<h1 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">Single Server Monitoring</h1>



<p class="wp-block-paragraph" style="margin-top:0;margin-bottom:var(--wp--preset--spacing--40)">Die schnellste Methode ist ein einzelner Server, lokal oder remote.</p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# Use CPU Config locally
Start-tpcMonitor -Configname CPU
Start-tpcMonitor -Confipath &#039;C:\Users\labadmin\Documents\PowerShell\Modules\psterminalperfcounter\0.4.0\Config\tpc_CPU.json&#039;

# Use CPU Config remote
Start-tpcMonitor -Computername &#039;dev-node1&#039; -Configname CPU
Start-tpcMonitor -Computername &#039;dev-node1&#039; -Confipath &#039;C:\Users\labadmin\Documents\PowerShell\Modules\psterminalperfcounter\0.4.0\Config\tpc_CPU.json&#039;

# Use CPU Config rmeote with different credentials
Start-tpcMonitor -Computername &#039;dev-node1&#039; -Credentials $(Get-Credential) -Configname CPU
</pre></div>


<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1022" height="773" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611185126524.png" alt="" class="wp-image-724" srcset="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611185126524.png 1022w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611185126524-300x227.png 300w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611185126524-768x581.png 768w" sizes="auto, (max-width: 1022px) 100vw, 1022px" /></figure>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--40)">Da die Legacy-Visualisierung spätestens bei Multiserver-Umgebungen an ihre Grenzen kommt, nutze ich für dieses Hands-on den <code>-TUI</code> Switch.</p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# Use CPU Config locally with TUI
Start-tpcMonitor -Configname CPU -TUI
</pre></div>


<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="562" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611190144159-1024x562.png" alt="" class="wp-image-725" srcset="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611190144159-1024x562.png 1024w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611190144159-300x165.png 300w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611190144159-768x422.png 768w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611190144159.png 1483w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h1 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">Multi Server Monitoring</h1>



<p class="wp-block-paragraph">Das Multiserver-Monitoring funktioniert ebenfalls über Konfigurationsdateien, wobei mir beim Schreiben dieser Zeilen auffällt: Warum habe ich nie daran gedacht, einfach eine Counterconfig gegen eine Serverliste laufen zu lassen? Ab ins Backlog damit. Wie auch immer, in der Environment-Konfiguration lassen sich beliebig viele Server mit verschiedenen Configs definieren. Da ich zuletzt noch Probleme hatte, die Sparklines in der TUI scrollbar zu machen, sind diese in der Multiserver-Ansicht deaktiviert.</p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# Start multi server monitoring
Start-tpcEnvironmentMonitor -EnvConfigPath &#039;C:\tpcConfig\ENV_SERVER_EXAMPLE.json&#039; -TUI
</pre></div>


<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="389" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611191606127-1024x389.png" alt="" class="wp-image-726" srcset="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611191606127-1024x389.png 1024w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611191606127-300x114.png 300w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611191606127-768x292.png 768w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611191606127.png 1472w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph"><em>LAB-NODE03 ist in dem Fall ein deutsche Installation</em></p>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:0">Die Spalte Duration zeigt die Zeit, die die Funktion auf dem jeweiligen Server gebraucht hat, um alle Werte einzusammeln. Den finalen Timestamp des gesamten Datensatzes definiert immer der langsamste Server, nur so ist eine homogene Zeitreihe möglich. Eine Varianz von in diesem Fall 1,3 Sekunden lässt sich dabei nicht vermeiden.</p>



<p class="wp-block-paragraph">Das wars auch schon zur Auführung, da dieses Modul nur das Framework bereitstellt. Das eigentliche Leben kommt mit den Counterconfigs. Wie diese erstellt werden schauen wir uns jetzt an.</p>



<h1 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">Erstellung von Counterconfigs</h1>



<p class="wp-block-paragraph">Am Anfang ist der Performance Counter. Bevor wir die Konfiguration schreiben können, brauchen wir die Counter-IDs statt der lokalisierten Namen. Dafür gibt es <code>Get-tpcPerformanceCounterInfo</code>, das in beide Richtungen funktioniert: Counterpath zu ID und umgekehrt.</p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# List all counter and counter objects containing &#039;processor time&#039;
Get-tpcPerformanceCounterInfo -ComputerName &#039;dev-node1&#039; -SearchTerm &#039;processor time&#039;
</pre></div>


<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="973" height="297" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612082832650.png" alt="" class="wp-image-727" srcset="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612082832650.png 973w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612082832650-300x92.png 300w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612082832650-768x234.png 768w" sizes="auto, (max-width: 973px) 100vw, 973px" /></figure>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:0">Wichtig sind die letzten beiden Spalten. Es gibt Single-Instance-Counter mit nur einem Wert, aber auch Multi-Instance-Counter wie das Processor-Counterset, bei dem jede Instanz einem Core entspricht, oder halt gesamt (<code>_Total</code>).</p>



<p class="wp-block-paragraph">Genauso lässt sich mit dem Cmdlet herausfinden, welcher Counter sich hinter einer Composite-ID verbirgt.</p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# Show counter name of composite ID
Get-tpcPerformanceCounterInfo -ComputerName &#039;dev-node1&#039; -SearchTerm &#039;238-6&#039;
</pre></div>


<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="911" height="105" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612083119093.png" alt="" class="wp-image-728" srcset="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612083119093.png 911w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612083119093-300x35.png 300w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612083119093-768x89.png 768w" sizes="auto, (max-width: 911px) 100vw, 911px" /></figure>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--40)">Oder auf einem deutschen System</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="904" height="115" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612083431840.png" alt="" class="wp-image-729" srcset="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612083431840.png 904w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612083431840-300x38.png 300w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612083431840-768x98.png 768w" sizes="auto, (max-width: 904px) 100vw, 904px" /></figure>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:0">Nehmen wir noch Counter, die nicht standardmäßig auf einem Windows-System vorhanden sind: SQL Server. DEV22A ist in diesem Fall die erste Instanz auf dem System.</p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# &quot;Out-String -Stream&quot; converts PowerShell&#039;s internal formatting objects into actual strings line by 
# line, enabling string operations like `Select-String` to work on them.

Get-tpcPerformanceCounterInfo -ComputerName &#039;dev-node1&#039; -SearchTerm &#039;dev22a&#039; | 
		Out-String -Stream | 
		Select-String &quot;lookup&quot;
</pre></div>


<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="117" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612081940564-1024x117.png" alt="" class="wp-image-730" srcset="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612081940564-1024x117.png 1024w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612081940564-300x34.png 300w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612081940564-768x88.png 768w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612081940564.png 1235w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:0">Nehmen wir zum Testen <code>Page lookups/sec</code> &#8211;&gt; 12222-12272</p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
Get-tpcPerformanceCounterInfo -ComputerName &#039;dev-node1&#039; -SearchTerm &#039;dev22a&#039; | 
		Out-String -Stream | 
		Select-String &quot;log flushes&quot;
</pre></div>


<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="120" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612082340599-1024x120.png" alt="" class="wp-image-731" srcset="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612082340599-1024x120.png 1024w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612082340599-300x35.png 300w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612082340599-768x90.png 768w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612082340599.png 1471w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--40)">Und <code>log flush/sec</code> auf der Instanz TempDB &#8211;&gt; 12352-12446</p>



<p class="wp-block-paragraph">Die minimale Config sieht dann so aus. Bei den SQL-Server-Countern fehlen einige Werte, das ist aber nicht SQL-Server-spezifisch, sondern zeigt, dass die meisten Felder Defaults bekommen. Details dazu finden sich in der Doku im Repo. Erwähnenswert noch: bei CPU nutze ich <code>"counterInstance": "1|2"</code>, wodurch automatisch zwei Counter erstellt werden, und bei den Page Lookups die Konvertierung durch 1000, damit die Zahlen lesbar bleiben.\</p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
{
	&quot;name&quot;: &quot;CPU and Sql Server Page Usage&quot;,
	&quot;description&quot;: &quot;CPU Usage&quot;,
	&quot;counters&quot;: &#x5B;
		{
			&quot;title&quot;: &quot;CPU Usage&quot;,
			&quot;unit&quot;: &quot;%&quot;,
			&quot;conversionFactor&quot;: 1,
			&quot;conversionExponent&quot;: 1,
			&quot;conversionType&quot;: &quot;D&quot;,
			&quot;format&quot;: &quot;table&quot;,
			&quot;counterID&quot;: &quot;238-6&quot;,
			&quot;counterSetType&quot;: &quot;MultiInstance&quot;,
			&quot;counterInstance&quot;: &quot;0|1&quot;,
			&quot;colorMap&quot;: {
				&quot;20&quot;: &quot;Green&quot;,
				&quot;50&quot;: &quot;Yellow&quot;,
				&quot;70&quot;: &quot;RED&quot;
			}
		},
		{
			&quot;title&quot;: &quot;Sql DEV22A Page Lookups/s&quot;,
			&quot;unit&quot;: &quot;#K&quot;,
			&quot;conversionFactor&quot;: 1000,
			&quot;conversionExponent&quot;: 1,
			&quot;conversionType&quot;: &quot;D&quot;,
			&quot;counterID&quot;: &quot;12222-12272&quot;,
			&quot;counterSetType&quot;: &quot;SingleInstance&quot;,
			&quot;counterInstance&quot;: &quot;&quot;
		},
		{
			&quot;title&quot;: &quot;Sql DEV22A TempDB Log Flushes/s&quot;,
			&quot;unit&quot;: &quot;#&quot;,
			&quot;counterID&quot;: &quot;12352-12446&quot;,
			&quot;counterSetType&quot;: &quot;MultiInstance&quot;,
			&quot;counterInstance&quot;: &quot;tempdb&quot;
		}

	]
}
</pre></div>


<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph" style="margin-top:0;margin-bottom:var(--wp--preset--spacing--40)">Das Ganze dann in einem registrierten Pfad speichern mit dem Präfix <code>tpc_NAME.json</code> und los geht&#8217;s.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="404" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612090849880-1024x404.png" alt="" class="wp-image-732" srcset="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612090849880-1024x404.png 1024w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612090849880-300x118.png 300w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612090849880-768x303.png 768w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612090849880-1536x605.png 1536w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612090849880.png 1713w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h1 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">Monitoring einer Umgebung mit Environment File</h1>



<p class="wp-block-paragraph">Auch hier keine Magic. Es können Confignamen oder Pfade angegeben werden. Eine noch nicht vollständig dokumentierte Funktion ist die Nutzung von SecretVaults: Über die PowerShell-Konnektoren lassen sich Credentials aus fast allen gängigen Lösungen laden, womit auch Nicht-Admins die Möglichkeit gegeben werden kann, Systeme zu überwachen.</p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
&#x5B;
    {
        &quot;name&quot;: &quot;SQL_ENVIRONMENT_001&quot;,
        &quot;description&quot;: &quot;SQL Server Environment Production&quot;,
        &quot;interval&quot;: 2,
        &quot;secretvaultname&quot;: &quot;SecretStore&quot;,
        &quot;credentialname&quot;: &quot;integrated&quot;,
        &quot;servers&quot;: &#x5B;
            {
                &quot;computername&quot;: &quot;DEV-NODE1&quot;,
                &quot;comment&quot;: &quot;Sql Server Node A Production&quot;,
                &quot;counterConfig&quot;: &#x5B;
                    &quot;SQLDEMO&quot;
                ]
            },
            {
                &quot;computername&quot;: &quot;DEV-NODE2&quot;,
                &quot;comment&quot;: &quot;Sql Server Node B Production&quot;,
                &quot;counterConfig&quot;: &#x5B;
                    &quot;C:\Users\devadmin\Desktop\tpc_SQLDEMO.json&quot;
                ]
            }
        ]
    }
]
</pre></div>


<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# without TUI
Start-tpcEnvironmentMonitor -EnvConfigPath &quot;C:\Users\devadmin\Desktop\ENV_SERVER_EXAMPLE2.json&quot;
</pre></div>


<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="277" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612092052078-1024x277.png" alt="" class="wp-image-733" srcset="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612092052078-1024x277.png 1024w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612092052078-300x81.png 300w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612092052078-768x208.png 768w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612092052078.png 1442w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h1 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">Datenexport</h1>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">CSV</h2>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="299" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612092726885-1024x299.png" alt="" class="wp-image-734" srcset="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612092726885-1024x299.png 1024w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612092726885-300x88.png 300w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612092726885-768x224.png 768w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612092726885.png 1129w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--40)">Um die Zeitreihen weiterzuverwenden, kann nach CSV exportiert werden. Achtung, aktuell noch ein bekannter Bug: der Export ist kommasepariert, Werte mit Komma werden dadurch ebenfalls getrennt, in 0.4.1 gefixt.</p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# -CsvPath is default desktop
	Start-tpcEnvironmentMonitor -EnvConfigPath &quot;C:\Users\devadmin\Desktop\ENV_SERVER_EXAMPLE2.json&quot; -ExportCsv
</pre></div>


<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<h1 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">HTML Report</h1>



<p class="wp-block-paragraph">Eine alternative Visualisierung als HTML, nützlich wenn ich eine Zeitreihe weitergeben möchte ohne Excel oder weitere Tools zu bemühen. Gruppiert werden kann nach Counter oder nach Server, je nachdem ob ich bei mehreren Servern alle Counter eines Servers in einem Diagramm sehen möchte, oder einen bestimmten Counter über mehrere Server hinweg. Details dazu finden sich in den Docs im Repo.</p>



<p class="wp-block-paragraph">Zu beachten: das HTML enthält ein rollendes Datenset, das an die maximale Sample-Anzahl gebunden ist. Je mehr Samples, desto träger wird das HTML. Für große Zeitreihen lieber CSV nutzen.</p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# -HTMLPath is default desktop
	Start-tpcEnvironmentMonitor -EnvConfigPath &quot;C:\Users\devadmin\Desktop\ENV_SERVER_EXAMPLE2.json&quot; -ExportHTML
</pre></div>


<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="339" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612093443448-1024x339.png" alt="" class="wp-image-735" srcset="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612093443448-1024x339.png 1024w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612093443448-300x99.png 300w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612093443448-768x254.png 768w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612093443448-1536x508.png 1536w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260612093443448-2048x678.png 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-461897f4 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-container-core-column-is-layout-cc415327 wp-block-column-is-layout-flow" style="flex-basis:20%">
<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="295" height="294" src="https://dbavonnebenan.de/wp-content/uploads/2025/04/2025-04-21_16-19.png" alt="" class="wp-image-113" style="width:157px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/04/2025-04-21_16-19.png 295w, https://dbavonnebenan.de/wp-content/uploads/2025/04/2025-04-21_16-19-150x150.png 150w" sizes="auto, (max-width: 295px) 100vw, 295px" /></figure>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<p class="wp-block-paragraph">Das war&#8217;s als Schnelleinstieg, mehr Details zu den einzelnen Funktionen finden sich wie immer in den Docs und der <a href="https://github.com/gabrielkoehl/psTerminalPerfCounter/blob/main/README.md" title="">README </a>im Repository.</p>
</div>
</div><p>The post <a href="https://dbavonnebenan.de/psterminalperfcounter-counter-in-terminal-handson_de/">psTerminalPerfCounter – Performance Counter im Terminal – HowTo</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://dbavonnebenan.de/psterminalperfcounter-counter-in-terminal-handson_de/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Windows Performance Counter: Wie sich das OS selbst überwacht</title>
		<link>https://dbavonnebenan.de/windows-performance-counters-how-the-os-monitors-itself_de/</link>
					<comments>https://dbavonnebenan.de/windows-performance-counters-how-the-os-monitors-itself_de/#respond</comments>
		
		<dc:creator><![CDATA[Gabriel]]></dc:creator>
		<pubDate>Sun, 14 Jun 2026 20:01:30 +0000</pubDate>
				<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[counter]]></category>
		<category><![CDATA[DE]]></category>
		<category><![CDATA[PERFORMANCE]]></category>
		<category><![CDATA[TERMINAL]]></category>
		<guid isPermaLink="false">https://dbavonnebenan.de/?p=686</guid>

					<description><![CDATA[<p>Performance Counter sind seit Windows NT 3.1 ein fester Bestandteil des Betriebssystems. In diesem Artikel erkläre ich kurz, wie das System intern funktioniert, warum Counter-Namen sprachabhängig sind und wie mein PowerShell Modul psTerminalPerfCounter genau dieses Problem löst.</p>
<p>The post <a href="https://dbavonnebenan.de/windows-performance-counters-how-the-os-monitors-itself_de/">Windows Performance Counter: Wie sich das OS selbst überwacht</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></description>
										<content:encoded><![CDATA[<h1 class="wp-block-heading" style="margin-bottom:var(--wp--preset--spacing--40)">Intro</h1>



<div class="wp-block-columns are-vertically-aligned-center is-layout-flex wp-container-core-columns-is-layout-461897f4 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50px">
<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="294" height="288" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/GitHub_Invertocat_Black.png" alt="Solid black square placeholder with no visible content." class="wp-image-720" style="aspect-ratio:1.0208344636753297;width:51px;height:auto"/></figure>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow">
<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--60)"><strong><a href="https://github.com/gabrielkoehl/psTerminalPerfCounter" target="_blank" rel="noopener" title="">psTerminalPerfCounter</a></strong></p>
</div>
</div>



<p class="wp-block-paragraph">Knapp ein Jahr ist es nun her, dass ich mit der Entwicklung von psTerminalPerfCounter begann. Stolz auf die &#8222;grafische&#8220; Implementierung, Sprachunabhängigkeit mittels 3rd-Party-Lib und den damaligen heiligen Gral: Klassen in PowerShell.</p>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--40)">Nichts davon ist geblieben:</p>



<ul class="wp-block-list">
<li>Grafische Implementierung? Schön bunt aber&#8230;
<ul class="wp-block-list">
<li>Bei Multiserver Umgebungen träge wie ein Stein</li>



<li>Dynamisch und Skalierbar, Fehlanzeige</li>



<li>Neuimplementierung in TUI (BETA)<br></li>
</ul>
</li>



<li>Multilanguage per 3rd Party Lib
<ul class="wp-block-list">
<li>Geht alles direkt per Windows Registry, durch Zufall darüber gestolpert<br></li>
</ul>
</li>



<li>Klassen in PowerShell? Sehr schlechte Entscheidung.
<ul class="wp-block-list">
<li>Nicht praktikabel, Instanzen nicht ohne weiteres serialisierbar, Methoden gehen verloren, Parallel Remoting mit Runspaces kaum umsetzbar</li>



<li>Rebuild in C#</li>
</ul>
</li>
</ul>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--50)">Mit dem aktuellen Release 0.4.1 sind erst mal alle Funktionen implementiert, welche ich im Backlog hatte. Da mir die nächsten Monate dazu die Zeit für dieses Projekt fehlen wird, ist dies ein guter Moment ein <a href="/psterminalperfcounter-counter-in-terminal-handson_de" target="_blank" rel="noopener" title="">HandsOn</a> zu schreiben, da die README.md im Repo doch manchmal herausfordernd ist&#8230; wie es scheint 🙂 Bevor es zum <a href="http://\/psterminalperfcounter-counter-in-terminal-handson_de" target="_blank" rel="noopener" title="">HandsOn</a> geht, aber erst mal ein wenig zur Technik und Geschichte der Performance Counter.</p>



<h1 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">Geschichte</h1>



<p class="wp-block-paragraph">Windows Performance Counter gibt es in Windows seit <strong>NT 3.1 (1993)</strong>, da hatte ich noch nicht mal einen Computer. Microsoft brauchte damals eine standardisierte Methode, um Systemzustände anzeigbar zu machen, ohne dass jede Anwendung eigene Monitoring-Mechanismen implementieren musste. Das hieẞ aber auch Messwerte aus der Registry kratzen und Deltas berechnen.</p>



<p class="wp-block-paragraph">Mit <strong>Windows 2000</strong> kam der <em>Performance Data Helper</em> (PDH) als komfortable High-Level-API dazu, die den Zugriff auf Performance Counter so gestaltet, wie wir ihn heute kennen. Gleichzeitig wurde WMI eingeführt, das Performance-Daten über eine objektorientierte Schnittstelle zugänglich machte. CIM war ja nicht proprietär und zu kompatibel 😛</p>



<p class="wp-block-paragraph"><strong>Windows Vista</strong> brachte das überarbeitete <strong>Provider V2-Modell</strong>: Statt einer DLL, die vom System in einen fremden Prozess geladen wurde (fehleranfällig, Absturz des Providers = Absturz des abfragenden Prozesses), registrieren sich Provider seitdem als eigenständige ausführbare Dateien oder Dienste. Das ist das heute noch gültige Modell.</p>



<h1 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">Aufbau und Funktion</h1>



<figure class="wp-block-image aligncenter size-full is-resized" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--60)"><img loading="lazy" decoding="async" width="1241" height="825" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/Screenshot_20260611_102741.png" alt="Diagram of the Applications &amp; Tools stack: PDH and WMI/Perflib feed into Registry, then Performance Counter Provider, then Kernel &amp; Drivers and Usermode Processes." class="wp-image-694" style="width:676px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2026/06/Screenshot_20260611_102741.png 1241w, https://dbavonnebenan.de/wp-content/uploads/2026/06/Screenshot_20260611_102741-300x199.png 300w, https://dbavonnebenan.de/wp-content/uploads/2026/06/Screenshot_20260611_102741-1024x681.png 1024w, https://dbavonnebenan.de/wp-content/uploads/2026/06/Screenshot_20260611_102741-768x511.png 768w" sizes="auto, (max-width: 1241px) 100vw, 1241px" /></figure>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">Metadaten in der Registry</h2>



<p class="wp-block-paragraph" style="margin-bottom:var(--wp--preset--spacing--40)">Alle bekannten Counter sind unter <code>HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib</code> registriert. Dort liegen Name, Beschreibung und eine numerische Index-Nummer pro Counter, getrennt nach Sprache (Unterkey <code>009</code> für Englisch, <code>007</code> für Deutsch usw.). </p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# List available language packs (counter IDs)
Get-ChildItem &quot;HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib&quot;
</pre></div>


<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:0"><img loading="lazy" decoding="async" width="1012" height="441" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611100323632.png" alt="Registry view of Perflib under HKEY_LOCAL_MACHINE showing entries like 009 and _V2Providers with Counter/Help text for Windows performance counters in a dark UI." class="wp-image-690" srcset="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611100323632.png 1012w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611100323632-300x131.png 300w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611100323632-768x335.png 768w" sizes="auto, (max-width: 1012px) 100vw, 1012px" /></figure>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">Provider liefern die Rohdaten</h2>



<p class="wp-block-paragraph" style="margin-bottom:var(--wp--preset--spacing--40)">Jede Komponente (Kernel, Treiber, Dienste wie IIS oder SQL Server, die .NET-Runtime) kann eigene Counter registrieren. Der Provider wartet passiv und liefert Daten erst dann, wenn sie jemand abfragt. Es gibt keinen dauerhaften Sammeldienst im Hintergrund.</p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# List all services that have a Performance subkey

Get-ChildItem &quot;HKLM:\SYSTEM\CurrentControlSet\Services&quot; |
    Where-Object { Test-Path &quot;$($_.PSPath)\Performance&quot; } |
    ForEach-Object {
        $perf = Get-ItemProperty &quot;$($_.PSPath)\Performance&quot;
        &#x5B;PSCustomObject]@{
            Service      = $_.PSChildName
            FirstCounter = $perf.&#039;First Counter&#039;
            LastCounter  = $perf.&#039;Last Counter&#039;
            Library      = $perf.Library
        }
    } | Format-Table -AutoSize
</pre></div>


<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image aligncenter size-full" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--40)"><img loading="lazy" decoding="async" width="1229" height="650" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611100323687.png" alt="Console output listing Windows services with counters and DLL paths (names like .NET CLR Data, IIS, SQL Server) and their FirstCounter/LastCounter values." class="wp-image-691" srcset="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611100323687.png 1229w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611100323687-300x159.png 300w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611100323687-1024x542.png 1024w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611100323687-768x406.png 768w" sizes="auto, (max-width: 1229px) 100vw, 1229px" /></figure>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">APIs für Anwendungen (CONSUMER)</h2>



<ul class="wp-block-list">
<li><strong>PDH</strong> (Performance Data Helper): Die bequeme API, die Werte bereits formatiert (z. B. Prozentwerte berechnet, Intervalle berücksichtigt). <code>Get-Counter</code> in PowerShell nutzt PDH.</li>



<li style="margin-top:0;margin-bottom:0"><strong>PerfLib / Registry direkt</strong>: Schneller, aber roh. WMI nutzt diesen Weg intern.</li>
</ul>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--40)"><strong>Aufbau</strong></p>



<p class="wp-block-paragraph">   \Prozessor(_Total)\Prozessorzeit (%)<br>   └─ Objekt: &#8222;Prozessor&#8220;<br>   └─ Instanz: &#8222;_Total&#8220; (Summe aller Kerne)<br>   └─ Counter: &#8222;Prozessorzeit (%)&#8220;</p>



<h1 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">Microsofts Tools</h1>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">Der Großvater PerfMon</h2>



<p class="wp-block-paragraph">Der Performance Monitor ist seit NT-Zeiten das offizielle Werkzeug von Microsoft und für das, wofür er gebaut wurde, nach wie vor ausgezeichnet: interaktive, grafische Tiefenanalyse einzelner Systeme, mit Aufzeichnung über Data Collector Sets, CSV-Export via <code>relog</code> und grundlegendem Remoting. Die Grenzen zeigen sich, sobald man skalieren, automatisieren oder schlicht im Terminal arbeiten will. Portable Konfigurationsdateien, mehrere Server gleichzeitig im Blick oder ein strukturierter Konsolenworkflow sind nicht seine Stärken.</p>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">Der NewComer Windows Admin Center</h2>



<p class="wp-block-paragraph">Microsofts browserbasierte Verwaltungsoberfläche bringt ebenfalls eine Performance-Ansicht mit, übersichtlicher als Perfmon und ohne lokale Installation auf dem verwalteten System. Für einen schnellen Blick auf CPU, Speicher und Netzwerk reicht das völlig aus. Zwei Punkte schränken den praktischen Nutzen jedoch ein: Erstens setzt WAC eine zentrale Installation voraus, mal eben auf einem Client oder beim Kunden starten geht nicht. Zweitens basiert die Counter-Auswahl wie überall auf der lokalisierten PDH-API, was Workspaces nicht wirklich transportabel macht.</p>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">Das Evergreen &#8211; Get-Counter</h2>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--40)"><code>Get-Counter</code> ist das PowerShell-Äquivalent für den Konsolenbetrieb und damit das flexibelste der drei Werkzeuge, solange man <del>frickeln möchte</del> bereit ist, selbst Hand anzulegen. Counter lassen sich direkt abfragen, per Schleife wiederholen und in die Pipeline schicken. Remoting funktioniert über den <code>-ComputerName</code>-Parameter. Das Problem ist dasselbe wie überall: Die Counter-Pfade sind sprachabhängig, ein auf einem deutschen System geschriebenes Skript schlägt auf einem englischen fehl. Hinzu kommt, dass <code>Get-Counter</code> Rohdaten liefert. Formatierung, Schwellenwerte, Verlaufsdarstellung und strukturierte Ausgabe sind vollständig Handarbeit. Für Einmalabfragen und schnelle Checks ist es ideal, für reproduzierbare, mehrsprachige Umgebungen stößt es schnell an seine Grenzen.</p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# Query CPU, memory and disk counters every 5 seconds, format output as table
$counters = @(
    &#039;\Processor(_Total)\% Processor Time&#039;,
    &#039;\Memory\Available MBytes&#039;,
    &#039;\PhysicalDisk(_Total)\Disk Bytes/sec&#039;
)

# German Counternames
# $counters = @( 
#	&#039;\Processor(_Total)\% Processor Time&#039;, 
#	&#039;\Memory\Available MBytes&#039;, 
#	&#039;\PhysicalDisk(_Total)\Disk Bytes/sec&#039; 
# )

Get-Counter -Counter $counters -SampleInterval 5 -Continuous | ForEach-Object {
    $sample = $_.CounterSamples

    &#x5B;PSCustomObject]@{
        Timestamp       = $_.Timestamp
        &#039;CPU %&#039;         = &#x5B;math]::Round(($sample | Where-Object Path -like &#039;*processor*&#039;).CookedValue, 1)
        &#039;RAM frei (MB)&#039; = &#x5B;math]::Round(($sample | Where-Object Path -like &#039;*available*&#039;).CookedValue, 0)
        &#039;Disk B/s&#039;      = &#x5B;math]::Round(($sample | Where-Object Path -like &#039;*disk bytes*&#039;).CookedValue, 0)
    } | Format-Table -AutoSize
}
</pre></div>


<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image aligncenter size-full" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--40)"><img loading="lazy" decoding="async" width="604" height="546" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611100323778.png" alt="Series of monitor entries showing time stamps, CPU usage percentages, free RAM (MB), and disk throughput (B/s) across samples." class="wp-image-692" srcset="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611100323778.png 604w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611100323778-300x271.png 300w" sizes="auto, (max-width: 604px) 100vw, 604px" /></figure>



<h1 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">Der Weg in die Sprachunabhängigkeit</h1>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--40)">Wie ihr seht, ist die Sprachabhängigkeit bei allen Implementierungen ein großes Problem. Dabei ist das gar nicht nötig. Unter der Haube hat jedes Counterobjekt und jeder Counter eine eindeutige ID. Schaut mal oben in den Screenshots, da steht immer First und Last Counter. Somit wird aus <code>\Processor(_Total)\Processor Time (%)</code> &#8211;&gt; <code>\238(_Total)\6</code> .</p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# Read counter ID/name mapping from registry (English - key 009, German - key 007)
Get-ItemProperty -Path &#039;HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\009&#039; -Name Counter |
		Select-Object -ExpandProperty Counter | Select-Object -First 20
</pre></div>


<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image aligncenter size-full" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--40)"><img loading="lazy" decoding="async" width="658" height="498" src="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611100323832.png" alt="Side-by-side terminal screenshots showing performance metrics in English (left) and German (right). Left window lists System, Memory, % Processor Time, File Read/Write Operations/sec, File Control Operations/sec, File Read Bytes/sec, File Write Bytes/sec, and a path; right window shows equivalent German terms like Arbeitsspeicher, Prozessorzeit, Lesevorgänge/s, Schreibvorgänge/s, Dateisteuerungen/s, Bytes gelesen/s, Bytes geschrieben/s, etc." class="wp-image-693" srcset="https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611100323832.png 658w, https://dbavonnebenan.de/wp-content/uploads/2026/06/IMG-20260611100323832-300x227.png 300w" sizes="auto, (max-width: 658px) 100vw, 658px" /></figure>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--70)">That&#8217;s it. Nun müssen einmal das Counter-Objekt und der Pfad übersetzt werden und schon habe ich meinen sprachunabhängigen CounterPfad. Vor dem Ausführen wird er auf dem Zielsystem übersetzt und dann <code>Get-Counter -Counter &lt;TRANSLATION&gt;</code> ausgeführt. Das übernimmt in psTerminalPerfcounter das CMDLET <a href="https://github.com/gabrielkoehl/psTerminalPerfCounter/blob/main/src/psTerminalPerfCounter/psTerminalPerfCounter/Public/Get-tpcPerformanceCounterInfo.ps1" target="_blank" rel="noreferrer noopener"><strong>get-tpcPerformanceCounterInfo</strong></a></p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-461897f4 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-container-core-column-is-layout-cc415327 wp-block-column-is-layout-flow" style="flex-basis:20%">
<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="284" height="334" src="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_3.png" alt="" class="wp-image-355" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_3.png 284w, https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_3-255x300.png 255w" sizes="auto, (max-width: 284px) 100vw, 284px" /></figure>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">https://dbavonnebenan.de/psterminalperfcounter-counter-in-terminal-handson_de/</div>
</div><p>The post <a href="https://dbavonnebenan.de/windows-performance-counters-how-the-os-monitors-itself_de/">Windows Performance Counter: Wie sich das OS selbst überwacht</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://dbavonnebenan.de/windows-performance-counters-how-the-os-monitors-itself_de/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SQL Server HA: AD, SPN und Cluster Setup automatisieren</title>
		<link>https://dbavonnebenan.de/automate-sql-server-ha-ad-spn-and-cluster-setup_de/</link>
					<comments>https://dbavonnebenan.de/automate-sql-server-ha-ad-spn-and-cluster-setup_de/#respond</comments>
		
		<dc:creator><![CDATA[Gabriel]]></dc:creator>
		<pubDate>Fri, 06 Feb 2026 11:20:00 +0000</pubDate>
				<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[Sql Server]]></category>
		<category><![CDATA[AD]]></category>
		<category><![CDATA[AlwaysOn]]></category>
		<category><![CDATA[DE]]></category>
		<category><![CDATA[Deployment]]></category>
		<category><![CDATA[GMSA]]></category>
		<category><![CDATA[SPN]]></category>
		<category><![CDATA[WSFC]]></category>
		<guid isPermaLink="false">https://dbavonnebenan.de/?p=641</guid>

					<description><![CDATA[<p>Production-Ready Windows Cluster Deployment: Automatisierung von Failover Cluster, gMSA, Kerberos und lokalen Rechten via PowerShell.</p>
<p>The post <a href="https://dbavonnebenan.de/automate-sql-server-ha-ad-spn-and-cluster-setup_de/">SQL Server HA: AD, SPN und Cluster Setup automatisieren</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></description>
										<content:encoded><![CDATA[<div style="border: 1px dotted #eb2b14;border-radius: 6px;background-color: #eeeeee;padding-top: var(--wp--preset--spacing--30);padding-left: var(--wp--preset--spacing--30);padding-right: var(--wp--preset--spacing--30);padding-bottom: var(--wp--preset--spacing--30)" class="ub-styled-box ub-bordered-box wp-block-ub-styled-box" id="ub-styled-box-8bf7c8f5-ed36-44d1-82b0-6849bd1507a5">
<p class="has-open-sauce-sans-font-family wp-block-paragraph" style="margin-top:0;margin-right:0;margin-bottom:0;margin-left:0" id="ub-styled-box-bordered-content-">Was erwartet dich hier?</p>



<ul style="margin-top:0;margin-right:0;margin-bottom:0;margin-left:0" class="wp-block-list has-open-sauce-sans-font-family">
<li><a href="#WSFC" title="">Windows Server Failover Cluster Deployment mit erweiterten AD- und DNS-Berechtigungen</a></li>



<li><a href="#GMSA" title="">Identity Management: Group Managed Service Accounts (gMSA)</a></li>



<li><a href="#KERBEROS" title="">Kerberos und Service Principal Names (SPN)</a></li>



<li><a href="#LSP" title="">Lokale Sicherheitsrichtlinien (Local Security Policies)</a></li>



<li>Hintergrundwissen warum Konfigurationen gemacht werden wie sie gemacht werden</li>
</ul>


</div>


<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>


https://github.com/gabrielkoehl/DBAScriptBox


<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" id="0-intro" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Intro</h2>



<p class="wp-block-paragraph">Wer kennt es nicht? Bevor man überhaupt mit der Installation von SQL Server loslegen kann, hat man sich entweder repetitiv durch seitenweise schlechte GUIs geklickt oder wenn verfügbar, mit einer losen Sammlung an Scripten jongliert und semiautomatisch alle Abhängigkeiten und Best Practices eingerichtet.</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-461897f4 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:80%">
<p class="wp-block-paragraph">Noch schlimmer ist der Fall, wenn man als Berater gar keinen Domain Admin hat (was auch so sein sollte) und man den Kunden, der dies vielleicht noch nie gemacht hat, per Teams &amp; Co anleiten muss. Wenn dies nicht eh eine geplante Installation mit Know-how-Transfer ist, erzeugt so etwas Schmerz. <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f92d.png" alt="🤭" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:20%">
<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="264" height="329" src="https://dbavonnebenan.de/wp-content/uploads/2025/06/emote-4.png" alt="PAIN Emote" class="wp-image-516" style="aspect-ratio:0.8024665981500514;width:75px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/06/emote-4.png 264w, https://dbavonnebenan.de/wp-content/uploads/2025/06/emote-4-241x300.png 241w" sizes="auto, (max-width: 264px) 100vw, 264px" /></figure>
</div>
</div>



<p class="wp-block-paragraph">Dementsprechend habe ich nun mal all meine losen Scripte zusammengewürfelt und für jeden Themenbereich eine recht umfangreiche Funktion entwickelt, welche all diese Tasks automatisiert und für Archivzwecke loggt. Danke auch an Claude für die Unterstützung. Keiner kontrolliert und dokumentiert Code besser als Claude. <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f60b.png" alt="😋" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p class="wp-block-paragraph">Nun könnte man sich fragen: Es gibt doch dbatools und DSC? Klar, nutze ich auch gerne. Aber transparente, nachvollziehbare Scripts ohne Framework-Overhead vermitteln Wissen besser &#8211; gerade wenn angehenden DBAs der Blick fürs Ganze fehlt. Betrieb von SQL Server fängt auf dem Blech an, geht über Storage, Windows Server, Active Directory und hört bei einer Certificate Authority auf. Und bei KRITIS-Unternehmen oder kleinen Mittelständlern mit 3 Clustern will ich zu Beginn keine Grundsatzdiskussion über dbatools oder DSC führen.</p>



<p class="wp-block-paragraph">Los geht&#8217;s mit einem Production-Ready Windows Server Failover Cluster &#8211; inklusive CNO/VCO-Berechtigungen, DNS-Registrierung und Quorum-Konfiguration. Alles wiederholbar, alles geloggt, alles nachvollziehbar.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" id="1-das-lab-szenario" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Das Lab Szenario</h2>



<p class="wp-block-paragraph" style="margin-bottom:var(--wp--preset--spacing--40)">Um die Schritte greifbar zu machen, wird folgende Umgebung und Naming-Conventions genutzt</p>



<ul class="wp-block-list">
<li><strong>Nodes</strong>
<ul class="wp-block-list">
<li><code>LAB-NODE01</code> (192.168.100.12)</li>



<li><code>LAB-NODE02</code> (192.168.100.13)</li>
</ul>
</li>



<li><strong>Cluster Name</strong>
<ul class="wp-block-list">
<li><code>MSSQL-CL-01</code> (192.168.100.14)</li>
</ul>
</li>



<li><strong>SQL Instanz</strong>
<ul class="wp-block-list">
<li><code>LAB22A</code></li>
</ul>
</li>



<li><strong>Domain</strong>
<ul class="wp-block-list">
<li><code>lab.local</code></li>
</ul>
</li>



<li><strong>Service Accounts</strong> Group Managed Service Accounts (gMSA)
<ul class="wp-block-list">
<li><code>g-LAB22A-oltp</code></li>



<li><code>g-LAB22A-agnt</code></li>
</ul>
</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--60)"/>



<h2 class="wp-block-heading" id="WSFC" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Windows Server Failover Cluster Deployment </h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-85e3fc83 wp-block-columns-is-layout-flex" style="border-width:1px;border-top-left-radius:2px;border-top-right-radius:2px;border-bottom-left-radius:2px;border-bottom-right-radius:2px;margin-top:var(--wp--preset--spacing--20);margin-bottom:var(--wp--preset--spacing--20);padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--80);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--80)">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%">
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-461897f4 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2692.png" alt="⚒" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/system/cluster_manageSQLCluster.ps1" target="_blank" rel="noopener" title="cluster_manageSQLCluster.ps1"><code>cluster_manageSQLCluster.ps1</code></a></p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4d3.png" alt="📓" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><code><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/system/cluster_manageSQLCluster.md" target="_blank" rel="noopener" title="cluster_manageSQLCluster.md">cluster_manageSQLCluster.md</a></code></p>
</div>
</div>
</div>
</div>



<div style="height:14px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="has-text-align-left wp-block-paragraph">Der WSFC bildet das Fundament für AlwaysOn Availability Groups. Das Script steuert den gesamten Lifecycle von der Feature-Installation bis zur Witness-Konfiguration.</p>



<h3 class="wp-block-heading has-text-align-left" id="3-feature-installation-amp-cluster-erstellung" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">Feature Installation &amp; Cluster Erstellung</h3>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Zunächst wird das Feature <code>Failover-Clustering</code> auf den einzelnen Nodes installiert und der Cluster erstellt. Ein wichtiger Aspekt bei der Automatisierung ist das Cluster-Objekt (CNO): Es benötigt Schreibrechte im DNS und Active Directory um später automatisiert Listener-Records anlegen zu können. </p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; auto-links: false; title: ; notranslate">
.\cluster_manageSQLCluster.ps1 `
    -Operation InstallFeature `
    -Nodes &quot;LAB-NODE01&quot;,&quot;LAB-NODE02&quot; `
    -LogPath &quot;C:\Temp\Cluster_Feature_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;
</pre></div>


<p class="has-text-align-left wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Anschließend wird der Cluster erstellt. Das Script setzt zusätzlich die entsprechenden Berechtigungen in AD und DNS.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
.\cluster_manageSQLCluster.ps1 `
    -Operation CreateCluster `
    -ClusterName &quot;MSSQL-CL-01&quot; `
    -Nodes &quot;LAB-NODE01&quot;,&quot;LAB-NODE02&quot; `
    -StaticIP &quot;192.168.100.14&quot; `
    -DnsDomain &quot;lab.local&quot; `
    -TargetOU &quot;OU=lab_computer,DC=lab,DC=local&quot; `
    -LogPath &quot;C:\Temp\Cluster_Create_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;
</pre></div>


<h3 class="wp-block-heading" id="4-file-share-witness-quorum" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">File Share Witness (Quorum)</h3>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Für einen 2-Node Cluster ist ein Witness zwingend erforderlich, um Split-Brain-Szenarien zu vermeiden. Hier wird ein File Share auf dem Domain Controller (<code>\\LAB-DC\quorum$</code>) genutzt. </p>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)"><strong>Manueller Zwischenschritt</strong><br>Das Cluster-Computer-Objekt (<code>MSSQL-CL-01$</code>) benötigt <strong>Full Control</strong> auf Ebene der Share- und NTFS-Berechtigungen des Witness-Pfads (<code>\\LAB-DC\quorum$</code>). Dies muss vor dem folgenden Befehl sichergestellt sein.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
.\cluster_manageSQLCluster.ps1 `
    -Operation SetWitness `
    -ClusterName &quot;MSSQL-CL-01&quot; `
    -WitnessShare &quot;\\LAB-DC\quorum$&quot; `
    -LogPath &quot;C:\Temp\Cluster_Witness_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;
</pre></div>


<h3 class="wp-block-heading" id="5-verwendete-microsoft-cmdlets" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">Verwendete Microsoft Cmdlets</h3>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Das Script nutzt primär das <a href="https://learn.microsoft.com/en-us/powershell/module/failoverclusters/" target="_blank" rel="noopener" title="FailoverClusters"><code>FailoverClusters</code></a> Modul</p>



<ul style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)" class="wp-block-list">
<li><code>New-Cluster</code>
<ul class="wp-block-list">
<li>Erstellt den Cluster, bindet die Nodes ein und setzt die Management-IP</li>
</ul>
</li>



<li><code>Set-ClusterQuorum</code>
<ul class="wp-block-list">
<li>Konfiguriert den Witness-Modus (hier: File Share Witness)</li>
</ul>
</li>



<li><code>Install-WindowsFeature</code>
<ul class="wp-block-list">
<li>Installiert die RSAT-Tools und den Cluster-Dienst</li>
</ul>
</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--70)"/>



<h2 class="wp-block-heading" id="GMSA" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Identity Management: gMSA</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-85e3fc83 wp-block-columns-is-layout-flex" style="border-width:1px;border-top-left-radius:2px;border-top-right-radius:2px;border-bottom-left-radius:2px;border-bottom-right-radius:2px;margin-top:var(--wp--preset--spacing--20);margin-bottom:var(--wp--preset--spacing--20);padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--80);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--80)">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%">
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-461897f4 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2692.png" alt="⚒" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_createGMSA.ps1" target="_blank" rel="noopener" title="security_createGMSA.ps1"><code>security_createGMSA.ps1</code></a></p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4d3.png" alt="📓" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_createGMSA.md" target="_blank" rel="noopener" title="security_createGMSA.md"><code>security_createGMSA.md</code></a></p>
</div>
</div>
</div>
</div>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Die Nutzung von Standard-Usern als Service Accounts kann zu abgelaufenen Passwörtern und Downtime führen. Die empfohlene Methode für SQL Server sind <a href="https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/group-managed-service-accounts/group-managed-service-accounts/group-managed-service-accounts-overview" target="_blank" rel="noopener" title="gMSA"><code>Group Managed Service Accounts (gMSA)</code></a>. Hierbei übernimmt der Key Distribution Service (KDS) des Active Directory die Passwortrotation; Niemand muss das Passwort kennen.</p>



<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--70);margin-right:0;margin-bottom:var(--wp--preset--spacing--50);margin-left:0">KDS Root Key</h3>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Bevor der erste gMSA erstellt werden kann, muss einmalig pro Forest ein KDS Root Key existieren. Sollte bereits ein Key existieren, wird die Erstellung übersprungen.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
.\security_createGMSA.ps1 `
    -InitializeKDS `
    -LogPath &quot;C:\Temp\KDS_Init.log&quot;
</pre></div>


<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--70);margin-right:0;margin-bottom:var(--wp--preset--spacing--50);margin-left:0">gMSA Erstellung</h3>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Anstatt Server direkt einem gMSA zuzuordnen, ist es Best Practice, eine Security Group zu erstellen (<code>SQL_LAB_CL01</code>), welche die Computerkonten der Cluster-Nodes enthält. Neben der Einrichtung der eigentlichen gMSA kann dieses Script auch die Security Gruppen erstellen und die Zuordnungen vornehmen. Und immer schön die Beschreibungen bei AD-Objekten pflegen. </p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
.\security_createGMSA.ps1 `
    -AccountName @( `
        @(&quot;g-LAB22A-oltp&quot;, &quot;SQL Server Engine Account for LAB22A&quot;), `
        @(&quot;g-LAB22A-agnt&quot;, &quot;SQL Server Agent Account for LAB22A&quot;) ) `
    -SecurityGroupName &quot;SQL_LAB_CL01&quot; `
    -SecurityGroupDescription &quot;SQL Server Cluster CL01 gMSA Group&quot; `
    -ServerNames &quot;LAB-NODE01&quot;,&quot;LAB-NODE02&quot; `
    -CreateSecurityGroup `
    -SecurityGroupOU &quot;OU=lab_secgrp,DC=lab,DC=local&quot; `
    -PasswordIntervalDays 90 `
    -LogPath &quot;C:\Temp\gMSA_Creation_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;
</pre></div>


<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:0">Nach diesem Schritt ist ein Neustart der Nodes erforderlich, damit die neue Gruppenmitgliedschaft wirksam wird.</p>



<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--70);margin-right:0;margin-bottom:var(--wp--preset--spacing--50);margin-left:0">Lokale Registrierung</h3>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Nach der Erstellung müssen die gMSA lokal auf den Nodes registriert werden. Hierzu kann folgendes  Script genutzt werden.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# Wichtig für lokale Einrichtung ( Local Admin )
Add-WindowsFeature -Name RSAT-AD-PowerShell

$gMSAs = @(
    &#039;g-LAB22A-oltp&#039;,
    &#039;g-LAB22A-agnt&#039;
)

foreach ($gMSA in $gMSAs) {

    if (Test-ADServiceAccount -Identity $gMSA -ErrorAction SilentlyContinue) {
        try {
            Install-ADServiceAccount -Identity $gMSA -ErrorAction Stop
            Write-Host &quot;$gMSA installiert&quot; -ForegroundColor Green
        } catch {
            Write-Host &quot;$gMSA - Fehler bei Installation: $($_.Exception.Message)&quot; -ForegroundColor Red
        }
    } else {
        Write-Host &quot;$gMSA existiert nicht&quot; -ForegroundColor Yellow
    }
}
</pre></div>


<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:0">Nach diesem Schritt ist ein Neustart der Nodes erforderlich, damit die neue Gruppenmitgliedschaft (TGT) wirksam wird.</p>



<h3 class="wp-block-heading" id="5-verwendete-microsoft-cmdlets" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">Verwendete Microsoft Cmdlets</h3>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Das Script nutzt primär das <a href="https://learn.microsoft.com/en-us/powershell/module/activedirectory/?view=windowsserver2025-ps" target="_blank" rel="noopener" title="ActiveDirectory"><code>ActiveDirectory</code></a> Modul</p>



<ul style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)" class="wp-block-list">
<li><code>Add-KdsRootKey</code>
<ul class="wp-block-list">
<li>Erzeugt den Root Key für den Group Key Distribution Service</li>
</ul>
</li>



<li><code>New-ADServiceAccount</code>
<ul class="wp-block-list">
<li>Erstellt das gMSA Objekt im AD</li>
</ul>
</li>



<li><code>Set-ADServiceAccount</code>
<ul class="wp-block-list">
<li>Konfiguriert, welche Computer (PrincipalsAllowedToRetrieveManagedPassword) das Passwort lesen dürfen</li>
</ul>
</li>
</ul>



<hr class="wp-block-separator has-alpha-channel-opacity" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--70)"/>



<h2 class="wp-block-heading" id="KERBEROS" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Kerberos und Service Principal Names (SPN)</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-85e3fc83 wp-block-columns-is-layout-flex" style="border-width:1px;border-top-left-radius:2px;border-top-right-radius:2px;border-bottom-left-radius:2px;border-bottom-right-radius:2px;margin-top:var(--wp--preset--spacing--20);margin-bottom:var(--wp--preset--spacing--20);padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--80);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--80)">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%">
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-461897f4 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2692.png" alt="⚒" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><code><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_setSqlSpn.ps1" target="_blank" rel="noopener" title="cluster_manageSQLCluster.ps1">security_setSqlSpn.ps1</a></code></p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4d3.png" alt="📓" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><code><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_setSqlSpn.md" target="_blank" rel="noopener" title="">security_setSqlSpn</a></code><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_setSqlSpn.md" title=""><code>.</code>md</a></p>
</div>
</div>
</div>
</div>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Ein gMSA besitzt standardmäßig nicht das Recht, seine eigenen SPNs im AD zu schreiben. Startet der SQL Dienst, schlägt die Registrierung oft fehl, was zu NTLM-Fallbacks führt. Auch wenn ich SPNs bevorzugt kontrolliert und manuell setze, muss die automatische Registrierung technisch möglich sein, um Fallbacks zu vermeiden.</p>



<p class="wp-block-paragraph" style="margin-bottom:var(--wp--preset--spacing--50)">Das Script löst dieses Problem, indem es zusätzlich zur SPN Erstellung dem gMSA die Rechte im AD gewährt, seine eigenen SPNs zu verwalten. Es setzt ACLs der Nodes für <code>Read</code>, <code>Write</code> und <code>Validated write</code> auf das Attribut <code>servicePrincipalName</code>.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
.\security_setSqlSpn.ps1 `
    -ServiceAccount &quot;LAB\g-LAB22A-oltp&quot; `
    -InstanceName &quot;LAB22A&quot; `
    -Port 51101 `
    -Hostnames &quot;LAB-NODE01&quot;,&quot;LAB-NODE02&quot; `
    -Domain &quot;lab.local&quot; `
    -IsGMSA `
    -SetADPermissions `
    -LogPath &quot;C:\Temp\SPN_AlwaysOn_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;
</pre></div>


<h3 class="wp-block-heading" id="5-verwendete-microsoft-cmdlets" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">Verwendete Microsoft Cmdlets</h3>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Zum einen wird <a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/setspn" target="_blank" rel="noopener" title="setspn.exe"><code>setspn.exe</code></a> für Verwaltung der SPN genutzt, zum anderen wieder das <a href="https://learn.microsoft.com/en-us/powershell/module/activedirectory/?view=windowsserver2025-ps" target="_blank" rel="noopener" title=""><code>ActiveDirectory</code></a> Modul, um Accounts zu verifizieren und die SIDs zu extrahieren.</p>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Da keines der Module Active Directory Permsission manipulieren kann, wird hierfür direkt das .NET Framework <a href="https://learn.microsoft.com/en-us/dotnet/api/system.directoryservices?view=windowsdesktop-10.0" target="_blank" rel="noopener" title="System.DirectoryServices"><code>System.DirectoryServices</code></a> genutzt, welches diverse Klassen bereitstellt, um das AD komplett auf Links zu drehen. </p>



<hr class="wp-block-separator has-alpha-channel-opacity" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--70)"/>



<h2 class="wp-block-heading" id="LSP" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Lokale Sicherheitsrichtlinien (Local Security Policies)</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-85e3fc83 wp-block-columns-is-layout-flex" style="border-width:1px;border-top-left-radius:2px;border-top-right-radius:2px;border-bottom-left-radius:2px;border-bottom-right-radius:2px;margin-top:var(--wp--preset--spacing--20);margin-bottom:var(--wp--preset--spacing--20);padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--80);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--80)">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%">
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-461897f4 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2692.png" alt="⚒" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><code><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_setSqlLocalSec.ps1" target="_blank" rel="noopener" title="security_setSqlLocalSec.ps1">security_setSqlLocalSec.ps1</a></code></p>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<p class="has-text-align-center wp-block-paragraph"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4d3.png" alt="📓" class="wp-smiley" style="height: 1em; max-height: 1em;" /><br><a href="https://github.com/gabrielkoehl/DBAScriptBox/blob/main/windows/security/security_setSqlLocalSec.md" target="_blank" rel="noopener" title=""><code>security_setSqlLocalSec.md</code></a></p>
</div>
</div>
</div>
</div>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Wie überall in der IT sollte auch beim Sql Server nach dem <a href="https://en.wikipedia.org/wiki/Principle_of_least_privilege" target="_blank" rel="noopener" title="PoLP"><code>PoLP</code></a> gearbeitet werden, weshalb kein Service Account den lokalen Administrator bekommt.Damit SQL Server trotzdem fehlerfrei und performant läuft, benötigt der Service Account spezifische Rechte im Betriebssystem (&#8222;User Rights Assignment&#8220;). Wer dies ein paar mal manuell gemacht hat, wird sich sehr über diese Lösung freuen. Diese muss lokal auf dem jeweiligen Node ausgeführt werden.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# ===== Set Engine Permissions for gMSA =====

    .\security_setSqlLocalSec.ps1 `
        -ServiceAccount &quot;g-LAB22A-oltp$&quot; `
        -ServiceType &quot;engine&quot; `
        -IsGMSA `
        -LogPath &quot;C:\Temp\LAB22A_Engine_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;

# ===== Set Agent Permissions for gMSA =====

    .\security_setSqlLocalSec.ps1 `
        -ServiceAccount &quot;g-LAB22A-agnt$&quot; `
        -ServiceType &quot;agent&quot; `
        -IsGMSA `
        -LogPath &quot;C:\Temp\LAB22A_Agent_$(Get-Date -Format &#039;yyyyMMdd_HHmmss&#039;).log&quot;
</pre></div>


<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">Verwendete Microsoft Cmdlets</h3>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Da PowerShell keine nativen Cmdlets besitzt, um lokale Sicherheitsrichtlinien (LSA) zu bearbeiten, wird das Windows-Tool <a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/secedit" target="_blank" rel="noopener" title="secedit.exe"><code>secedit.exe</code></a> genutzt und der Export manipuliert.</p>



<hr class="wp-block-separator has-alpha-channel-opacity" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--70)"/>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Summary</h2>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Mit diesen vier Bausteinen wurde das Fundament für eine Sql Server Installation gelegt. Es wurde nicht nur eine Installation durchgeführt, sondern eine valide, dokumentierte Konfiguration geschaffen, die Identität (gMSA), Netzwerk (DNS/SPN) und OS-Performance (LSA) berücksichtigt. Im nächsten Schritt kann die eigentliche SQL Server Instanz installiert werden.</p>



<p class="wp-block-paragraph">Schaut euch auch die verlinkten MD-Files an, dort stehen Hintergrundinfos zu den Best Practices und weiterführende Artikel von Microsoft</p>



<figure class="wp-block-image aligncenter size-full" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--60)"><img loading="lazy" decoding="async" width="307" height="328" src="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_2.png" alt="good to know emote" class="wp-image-345" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_2.png 307w, https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_2-281x300.png 281w" sizes="auto, (max-width: 307px) 100vw, 307px" /></figure><p>The post <a href="https://dbavonnebenan.de/automate-sql-server-ha-ad-spn-and-cluster-setup_de/">SQL Server HA: AD, SPN und Cluster Setup automatisieren</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://dbavonnebenan.de/automate-sql-server-ha-ad-spn-and-cluster-setup_de/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>psTerminalPerfCounter &#8211; SpeedUp your Performance Counters in Terminal</title>
		<link>https://dbavonnebenan.de/psterminalperfcounter-speedup-your-performance-counters_de/</link>
					<comments>https://dbavonnebenan.de/psterminalperfcounter-speedup-your-performance-counters_de/#respond</comments>
		
		<dc:creator><![CDATA[Gabriel]]></dc:creator>
		<pubDate>Sat, 26 Jul 2025 15:53:33 +0000</pubDate>
				<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[CONSOLE]]></category>
		<category><![CDATA[counter]]></category>
		<category><![CDATA[DE]]></category>
		<category><![CDATA[PERFORMANCE]]></category>
		<category><![CDATA[TERMINAL]]></category>
		<guid isPermaLink="false">https://dbavonnebenan.de/?p=605</guid>

					<description><![CDATA[<p>Visualisiere Windows Performance Counter direkt im Terminal mit psTerminalPerfCounter. Sprachunabhängige Templates, maximale Anpassbarkeit. Perfekt für schnelles AdHoc-Monitoring.</p>
<p>The post <a href="https://dbavonnebenan.de/psterminalperfcounter-speedup-your-performance-counters_de/">psTerminalPerfCounter – SpeedUp your Performance Counters in Terminal</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading">Current Release ( 2026-06-08 )</h2>



<div style="height:13px" aria-hidden="true" class="wp-block-spacer"></div>


https://www.powershellgallery.com/packages/psTerminalPerfCounter


<ul class="wp-block-list">
<li><a href="https://github.com/gabrielkoehl/psTerminalPerfCounter/blob/main/CHANGELOG.md" target="_blank" rel="noopener" title="Changelog ">Changelog</a></li>



<li><a href="https://github.com/gabrielkoehl/psTerminalPerfCounter/tree/main?tab=readme-ov-file#documentation" target="_blank" rel="noopener" title="Documentation">Documentation</a></li>
</ul>



<div style="height:13px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" style="margin-bottom:var(--wp--preset--spacing--50)">Intro</h2>



<p class="wp-block-paragraph">Die Windows Performance Counter sind absolut mächtig, aber wenn es mal schnell gehen muss oder ich genau passend zu einem Problem ein Monitoring benötige, bin ich schnell genervt. Meistens nutze ich dann doch andere Möglichkeiten, PowerShell Native Get-Counter, CIM oder greife auf vorhandene Monitoring-Lösungen bei meinen Kunden zurück. Aber auch das geht nie AdHoc aber ich habe es akzeptiert.</p>



<p class="wp-block-paragraph">Nun habe ich beim letzten DataSaturday Uwe Ricken beim Performance Tuning Workshop begleiten dürfen und auch er hat die Performance Counter noch mal klar als Musthave im Werkzeugkoffer eines jeden DBA benannt. Was mir bis dahin nicht bewusst war: SQL Server legt individuelle Counter wie <strong>Compile/sec</strong>, <strong>AdHoc Queries/sec</strong> usw. an.</p>



<figure class="wp-block-image aligncenter size-full is-resized is-style-default" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--60)"><img loading="lazy" decoding="async" width="500" height="54" src="https://dbavonnebenan.de/wp-content/uploads/2025/07/mindblowing.png" alt="MINDBLOWING" class="wp-image-608" style="width:500px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/07/mindblowing.png 500w, https://dbavonnebenan.de/wp-content/uploads/2025/07/mindblowing-300x32.png 300w" sizes="auto, (max-width: 500px) 100vw, 500px" /></figure>



<p class="wp-block-paragraph">Die Möglichkeiten, Abhängigkeiten direkt zu visualisieren &#8211; endlos. Aber wie lange soll ich da sitzen, bis ich das im Windows Performance Monitor abgebildet habe, am besten Adhoc bei einem Kunden? </p>



<p class="wp-block-paragraph">Uwe zeigte die Möglichkeiten im <strong>Windows Admin Center</strong> auf, welches Templates ermöglicht, die man flexibel auf Servern nutzen kann. Für System oder Umgebungen, in denen das Admin Center strategisch genutzt wird, definitiv eine sehr gut Wahl.</p>



<p class="wp-block-paragraph">Aber zwei Probleme bleiben: Als Berater kann ich nicht bei jedem Kunden erst Software installieren und wenn das Admin Center doch läuft, scheitern meine englischen Templates an deutschen Systemen.</p>



<p class="wp-block-paragraph" style="margin-bottom:var(--wp--preset--spacing--50)">Nun war ich trotzdem so unglaublich angefixt von der Template-Idee und den Sql Server Möglichkeiten, dass nach Feierabend 4 Wochen Crunchtime im Kopf waren. Somit möchte ich euch an dieser Stelle mein erstes Modul in der PowerShell Gallery vorstellen</p>


https://www.powershellgallery.com/packages/psTerminalPerfCounter/0.1.0-preview


<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Die technischen Details, Entwicklungsstand findet ihr alle im Repository, ich möchte hier einfach die verschiedenen Möglichkeiten zeigen.</p>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Main Features</h2>



<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">100% PowerShell</h3>



<ul class="wp-block-list">
<li>keine externen Abhängigkeiten</li>
</ul>



<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Terminal-Grafiken</h3>



<ul class="wp-block-list">
<li>natürlich ist es keine richtige Engine, je mehr Counter desto Hakeliger wird es, aber es reicht</li>



<li>Als Basis dient die Lösung von <a href="https://github.com/PrateekKumarSingh/Graphical" target="_blank" rel="noopener" title="">Prateek Singh</a>. Ohne diesen Fund hätte ich wohl dieses Problem erst lösen müssen, wofür ich vermutlich keine Zeit gehabt hätte. DANKE!!!</li>
</ul>



<figure class="wp-block-image aligncenter size-full is-resized" style="margin-top:var(--wp--preset--spacing--40);margin-bottom:var(--wp--preset--spacing--40)"><img loading="lazy" decoding="async" width="632" height="757" src="https://dbavonnebenan.de/wp-content/uploads/2025/07/perfcounter_example_cpu.png" alt="" class="wp-image-609" style="width:383px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/07/perfcounter_example_cpu.png 632w, https://dbavonnebenan.de/wp-content/uploads/2025/07/perfcounter_example_cpu-250x300.png 250w" sizes="auto, (max-width: 632px) 100vw, 632px" /></figure>



<p class="wp-block-paragraph"></p>



<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Configuration Templates</h3>



<ul class="wp-block-list">
<li>jedes Element der Ausgabe wird über Configuration Files gesteuert</li>



<li>flexibel, transportabel</li>
</ul>



<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Individualisierung der Datenausgaben </h3>



<ul class="wp-block-list">
<li>für jeden Counter im Configuration-File kann eine eigene Ausgabe genutzt werden
<ul class="wp-block-list">
<li>Table, Graph oder Beide</li>
</ul>
</li>



<li>beliebige Skalierungen der Achsen in Steps und Max</li>



<li>beliebige Einfärbung der Values auf Basis einer benutzerdefinierten ColorMap per Counter</li>



<li>Konvertierung der Values per Configuration-File ( Byte -&gt; KB -&gt; MB -&gt; GB )</li>
</ul>



<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Sprachunabhängig</h3>



<ul class="wp-block-list">
<li>Configuration Files basieren nicht auf den Counter-Pfaden sondern IDs</li>



<li>immer noch transportabel, egal welche Systemsprache</li>



<li>Translation von ID zu Counterpfad erfolgt erst zur Laufzeit</li>
</ul>



<ul class="wp-block-list">
<li>keine externen Abhängigkeiten</li>



<li>Ich wusste gar nicht, dass dies möglich ist mit Windows Bordmitteln. Auch hier bin ich durch Zufall über einen Blog gestolpert bei <a href="https://powershell.one/tricks/performance/performance-counters" target="_blank" rel="noopener" title="">PowerShell.ONE</a>. Auch hier ein dickes DANKE!!! Meine eigentliche Lösung sah mehrere Sprachvarianten ( DE, EN, FR, ES) vor, welche dann per auslesen der Systemlokalen gesetzt wurden.</li>
</ul>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Demo mit CPU Configuration</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# Install module with required dependency (GripDevJsonSchemaValidator)
Install-Module -Name psTerminalPerfCounter -AllowPrerelease

# Import the module
Import-Module psTerminalPerfCounter

# Start Default Cpu Configuration
Start-tpcMonitor -ConfigName CPU
</pre></div>


<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-464c60d7 wp-block-columns-is-layout-flex" style="padding-top:var(--wp--preset--spacing--50);padding-bottom:var(--wp--preset--spacing--50)">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="552" height="110" src="https://dbavonnebenan.de/wp-content/uploads/2025/07/perfcounter_load.png" alt="" class="wp-image-610" style="width:622px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/07/perfcounter_load.png 552w, https://dbavonnebenan.de/wp-content/uploads/2025/07/perfcounter_load-300x60.png 300w" sizes="auto, (max-width: 552px) 100vw, 552px" /></figure>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<p class="wp-block-paragraph">Beim Laden der Konfiguration werden die Counter IDs zu Counterpfaden übersetzt und getestet</p>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-b2e80fc7 wp-block-columns-is-layout-flex" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="613" height="155" src="https://dbavonnebenan.de/wp-content/uploads/2025/07/perfcounter_sample.png" alt="" class="wp-image-611" style="width:629px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/07/perfcounter_sample.png 613w, https://dbavonnebenan.de/wp-content/uploads/2025/07/perfcounter_sample-300x76.png 300w" sizes="auto, (max-width: 613px) 100vw, 613px" /></figure>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<p class="wp-block-paragraph">Wenn das erfolgreich war, werden die ersten Samples gesammelt, damit ein Graph angezeigt werden kann</p>
</div>
</div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9c5688cc wp-block-columns-is-layout-flex" style="padding-top:var(--wp--preset--spacing--50);padding-right:0;padding-bottom:var(--wp--preset--spacing--50);padding-left:0">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%">
<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="767" height="774" src="https://dbavonnebenan.de/wp-content/uploads/2025/07/perfcounter_run.png" alt="" class="wp-image-612" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/07/perfcounter_run.png 767w, https://dbavonnebenan.de/wp-content/uploads/2025/07/perfcounter_run-297x300.png 297w, https://dbavonnebenan.de/wp-content/uploads/2025/07/perfcounter_run-150x150.png 150w" sizes="auto, (max-width: 767px) 100vw, 767px" /></figure>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<p class="wp-block-paragraph">Thats IT! </p>



<p class="wp-block-paragraph">Innerhalb von 10 Sekunden 2 Performance Counter am Laufen, ohne Installation, ohne Sprachprobleme.</p>
</div>
</div>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Demo Counter Translation</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# Translate ID -&gt; Path
Get-tpcPerformanceCounterInfo 238-6
</pre></div>


<figure class="wp-block-image aligncenter size-full" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)"><img loading="lazy" decoding="async" width="711" height="148" src="https://dbavonnebenan.de/wp-content/uploads/2025/07/perfmon_translate_id.png" alt="" class="wp-image-613" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/07/perfmon_translate_id.png 711w, https://dbavonnebenan.de/wp-content/uploads/2025/07/perfmon_translate_id-300x62.png 300w" sizes="auto, (max-width: 711px) 100vw, 711px" /></figure>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# Search Counter -&gt; Including ID
Get-tpcPerformanceCounterInfo &quot;Queue&quot;
</pre></div>


<figure class="wp-block-image aligncenter size-full" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:0"><img loading="lazy" decoding="async" width="1013" height="600" src="https://dbavonnebenan.de/wp-content/uploads/2025/07/perfmon_translate_search1.png" alt="" class="wp-image-614" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/07/perfmon_translate_search1.png 1013w, https://dbavonnebenan.de/wp-content/uploads/2025/07/perfmon_translate_search1-300x178.png 300w, https://dbavonnebenan.de/wp-content/uploads/2025/07/perfmon_translate_search1-768x455.png 768w" sizes="auto, (max-width: 1013px) 100vw, 1013px" /></figure>



<p class="has-contrast-color has-text-color has-link-color wp-elements-a85209788f442c1a4bfcffc9ae003cc8 wp-block-paragraph" style="margin-bottom:var(--wp--preset--spacing--50)"><em>Die Ausgabe ist stark beschnitten, da das ResultSet enorm ist</em></p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# Search Counter -&gt; Including ID
Get-tpcPerformanceCounterInfo &quot;Processor Queue&quot;
</pre></div>


<figure class="wp-block-image aligncenter size-full" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)"><img loading="lazy" decoding="async" width="675" height="79" src="https://dbavonnebenan.de/wp-content/uploads/2025/07/perfmon_translate_search2.png" alt="" class="wp-image-615" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/07/perfmon_translate_search2.png 675w, https://dbavonnebenan.de/wp-content/uploads/2025/07/perfmon_translate_search2-300x35.png 300w" sizes="auto, (max-width: 675px) 100vw, 675px" /></figure>



<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Wie geht&#8217;s weiter?</h2>



<p class="wp-block-paragraph">Die Basis läuft, die planned Feature <a href="https://github.com/gabrielkoehl/psTerminalPerfCounter/blob/main/docs/en-US/DevelopmentStatus.MD" target="_blank" rel="noopener" title="">Liste</a> ist lang und das, was ich wirklich brauche bezüglich Sql Server ist noch in weiter Ferne. Ich hoffe ich habe nicht ein Problem bedient, was es gar nicht gibt, aber dieses Modul adressiert einfach alle Probleme, die ich schon immer mit den Performance Countern hatte, siehe oben.</p>



<p class="wp-block-paragraph">Weiterhin ist das mein erstes größeres Projekt, was ich Public entwickle, somit könnte Github nicht immer den erwarteten Standards entsprechen. Über Feedback und Hinweise freue ich mich immer.</p>



<figure class="wp-block-image aligncenter size-full is-resized" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)"><img loading="lazy" decoding="async" width="246" height="470" src="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_standing_1.png" alt="happy" class="wp-image-330" style="width:125px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_standing_1.png 246w, https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_standing_1-157x300.png 157w" sizes="auto, (max-width: 246px) 100vw, 246px" /></figure>


https://github.com/gabrielkoehl/psTerminalPerfCounter<p>The post <a href="https://dbavonnebenan.de/psterminalperfcounter-speedup-your-performance-counters_de/">psTerminalPerfCounter – SpeedUp your Performance Counters in Terminal</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://dbavonnebenan.de/psterminalperfcounter-speedup-your-performance-counters_de/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SQL Server Disk Latency richtig messen &#8211; snapDiskLatency</title>
		<link>https://dbavonnebenan.de/sql-server-disk-latency-de/</link>
					<comments>https://dbavonnebenan.de/sql-server-disk-latency-de/#respond</comments>
		
		<dc:creator><![CDATA[Gabriel]]></dc:creator>
		<pubDate>Thu, 17 Jul 2025 14:06:35 +0000</pubDate>
				<category><![CDATA[Sql Server]]></category>
		<category><![CDATA[TSql]]></category>
		<category><![CDATA[DE]]></category>
		<category><![CDATA[diskio]]></category>
		<category><![CDATA[DISKSPD]]></category>
		<category><![CDATA[latency]]></category>
		<category><![CDATA[PERFORMANCE]]></category>
		<guid isPermaLink="false">https://dbavonnebenan.de/?p=563</guid>

					<description><![CDATA[<p>Wie du richtige SQL Server Disk Latency Werte erhältst statt verfälschter DMV-Daten. Open Source snapDiskLatency Lösung für Langzeitanalyse.</p>
<p>The post <a href="https://dbavonnebenan.de/sql-server-disk-latency-de/">SQL Server Disk Latency richtig messen – snapDiskLatency</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></description>
										<content:encoded><![CDATA[<div style="border: 1px dotted #eb2b14;border-radius: 0%;background-color: #eeeeee;padding-top: var(--wp--preset--spacing--30);padding-left: var(--wp--preset--spacing--30);padding-right: var(--wp--preset--spacing--30);padding-bottom: var(--wp--preset--spacing--30)" class="ub-styled-box ub-bordered-box wp-block-ub-styled-box" id="ub-styled-box-ba0a6049-a2c1-44fc-b531-47f63f78c8b4">
<p class="wp-block-paragraph" style="margin-top:0;margin-right:0;margin-bottom:0;margin-left:0" id="ub-styled-box-bordered-content-">Was erwartet dich hier?</p>



<ul style="margin-top:0;margin-right:0;margin-bottom:0;margin-left:0" class="wp-block-list">
<li><a href="#DISKSPD" title="DISKSPD Templates und Wrapper">DISKSPD Templates und Wrapper</a></li>



<li><a href="#sqlfilestats" title="">Wie kann ich die Latenzen der einzelnen Datenbank Files anzeigen lassen?</a></li>



<li><a href="#snapdisk" title="Langzeit Analyse der Latenzen!!!">Langzeit Analyse der Latenzen!!!</a></li>
</ul>


</div>


<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>


https://github.com/gabrielkoehl/DBAScriptBox


<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Intro</h2>



<p class="wp-block-paragraph">Aktuell bin ich bei einem Kunden an einem Code Review beteiligt. Dabei beleuchten wir neben der Applikation auch alle Schnittstellen.. Dazu gehört natürlich auch die Sql Server Landschaft, welche ich Anfang des Jahres aufgesetzt habe.</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-461897f4 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:85%">
<p class="wp-block-paragraph">Das Ergebnis waren doch mehr Findings in meine Richtung als erwartet und ich fühlte mich sofort gechallenged. Das Meiste konnte ich widerlegen, bei einigen Themen war der Kunde in der Pflicht und ein Thema hat mich rein interessenhalber gepackt.</p>



<p class="has-text-align-center wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)"><strong>Read- und Write Latenzen der TempDB</strong> ( und jeder anderen DB )</p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:15%">
<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="264" height="329" src="https://dbavonnebenan.de/wp-content/uploads/2025/06/emote-4.png" alt="" class="wp-image-516" style="width:131px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/06/emote-4.png 264w, https://dbavonnebenan.de/wp-content/uploads/2025/06/emote-4-241x300.png 241w" sizes="auto, (max-width: 264px) 100vw, 264px" /></figure>
</div>
</div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Vorgeschichte</h2>



<p class="wp-block-paragraph">Der Kunde betreibt kein eigenes Rechenzentrum, ist somit vom Hoster abhängig, welcher leider nicht sehr kommunikativ ist. Somit musste ich mir auf diverse andere Wege ein Bild von der total overcommitteten Hardware machen und den Hoster Challengen. Dies war auch der Grund, warum ich ziemlich genau im Bilde war, was die Hosts und Storage Systeme des Hosters im Stande waren zu leisten.</p>



<p class="wp-block-paragraph">Deshalb hat es mich doch sehr gewundert, dass als Finding 32ms Write Latency aufgeführt waren. Keine Frage, das ist ein schlechter Wert für AllFlash NVMe aber der stand da einfach so im Report, ohne Differenzierung oder tiefere Analysen. Nur die Aussage schlechter Storage. Also habe ich meine Werkzeuge ausgepackt, mir das noch mal angeschaut und auch eine neue Lösung entwickelt, die ich mit euch teilen möchte.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" id="DISKSPD" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:0">DISKSPD</h2>



<p class="wp-block-paragraph" style="margin-bottom:var(--wp--preset--spacing--50)"><em><strong>Repo</strong>: perf_diskspeed/DISKSPD</em><br><strong><em>Microsoft</em>:</strong> <a href="https://github.com/microsoft/diskspd">https://github.com/microsoft/diskspd</a></p>



<p class="wp-block-paragraph">Mit DISKSPD sollte man auf unbekannten Systemen immer anfangen, oft liegt hier schon ein großer Anteil an Problemen versteckt. Bei diesem Kudnen lief zum Beispiel eine DB mit hohem Workload auf Spindeln, mit Latenzen in der .2 Perzentile mit 15 Sekunden Latenz, dazu 14MB Durchsatz. Da brauche ich mir um Indexe usw. keine Gedanken mehr machen.</p>



<p class="wp-block-paragraph">Um schnell an Ergebnisse zu kommen, habe ich mir einen Wrapper für DISKSP gebaut, welcher immer 3 Standardtests ausführt</p>



<ul class="wp-block-list">
<li>Sql Server Workload</li>



<li>Random 64KB Workload</li>



<li>Sequenziell 64KB Workload</li>
</ul>



<p class="wp-block-paragraph">Die Dateien werden separat im Unterordner _Output gespeichert und ermöglichen somit recht gute Hinweise auf Storage-Probleme. Nun waren das wie erwartet super Werte, welche NVMe Storage entsprechen. Also weiter zu Sql Server und wir schauen mal was uns dieser so sagt.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" id="sqlfilestats" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">sys.dm_io_virtual_file_stats</h2>



<p class="wp-block-paragraph">Sql Server bietet eine View, in der alle Metriken der Files sauber aufgelistet werden.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
select * from sys.dm_io_virtual_file_stats(null,null)
</pre></div>


<ul class="wp-block-list">
<li><a href="https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-io-virtual-file-stats-transact-sql?view=sql-server-ver17" target="_blank" rel="noopener" title="">microsoft.com</a></li>
</ul>



<figure class="wp-block-image size-full" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)"><img loading="lazy" decoding="async" width="1117" height="458" src="https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_dmv.png" alt="sys.dm_io_virtual_file_stats" class="wp-image-569" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_dmv.png 1117w, https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_dmv-300x123.png 300w, https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_dmv-1024x420.png 1024w, https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_dmv-768x315.png 768w" sizes="auto, (max-width: 1117px) 100vw, 1117px" /></figure>



<p class="wp-block-paragraph">Darauf basierend habe ich ein Analyse Script gebaut, welches mir mehr Infos gibt und ich auch weitere Rückschlüsse wie ReadWrite Ratio, wahrscheinliche IOPS auf Basis der Anzahl der Vorgänge und auch Mengen. Damit kann man schon ganz gut Storage Systeme zu speziellen Workloads aufbauen.</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-461897f4 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:80%">
<p class="wp-block-paragraph">Und auch dieser Wert hat mir 32ms Write Latency der TempDB angezeigt. Dann habe ich angefangen mich zu fragen, wie entstehet dieser Wert überhaupt? </p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:20%">
<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="261" height="323" src="https://dbavonnebenan.de/wp-content/uploads/2025/07/emotes_question.png" alt="Question" class="wp-image-575" style="width:125px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/07/emotes_question.png 261w, https://dbavonnebenan.de/wp-content/uploads/2025/07/emotes_question-242x300.png 242w" sizes="auto, (max-width: 261px) 100vw, 261px" /></figure>
</div>
</div>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Grundlegende Berechnung der Werte</h3>



<p class="wp-block-paragraph">SQL Server misst I/O-Latenz durch einfache Division der kumulativen Wartezeiten durch die Anzahl der Operationen:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
-- Read Latency in ms
io_stall_read_ms / num_of_reads = durchschnittliche Read-Latenz

-- Write Latency in ms  
io_stall_write_ms / num_of_writes = durchschnittliche Write-Latenz
</pre></div>


<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">Thats it!!! Und nun sollte klar sein, dass die punktuelle Nutzung dieser DMV nicht wirklich aussagekräftig ist, im Gegenteil. Die Werte können wie bei dem Review Unternehmen, was sich auf solche Prozesse spezialisiert hat, zu falschen Rückschlüssen und Handlungsempfehlungen führen. Gruselig.</p>



<p class="wp-block-paragraph">Wenn das Storagesystem seit dem Start von Sql Server mal hart Schluckauf hatte, sind die Metriken verunreiningt. Laut meinen Recherchen kann man diese nicht flushen, nur durch das Offline nehmen der DB, was ich üblicherweise in produktiven Umgebungen vermeide.</p>



<p class="wp-block-paragraph">Nach ein paar weiteren Runden in meinem Kopf kam recht schnell die Lösung. Das sind alles SnapShot Values, ich brauche ja nur das Delta zwischen zwei Werten und schon habe ich reale Werte. Und das Ganze kann ich auch noch in eine Zeitreihe packen und habe eine Langzeitanalyse.  Mindblowing <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f61d.png" alt="😝" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p class="wp-block-paragraph">Google hat auch schnell entsprechende Lösungen aufgezeigt, die ich als Basis Idee genutzt habe. Danke an Paul Randal ( <a href="https://www.sqlskills.com/blogs/paul/capturing-io-latencies-period-time/">sqlskills.com</a> )</p>



<p class="wp-block-paragraph">Für die Analyse einer kompletten Woche ist dies aber auch nicht geeignet. Somit habe ich das Ganze auf eine Langzeitanalyse mit persistenten Table erweitert.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" id="snapdisk" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:0">Die Lösung &#8211;&gt; snapDiskLatency</h2>



<p class="wp-block-paragraph"><em><strong>Repo</strong>: </em>perf_diskspeed/snapDiskLatency</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Was macht snapDiskLatency?</h3>



<p class="wp-block-paragraph">Das Problem ist bekannt &#8211; die Standard-DMV <code>sys.dm_io_virtual_file_stats</code> zeigt kumulative Werte seit dem SQL Server Start. Ein einziger Storage-Schluckauf vor Wochen und die Latenzwerte sind nicht mehr repräsentativ. </p>



<p class="wp-block-paragraph"><strong>snapDiskLatency</strong> löst das Problem mit zwei simplen Stored Procedures:</p>



<p class="wp-block-paragraph">Die Erste sammelt stündlich die Rohdaten aus der DMV und schreibt sie in einen permanenten Table. Warum stündlich? Kürzere Intervalle führen zu unzuverlässigen Delta-Berechnungen &#8211; zu wenig I/O zwischen den Snapshots macht die Werte ungenau. Dies kann aber individuell angepasst werden, da der Agent die Prozedur triggert.</p>



<figure class="wp-block-image size-full" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)"><img loading="lazy" decoding="async" width="1360" height="382" src="https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_raw.png" alt="snapDiskLatency collect" class="wp-image-567" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_raw.png 1360w, https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_raw-300x84.png 300w, https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_raw-1024x288.png 1024w, https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_raw-768x216.png 768w" sizes="auto, (max-width: 1360px) 100vw, 1360px" /></figure>



<p class="wp-block-paragraph">Die Zweite macht die Delta-Berechnungen zwischen den Snapshots und erstellt das Resultset. Somit werden reale Latenzwerte für definierte Zeiträume erzeugt, ohne historischen Schluckauf, welche dann mit anderen Metriken abgeglichen werden können.</p>



<figure class="wp-block-image size-full" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)"><img loading="lazy" decoding="async" width="1298" height="326" src="https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_report-2.png" alt="snapDiskLatency report" class="wp-image-571" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_report-2.png 1298w, https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_report-2-300x75.png 300w, https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_report-2-1024x257.png 1024w, https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_report-2-768x193.png 768w" sizes="auto, (max-width: 1298px) 100vw, 1298px" /></figure>



<p class="wp-block-paragraph">Hier sehen wir zum Beispiel im <a href="https://dbavonnebenan.de/sql-server-truncate-vs-delete-dml-or-ddl-de/" title="TRUNCATE TABLE Operationen im Detail – ROLLBACK ist doch möglich">TRUNCATE Table</a> einen Aureißer ( hier liegt auch NVMe drunter ). Auf dem LAB System ist keine Last, DOXIS ist nur ein Table mit 2 Spalten und wurde künstlich mit Workload bearbeitet.</p>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)"><strong>Das Ergebnis</strong></h3>



<p class="wp-block-paragraph">Statt möglicherweise verunreinigten Werten</p>



<ul class="wp-block-list">
<li>Durchschnittliche Latenzen pro Intervall</li>



<li>Read/Write-Verhältnisse</li>



<li>Aufschlüsselung nach Datenbank und File-Type</li>
</ul>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)"><strong>Setup</strong></h3>



<p class="wp-block-paragraph">Agent Job erstellen, stündlich laufen lassen, fertig. Die komplette Anleitung steht in der README, copy &amp; paste &amp; freuen</p>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="246" height="470" src="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_standing_1.png" alt="happy" class="wp-image-330" style="width:125px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_standing_1.png 246w, https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_standing_1-157x300.png 157w" sizes="auto, (max-width: 246px) 100vw, 246px" /></figure>



<div style="height:20px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)">Diagramme</h2>



<p class="wp-block-paragraph">Da dies auch jedes gute propritäre DatabaseMonitoringTool macht und hübsch grafisch darstellt, will ich zumindest eine Möglichkeit in Excel im Ergebnis zeigen. Dazu muss das Ergebnis der DataSource oder Copy pivotiert werden und schon kann man gut seine Daily Peaks erkennen.</p>



<figure class="wp-block-image size-full" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)"><img loading="lazy" decoding="async" width="1617" height="604" src="https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_graph.png" alt="" class="wp-image-572" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_graph.png 1617w, https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_graph-300x112.png 300w, https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_graph-1024x382.png 1024w, https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_graph-768x287.png 768w, https://dbavonnebenan.de/wp-content/uploads/2025/07/disklat_graph-1536x574.png 1536w" sizes="auto, (max-width: 1617px) 100vw, 1617px" /></figure><p>The post <a href="https://dbavonnebenan.de/sql-server-disk-latency-de/">SQL Server Disk Latency richtig messen – snapDiskLatency</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://dbavonnebenan.de/sql-server-disk-latency-de/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>PowerShell Klassen für Anfänger &#8211; Strukturierter Code statt Funktions-Chaos</title>
		<link>https://dbavonnebenan.de/powershell-classes-for-beginner-de/</link>
					<comments>https://dbavonnebenan.de/powershell-classes-for-beginner-de/#respond</comments>
		
		<dc:creator><![CDATA[Gabriel]]></dc:creator>
		<pubDate>Sat, 14 Jun 2025 18:33:32 +0000</pubDate>
				<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[Classes]]></category>
		<category><![CDATA[DE]]></category>
		<category><![CDATA[Klassen]]></category>
		<category><![CDATA[TUTORIAL]]></category>
		<guid isPermaLink="false">https://dbavonnebenan.de/?p=511</guid>

					<description><![CDATA[<p>Intro Seit eh und je nutze ich PowerShell nach Schema F, so ungefähr war auch mein Lernpfad entsprechend PowerShell Best Practices. Auf den letzten beiden Ebenen habe ich mich nun sehr lange bewegt, natürlich immer effizienter und allgemein wurde das PowerShell Wissen breiter. Während dieser Entwicklung merkte ich jedoch ein Problem: Ich bin ein sehr [&#8230;]</p>
<p>The post <a href="https://dbavonnebenan.de/powershell-classes-for-beginner-de/">PowerShell Klassen für Anfänger – Strukturierter Code statt Funktions-Chaos</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></description>
										<content:encoded><![CDATA[<h2 class="wp-block-heading" style="margin-top:0;margin-bottom:var(--wp--preset--spacing--50)">Intro</h2>



<p class="wp-block-paragraph">Seit eh und je nutze ich PowerShell nach Schema F, so ungefähr war auch mein Lernpfad entsprechend PowerShell Best Practices.</p>



<ul class="wp-block-list">
<li>Quick and Dirty Einzeiler in der Konsole, mittlerweile Terminal</li>



<li>Nach und nach wurden aus den Einzeilern PowerShell Scripte mit besser organisierten Einzeilern</li>



<li>Es kamen Variablen, dann Funktionen, die Wiederverwendbarkeit stieg</li>



<li>Aus Funktionen wurden Module, persistente Datenhalden in Json und technologisch war das erstmal das Ende</li>



<li>Das letzte HighLight war Threading und Runspaces</li>
</ul>



<p class="wp-block-paragraph">Auf den letzten beiden Ebenen habe ich mich nun sehr lange bewegt, natürlich immer effizienter und allgemein wurde das PowerShell Wissen breiter.</p>



<p class="wp-block-paragraph">Während dieser Entwicklung merkte ich jedoch ein Problem: Ich bin ein sehr organisierter Mensch, zumindestens im beruflichen Umfeld 😉, und so soll auch mein Code sein, strukturiert. Von Clean Code halte ich nichts, Kommentare sind einfach punktuell wichtig, für mich, für andere, für meine Kunden. Neben Kommentaren ist aber auch die Struktur wichtig, was bringen mir Kommentare an einem &#8218;Bucket of Chars&#8216; ? Ebenfalls liebe ich symmetrischen Code, ja lacht ruhig, aber es macht sofort ersichtlich, was ist eine Variable, was eine Zusweisung und was der Value. Wenn ich sowas sehe&#8230;</p>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-461897f4 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%"><div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
$userInfo = @{
   Name = $varSurename
   Address = $varStreet
   Data = $varRandomLongVar
}

$configSettings = @{
   ServerName = &#039;localhost&#039;
   Port = 5432
   Database = &#039;mydb&#039;
   Timeout = 30
}

$employee = @{
   Personal = @{
       FirstName = $varSurename
       LastName = &#039;Doe&#039;
       Street = $varStreet
   }
   Work = @{
       Department = &#039;IT&#039;
       Position = &#039;Developer&#039;
   }
}
</pre></div></div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="264" height="329" src="https://dbavonnebenan.de/wp-content/uploads/2025/06/emote-4.png" alt="" class="wp-image-516" style="width:229px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/06/emote-4.png 264w, https://dbavonnebenan.de/wp-content/uploads/2025/06/emote-4-241x300.png 241w" sizes="auto, (max-width: 264px) 100vw, 264px" /></figure>
</div>
</div>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph" style="margin-top:0;margin-bottom:0">&#8230; da kehrt sich mein Innerstes nach Außen 🤣 Ist vielleicht auch eine Krankheit. Dies ist eine normale Schreibweise, die als sauber gilt, ich weiß, aber das sollte doch so aussehen..</p>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-461897f4 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%"><div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
$userInfo = @{
   Name 	= $varSurename
   Address 	= $varStreet
   Data 	= $varRandomLongVar
}

$configSettings = @{
   ServerName 	= &#039;localhost&#039;
   Port 		= 5432
   Database 	= &#039;mydb&#039;
   Timeout 		= 30
}

$employee = @{
   Personal = @{
       FirstName 	= $varSurename
       LastName 	= &#039;Doe&#039;
       Street 		= $varStreet
   }
   Work 	= @{
       Department 	= &#039;IT&#039;
       Position 	= &#039;Developer&#039;
   }
}

$users = @(
   @{ Name = &#039;Alice&#039;; 		Role = &#039;Admin&#039; }
   @{ Name = $varSurename; 	Role = &#039;User&#039; }
)
</pre></div></div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">
<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="251" height="330" src="https://dbavonnebenan.de/wp-content/uploads/2025/06/emote-5.png" alt="" class="wp-image-515" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/06/emote-5.png 251w, https://dbavonnebenan.de/wp-content/uploads/2025/06/emote-5-228x300.png 228w" sizes="auto, (max-width: 251px) 100vw, 251px" /></figure>
</div>
</div>



<div style="height:25px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph" style="margin-top:0;margin-bottom:0">Wie auch immer, darum solls hier nicht gehen. Es ist grundsätzlich so, dass die Struktur und Ordnung bei größeren Projekten nach und nach abnimmt und Klassen genau auf dieses Thema einspielen. Aber warum jetzt erst? Klassen gibt es seit PowerShell 5.0 .</p>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--40)">AI Assisted Coding</h2>



<p class="wp-block-paragraph">Natürlich war ich early Adopter der ganzen KI Chatbots und war erstaunt, was ChatGPT vor 2 Jahren schon konnte. Natürlich war der Code, egal welche Sprache, unbrauchbar, aber für Wissenserzeugung allemal nützlich. So wurde mit fortlaufender Qualität der Code besser und auch die Lösungswege wurden immer interessanter. </p>



<p class="wp-block-paragraph">Irgendwann sind in den Vorschlägen Klassen aufgetaucht und ich habe es konsequent ignoriert 😝. Durch meine C# Reisen wusste ich, dass Klassen einfach der Standard sind, man aber schon grundlegend Wissen sollte, was ich damit machen will im Projekt, um diese sauber zu entwerfen. Und diesen Aufwand wollte ich mir nicht machen.</p>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--40)">Das neue Projekt</h2>



<p class="wp-block-paragraph">Es war an der Zeit, eine 4 Jahre alte Lösung an neue Anforderungen anzupassen, wie das halt manchmal so der Fall ist. Da mein Wissen ja auch nicht stehen bleibt, habe ich nach kurzem überfliegen des Codes entschieden:</p>



<p class="has-text-align-center wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50)"><strong>DAS FASSE ICH NICHT MEHR AN</strong>, <strong>DAS MUSS ALLES NEU!!</strong></p>



<p class="wp-block-paragraph">Nun war ich in der entspannten Lage, dass für die Lösung alles vorhanden war</p>



<ul class="wp-block-list">
<li>Programmablaufplan (PAP)</li>



<li>Funktionsbeschreibungen</li>



<li>Programmatische Lösung der benötigten Funktionen auf unterster Ebene</li>



<li>4 Jahre Lessons learned</li>
</ul>



<p class="wp-block-paragraph">Somit musste ich mich nur um die Orchestrierung aller Schritte kümmern und habe entschieden, dass ich den Klassen mal eine Chance geben werde. In der Theorie ist dies nämlich die sauberste und strukturierteste Möglichkeit zu Coden.</p>



<p class="wp-block-paragraph">Das Ergebnis hat meine Art zu Coden nicht völlig, aber nachhaltig verändert &#8211; aber dazu später mehr.</p>



<h2 class="wp-block-heading" id="powershell-classes-conclusion" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">Fazit nach 5 Wochen im Projekt</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-461897f4 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<figure class="wp-block-image size-full has-custom-border"><img loading="lazy" decoding="async" width="532" height="500" src="https://dbavonnebenan.de/wp-content/uploads/2025/06/class-perfection.jpg" alt="I SAID PERFECTION" class="has-border-color has-contrast-border-color wp-image-518" style="border-width:1px" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/06/class-perfection.jpg 532w, https://dbavonnebenan.de/wp-content/uploads/2025/06/class-perfection-300x282.jpg 300w" sizes="auto, (max-width: 532px) 100vw, 532px" /></figure>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:50%">
<p class="wp-block-paragraph">Klassen in PowerShell sind so unglaublich simpel, so strukturiert, so wunderschön❤️. Natürlich muss der UseCase passen, da die Implementierung initial mehr Struktur verlangt, aber wenn dieser gegeben ist, wird für mich kein Weg mehr an Klassen vorbei führen. </p>
</div>
</div>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--50)">Warum dieser Artikel, gibt doch genug?</h2>



<p class="wp-block-paragraph">Es gibt einige Artikel über Klassen in PowerShell und Objektorientierung aber trotzdem wird dieses Thema kaum in einem klassischen Tutorial behandelt. Es sind immer die Funktionen und man muss um dieses Thema wissen um es zu finden. Somit schreibe ich einfach noch einen Artikel, auch mal in deutsch, um vielleicht doch den ein oder anderen zusätzlich zu erreichen, weil&#8230;</p>



<figure class="wp-block-image aligncenter size-large is-resized" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"><img loading="lazy" decoding="async" width="1024" height="191" src="https://dbavonnebenan.de/wp-content/uploads/2025/06/powershell-classes-ftw-1024x191.png" alt="" class="wp-image-517" style="width:493px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/06/powershell-classes-ftw-1024x191.png 1024w, https://dbavonnebenan.de/wp-content/uploads/2025/06/powershell-classes-ftw-300x56.png 300w, https://dbavonnebenan.de/wp-content/uploads/2025/06/powershell-classes-ftw-768x143.png 768w, https://dbavonnebenan.de/wp-content/uploads/2025/06/powershell-classes-ftw.png 1153w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<hr class="wp-block-separator has-alpha-channel-opacity" style="margin-top:var(--wp--preset--spacing--80);margin-bottom:var(--wp--preset--spacing--80)"/>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-right:var(--wp--preset--spacing--30);margin-bottom:var(--wp--preset--spacing--60)">Hintergrundinfos zu PowerShell Klassen &#8211; OOP seit Version 5.0</h2>



<p class="wp-block-paragraph" style="margin-bottom:var(--wp--preset--spacing--30)">Klassen gibt es in PowerShell seit PowerShell 5.0 also 2016&#8230; und ich habe vor einem Jahr erst davon erfahren 🤦‍♂️. PowerShell Klassen bringen objektorientierte Programmierkonzepte in PowerShell und haben somit auch haben alle grundlegenden Features einer Klasse.</p>



<ul class="wp-block-list">
<li><strong>Eigenschaften (Properties)</strong>: Eigenschaften sind stark typisiert und unterstützen die meisten Validierungsattribute wie [ValidateNotNull()], [ValidateRange()] etc. </li>



<li><strong>Methoden</strong>: Methoden sind Funktionen, die an ein Objekt gebunden sind und explizit den Rückgabetyp definieren müssen</li>



<li><strong>Konstruktoren</strong>: Ermöglichen die Initialisierung von Objekten mit spezifischen Werten </li>



<li><strong>Vererbung</strong>: Erlaubt es, neue Klassen von bestehenden Klassen zu erstellen und deren Funktionalität zu erweitern</li>
</ul>



<p class="wp-block-paragraph" style="margin-top:var(--wp--preset--spacing--30)">Eine Klasse ist nach dem Laden ein eigener Datentyp, später mehr dazu.</p>



<hr class="wp-block-separator has-alpha-channel-opacity" style="margin-top:var(--wp--preset--spacing--70);margin-bottom:var(--wp--preset--spacing--70)"/>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--70);margin-right:var(--wp--preset--spacing--30);margin-bottom:var(--wp--preset--spacing--50)">Und wie funktioniert das jetzt?</h2>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-461897f4 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-vertically-aligned-top is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:20%">
<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="307" height="328" src="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_2.png" alt="Explanation incoming" class="wp-image-345" style="width:143px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_2.png 307w, https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_2-281x300.png 281w" sizes="auto, (max-width: 307px) 100vw, 307px" /></figure>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:80%">
<p class="wp-block-paragraph">Ich weiß noch, wie ich mich vor vielen Jahren mit OOP schwer tat und allgemein davon auszugehen ist, dass sich doch eher Admins als vollblut Software Entwickler initial mit PowerShell auseinander setzen. Somit versuche ich es mal total simpel.</p>
</div>
</div>



<div style="height:26px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">Ich nehme mal ein fiktives Modul, welche per Remoting Serverproperties abfragt und lokal auswertet, ein klassischer AdminTask. </p>



<p class="wp-block-paragraph"><strong>ACHTUNG:</strong> Natürlich ist dieses Beispiel sehr konstruiert und nicht zwingend praktikabel aber es zeigt meiner Meinung nach die Zusammenhänge recht gut.</p>



<p class="wp-block-paragraph">Dieses PowerShell Klassen Tutorial zeigt dir die wichtigsten Konzepte für objektorientierte Programmierung mit PowerShell.</p>



<div style="height:26px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--40);margin-right:var(--wp--preset--spacing--20);margin-bottom:var(--wp--preset--spacing--40);margin-left:var(--wp--preset--spacing--20)">Eigenschaften (Properties)</h3>



<p class="wp-block-paragraph" style="margin-top:0">Eine Klasse hat zu jeder Zeit ein fest definierten Satz an Eigenschaften. Dieser kann zur Laufzeit nicht geändert werden, sondern muss in der Definition der Klasse beschrieben werden.</p>



<div style="height:26px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--40);margin-right:var(--wp--preset--spacing--20);margin-bottom:var(--wp--preset--spacing--40);margin-left:var(--wp--preset--spacing--20)">Die Klasse</h3>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
class Server {
# All Possible properties for the Server class
    &#x5B;string]        $ServerName
    &#x5B;string]        $Status
    &#x5B;ipaddress]     $IP 
    &#x5B;string]        $Location
    &#x5B;datetime]      $LastBootTime
    &#x5B;string]        $OperatingSystem
# -------------------------------------------
}
</pre></div>


<div style="height:26px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--40);margin-right:var(--wp--preset--spacing--20);margin-bottom:var(--wp--preset--spacing--40);margin-left:var(--wp--preset--spacing--20)">Konstruktoren</h3>



<p class="wp-block-paragraph" style="margin-bottom:var(--wp--preset--spacing--50)">Ein Konstruktor erzeugt die Instanz einer Klasse. Dabei kann die Klasse default Values erhalten, oder der Konstruktor erwartet Parameter. In unserem Fall ist ein Servername sinnvoll. Somit gebe ich der Property <strong>ServerName </strong>direkt beim Laden einen Wert.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
class Server {
# All Possible properties for the Server class
    &#x5B;string]        $ServerName
    &#x5B;string]        $Status
    &#x5B;ipaddress]     $IP 
    &#x5B;string]        $Location
    &#x5B;datetime]      $LastBootTime
    &#x5B;string]        $OperatingSystem
# -------------------------------------------

    # Constructor
    Server(&#x5B;string]$name) {
        $this.ServerName = $name
    }
}
</pre></div>


<div style="height:26px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">Nun haben wir zwar ein Klasse aber wie bekommen wir nun einen Server da rein? Indem wir die Klasse in eine Variable laden und am besten auch noch dynamisch für alle Server.</p>



<div style="height:26px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
class Server {
# All Possible properties for the Server class
    &#x5B;string]        $ServerName
    &#x5B;string]        $Status
    &#x5B;ipaddress]     $IP 
    &#x5B;string]        $Location
    &#x5B;datetime]      $LastBootTime
    &#x5B;string]        $OperatingSystem
# -------------------------------------------

    # Constructor
    Server(&#x5B;string]$name) {
        $this.ServerName = $name
    }
}

$serverlist = @(&#039;Server1&#039;,&#039;Server2&#039;,&#039;Server3&#039;)

foreach ( $server in $serverlist ) {
    
    # Create a new Server object for each server name
    New-Variable -Name &quot;obj_$server&quot; -Value (&#x5B;Server]::new($server)) -Force

}
</pre></div>


<div style="height:26px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--40);margin-right:var(--wp--preset--spacing--20);margin-bottom:var(--wp--preset--spacing--40);margin-left:var(--wp--preset--spacing--20)">Methoden</h3>



<p class="wp-block-paragraph">Methoden machen meiner Meinung nach die Klassen erst richtig interessant. Methoden sind die Funktionen mit denen ich Eigenschaften der Klassen manipulieren kann oder auf Basis von Argumenten, welche ich dem Aufruf hinzufüge, RETURNS erzeugen kann. Hierbei gebe ich bereits in der Methodendefintion den Typ der Rückgabe an. <strong>VOID</strong> erzeugt somit logischerweise keine Rückgabe.</p>



<div style="height:26px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
class Server {
    &#x5B;string]        $ServerName
    &#x5B;string]        $Status
    &#x5B;ipaddress]     $IP 
    &#x5B;string]        $Location
    &#x5B;datetime]      $LastBootTime
    &#x5B;string]        $OperatingSystem

    Server(&#x5B;string]$name) {
        $this.ServerName = $name
    }

    &#x5B;void] GetServerInfo() {

        try {

            $osInfo = Invoke-Command -ComputerName $this.ServerName -ScriptBlock {
                Get-CimInstance Win32_OperatingSystem | Select-Object Caption, LastBootUpTime
            } -ErrorAction Stop

            $this.IP = Invoke-Command -ComputerName $this.ServerName -ScriptBlock {
                (Get-NetIPAddress -AddressFamily IPv4 | Where-Object {$_.IPAddress -notlike &#039;169.*&#039;} | Select-Object -First 1).IPAddress
            } -ErrorAction Stop

            $this.OperatingSystem = $osInfo.Caption
            $this.LastBootTime    = $osInfo.LastBootUpTime
            $this.Status          = &quot;Online&quot;

        } catch {

            $this.Status = &quot;Offline&quot;

        }
    }
}

$serverlist = @(&#039;Server1&#039;,&#039;Server2&#039;,&#039;Server3&#039;)

foreach ($server in $serverlist) {
    New-Variable -Name &quot;obj_$server&quot; -Value (&#x5B;Server]::new($server)) -Force
}

$obj_Server1.GetServerInfo()
$obj_Server2.GetServerInfo()
$obj_Server3.GetServerInfo()
</pre></div>


<div style="height:26px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">Hier habe ich die Methode <strong>GetServerInfo()</strong> eingefügt, die nun die Informationen zum Server sammelt. Dabei muss ich weder ein Argument mitgeben oder ein Cmdlet starten, da die Instanz (Servername) der Klasse (Server) alles vererbt bekommen hat. Ich brauche keine externen Funktionen, Variablen oder sonst was, alles in der Klasse vorhanden. ❤️ Folgend ein Beispiel aus dem LAB</p>



<div style="height:26px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image aligncenter size-full" style="margin-top:var(--wp--preset--spacing--30);margin-bottom:var(--wp--preset--spacing--30)"><img loading="lazy" decoding="async" width="542" height="283" src="https://dbavonnebenan.de/wp-content/uploads/2025/06/powershell_classes_lab.png" alt="LAB Result of simple powershell class" class="wp-image-532" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/06/powershell_classes_lab.png 542w, https://dbavonnebenan.de/wp-content/uploads/2025/06/powershell_classes_lab-300x157.png 300w" sizes="auto, (max-width: 542px) 100vw, 542px" /><figcaption class="wp-element-caption">LAB Results</figcaption></figure>



<div style="height:26px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--70);margin-right:0;margin-bottom:var(--wp--preset--spacing--50);margin-left:0">Und jetzt, wo ist der Nutzen?</h2>



<p class="wp-block-paragraph">Natürlich ist das nicht der optimalste Use Case, dafür verständlich. Eine Möglichkeit wäre, eine LoggingKlasse zu erstellen. Anbei aus meinem Projekt:</p>



<div style="height:26px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
class PatchLogger {
     &#x5B;string]        $ServerName
     &#x5B;string]        $LogPathServer
     &#x5B;string]        $LogLevel = &#039;INFO&#039;
     &#x5B;hashtable]     $LogTargets
     &#x5B;bool]          $ConsoleOutput
     &#x5B;bool]          $StreamOutput
     &#x5B;string]        $VerbosePreference
     &#x5B;string]        $DebugPreference

     # Constructor
     PatchLogger(	&#x5B;string]$LogPathServer,
					&#x5B;string]$ServerName,
					&#x5B;string]$MessageFile,
					&#x5B;string]$ErrorFile,
					&#x5B;string]$VerboseFile,
					&#x5B;string]$DebugFile,
					&#x5B;string]$SqlServerFile,
					&#x5B;bool]$ConsoleOutput 	= $true,
					&#x5B;bool]$StreamOutput 	= $false) {
					
          $this.LogPathServer   = $LogPathServer
          $this.ServerName      = $ServerName
          $this.ConsoleOutput   = $ConsoleOutput
          $this.StreamOutput    = $StreamOutput
          $this.LogTargets      = @{
               Main           = $MessageFile
               Error          = $ErrorFile
               Verbose        = $VerboseFile
               Debug          = $DebugFile
               SqlServer      = $SqlServerFile
          }

          $this.VerbosePreference = $Global:VerbosePreference
          $this.DebugPreference   = $Global:DebugPreference

          if ( -not (Test-Path $this.LogPathServer) ) {
               New-Item -Path $this.LogPathServer -ItemType Directory -Force | Out-Null
          }
    }
	
}
</pre></div>


<div style="height:26px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">Eine Instanz der Klasse wird direkt beim Laden des Moduls in die Variable <strong>$PatchLogger </strong>geladen und ab jetzt kann ich mit <strong>$PatchLogger.Info($message)</strong> schon direkt loslegen. Wie? So ein kurzer Befehl bei einer so komplexen Klasse? Kommen wir zum letzten, was meiner Meinung nach der Vorteil schlechthin ist: <strong>Überladung</strong>.</p>



<div style="height:26px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--50)">Überladung</h2>



<p class="wp-block-paragraph">Überladung heisst nichts anderes als die Anzahl der Argumente, die ich einer Methode übergebe. Anhand der Anzahl kann die Engine unterscheiden, welche Methode genutzt wird, da ich mehrer mit den gleichen Namen haben kann. Der Fachbegriff wäre Method Overloading.</p>



<p class="wp-block-paragraph">Der Mehrwert liegt darin, dass ich nicht wie bei Funktionen mit BoundParams, Switch oder IF SChleifen arbeiten muss. Wenn eine Helfer Methode mit nur einem Argument aufgerufen wird, werden intern die fehlenden Argumente aufgefüllt und die Hauptmethode aufgerufen.</p>



<p class="wp-block-paragraph">Nehmen wir das Beispiel <strong>$PatchLogger.Info($message)</strong> </p>



<div style="height:26px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
# Hauptmethode
&#x5B;hashtable] WriteLog(&#x5B;string]$Level, &#x5B;string]$Message, &#x5B;string]$MessageCode, &#x5B;int]$Severity, &#x5B;string]$Target, &#x5B;bool]$ReturnObject) {...}

# Helfermethode
&#x5B;hashtable] Info(&#x5B;string]$Message) {
      return $this.WriteLog(&#039;INFO&#039;, $Message, $null, 0, &#039;Main&#039;, $true)
}

# Helfermethode 2
&#x5B;hashtable] Info(&#x5B;string]$Message, &#x5B;string]$MessageCode) {
      return $this.WriteLog(&#039;INFO&#039;, $Message, $MessageCode, 0, &#039;Main&#039;, $true)
}
</pre></div>


<div style="height:26px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">Was wird das meiste sein, was ich ich einem großen Modul benötige? Eine einfache Message ins Main Log, ohne Messagecode und SchnickSchnack. Somit ruft die Helfermethode die Hauptmethode mit den Defaultwerten auf, die genau das bewirken.</p>



<p class="wp-block-paragraph">Schauen wir uns Helfermethode 2 an. Diese erwartet 2 Argumente, nämlich einen MessageCode. Schon kann ich mit <strong>$PatchLogger.Info(&#8222;Prozess XY abgeschlossen&#8220;,&#8220;I0002&#8243;)</strong> eine registrierte Message schreiben. </p>



<p class="wp-block-paragraph">Natürlich kann ich das auch alles mit Funktionen realisieren aber ich denke nicht so strukturiert und hübsch. </p>



<div style="height:26px" aria-hidden="true" class="wp-block-spacer"></div>



<details class="wp-block-details is-layout-flow wp-block-details-is-layout-flow"><summary>Gesamte PatchLogger Klasse</summary><div class="wp-block-syntaxhighlighter-code "><pre class="brush: powershell; title: ; notranslate">
class PatchLogger {
     &#x5B;string]        $ServerName
     &#x5B;string]        $LogPathServer
     &#x5B;string]        $LogLevel = &#039;INFO&#039;
     &#x5B;hashtable]     $LogTargets
     &#x5B;bool]          $ConsoleOutput
     &#x5B;bool]          $StreamOutput
     &#x5B;string]        $VerbosePreference
     &#x5B;string]        $DebugPreference

     # Constructor
     PatchLogger(&#x5B;string]$LogPathServer, &#x5B;string]$ServerName, &#x5B;string]$MessageFile, &#x5B;string]$ErrorFile, &#x5B;string]$VerboseFile, &#x5B;string]$DebugFile, &#x5B;string]$SqlServerFile, &#x5B;bool]$ConsoleOutput = $true, &#x5B;bool]$StreamOutput = $false) {
          $this.LogPathServer   = $LogPathServer
          $this.ServerName      = $ServerName
          $this.ConsoleOutput   = $ConsoleOutput
          $this.StreamOutput    = $StreamOutput
          $this.LogTargets      = @{
               Main           = $MessageFile
               Error          = $ErrorFile
               Verbose        = $VerboseFile
               Debug          = $DebugFile
               SqlServer      = $SqlServerFile
          }

          $this.VerbosePreference = $Global:VerbosePreference
          $this.DebugPreference   = $Global:DebugPreference

          if ( -not (Test-Path $this.LogPathServer) ) {
               New-Item -Path $this.LogPathServer -ItemType Directory -Force | Out-Null
          }
     }

     &#x5B;void] UpdatePreferences(&#x5B;string]$VerbosePreference, &#x5B;string]$DebugPreference) {
          $this.VerbosePreference = $VerbosePreference
          $this.DebugPreference   = $DebugPreference
     }

     # Central WriteLog method - all other methods call this one
     &#x5B;object] WriteLog(&#x5B;string]$Level, &#x5B;string]$Message, &#x5B;string]$MessageCode, &#x5B;int]$Severity, &#x5B;string]$Target, &#x5B;bool]$ReturnObject) {

          $timestamp          = Get-Date -Format &quot;yyyy-MM-dd HH:mm:ss&quot;
          $severityText       = if ($Severity -ne 9999) { &quot;S$Severity&quot; } else { &quot;&quot; }
          $messageCodeText    = if ($MessageCode) { &quot; $MessageCode&quot; } else { &quot;&quot; }

          $paddedLevel        = &quot;{0,-9}&quot; -f &quot;&#x5B;$Level]&quot;

          $additionalInfo     = &quot;$severityText$messageCodeText&quot;
          $logEntry           = if ( $level -eq &#039;BLANK&#039; ) {
                                   &quot;&quot;
                                } elseif ( $Level -notin @(&#039;VERBOSE&#039;,&#039;DEBUG&#039;) -and -not &#x5B;string]::IsNullOrEmpty($additionalInfo) ) {
                                   &quot;&#x5B;$timestamp] $paddedLevel &#x5B;$($this.ServerName)] $Message ($additionalInfo)&quot;
                                } else {
                                   &quot;&#x5B;$timestamp] $paddedLevel &#x5B;$($this.ServerName)] $Message&quot;
                              }

          $targetFile = $this.LogTargets&#x5B;$Target]
          if ( $targetFile ) {
               try {
                    Add-Content -Path $targetFile -Value $logEntry -Encoding UTF8

                    $verboseFile = $this.LogTargets&#x5B;&#039;Verbose&#039;]
                    if ( $verboseFile -and $Level -ne &#039;VERBOSE&#039; ) {
                         Add-Content -Path $verboseFile -Value $logEntry -Encoding UTF8 # for unbroken verbose stream
                    }

               } catch {
                    Invoke-ScriptError $_
               }
          }

          if ( $this.ConsoleOutput ) {
               switch ( $Level ) {
                    &#039;ERROR&#039;        { Write-Host $logEntry -ForegroundColor Red }
                    &#039;WARNING&#039;      { Write-Host $logEntry -ForegroundColor Yellow }
                    &#039;INFO&#039;         { Write-Host $logEntry -ForegroundColor Green }
                    &#039;BLANK&#039;        { Write-Host $logEntry}
                    &#039;VERBOSE&#039;      {
                         if ( $this.VerbosePreference -ne &#039;SilentlyContinue&#039; ) {
                              Write-Host $logEntry -ForegroundColor Cyan
                         }
                    }
                    &#039;DEBUG&#039;        {
                         if ( $this.DebugPreference -ne &#039;SilentlyContinue&#039; ) {
                              Write-Host $logEntry -ForegroundColor Magenta
                         }
                    }
               }
          }

          # PowerShell streams for integration - only if explicitly enabled
          if ( $this.StreamOutput ) {
               switch ($Level) {
                    &#039;ERROR&#039;   { Write-Error       $Message -ErrorAction Continue }
                    &#039;WARNING&#039; { Write-Warning     $Message }
                    &#039;VERBOSE&#039; { Write-Verbose     $Message }
                    &#039;DEBUG&#039;   { Write-Debug       $Message }
               }
          }

          # Return object only when requested
          if ( $ReturnObject ) {
               return @{
                    HasError    = $Level -eq &#039;ERROR&#039;
                    HasWarning  = $Level -eq &#039;WARNING&#039;
                    MessageCode = if ( $MessageCode ) { $MessageCode } else { $null }
                    Message     = $Message
                    Severity    = $Severity
                    Level       = $Level
                    Target      = $Target
               }
          }

          return $null
     }

     # BLANK Line
     &#x5B;void] BlankLine() {
          $this.WriteLog(&#039;BLANK&#039;, &#039;&#039;, $null, 0, &#039;Main&#039;, $false)
     }

     # INFO Methods - standard return hashtable
     &#x5B;hashtable] Info(&#x5B;string]$Message) {
          return $this.WriteLog(&#039;INFO&#039;, $Message, $null, 0, &#039;Main&#039;, $true)
     }

     &#x5B;hashtable] Info(&#x5B;string]$Message, &#x5B;string]$MessageCode) {
          return $this.WriteLog(&#039;INFO&#039;, $Message, $MessageCode, 0, &#039;Main&#039;, $true)
     }

     # INFO NoReturn
     &#x5B;void] InfoNoReturn(&#x5B;string]$Message) {
          $this.WriteLog(&#039;INFO&#039;, $Message, $null, 0, &#039;Main&#039;, $false)
     }

     &#x5B;void] InfoNoReturn(&#x5B;string]$Message, &#x5B;string]$MessageCode) {
          $this.WriteLog(&#039;INFO&#039;, $Message, $MessageCode, 0, &#039;Main&#039;, $false)
     }

     # WARNING Methods - standard return hashtable
     &#x5B;hashtable] Warning(&#x5B;string]$Message) {
          return $this.WriteLog(&#039;WARNING&#039;, $Message, $null, 1, &#039;Main&#039;, $true)
     }

     &#x5B;hashtable] Warning(&#x5B;string]$Message, &#x5B;string]$MessageCode) {
          return $this.WriteLog(&#039;WARNING&#039;, $Message, $MessageCode, 1, &#039;Main&#039;, $true)
     }

     # WARNING NoReturn
     &#x5B;void] WarningNoReturn(&#x5B;string]$Message) {
          $this.WriteLog(&#039;WARNING&#039;, $Message, $null, 1, &#039;Main&#039;, $false)
     }

     &#x5B;void] WarningNoReturn(&#x5B;string]$Message, &#x5B;string]$MessageCode) {
          $this.WriteLog(&#039;WARNING&#039;, $Message, $MessageCode, 1, &#039;Main&#039;, $false)
     }

     # ERROR Methods - standard return hashtable
     &#x5B;hashtable] Error(&#x5B;string]$Message) {
          return $this.WriteLog(&#039;ERROR&#039;, $Message, $null, 2, &#039;Error&#039;, $true)
     }

     &#x5B;hashtable] Error(&#x5B;string]$Message, &#x5B;string]$MessageCode) {
          return $this.WriteLog(&#039;ERROR&#039;, $Message, $MessageCode, 2, &#039;Error&#039;, $true)
     }

     # ERROR NoReturn
     &#x5B;void] ErrorNoReturn(&#x5B;string]$Message) {
          $this.WriteLog(&#039;ERROR&#039;, $Message, $null, 2, &#039;Error&#039;, $false)
     }

     &#x5B;void] ErrorNoReturn(&#x5B;string]$Message, &#x5B;string]$MessageCode) {
          $this.WriteLog(&#039;ERROR&#039;, $Message, $MessageCode, 2, &#039;Error&#039;, $false)
     }

     # VERBOSE &amp; DEBUG - NoReturn
     &#x5B;void] Verbose(&#x5B;string]$Message) {
          $this.WriteLog(&#039;VERBOSE&#039;, $Message, $null, 0, &#039;Verbose&#039;, $false)
     }

     &#x5B;void] Debug(&#x5B;string]$Message) {
          $this.WriteLog(&#039;DEBUG&#039;, $Message, $null, 0, &#039;Debug&#039;, $false)
     }

     # Step/SQL/AG Methods - NoReturn
     &#x5B;void] StepStart(&#x5B;int]$Step, &#x5B;string]$StepName) {
          $this.BlankLine()
          $message = &quot;========================= STEP $Step START: $StepName =========================&quot;
          $this.WriteLog(&#039;INFO&#039;, $message, $null, 9999, &#039;Main&#039;, $false)
     }

     &#x5B;void] StepEnd(&#x5B;int]$Step, &#x5B;string]$StepName, &#x5B;bool]$Success) {
          $status   = if ( $Success ) { &quot;SUCCESS&quot; } else { &quot;FAILED&quot; }
          $message  = &quot;========================= STEP $Step END: $StepName - $status =========================&quot;
          $this.WriteLog(&#039;INFO&#039;, $message, $null, 9999, &#039;Main&#039;, $false)
          $this.BlankLine()
     }

}
</pre></div></details>



<div style="height:26px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading" style="margin-top:var(--wp--preset--spacing--60);margin-bottom:var(--wp--preset--spacing--40)">Zusammenfassung</h2>



<p class="wp-block-paragraph">PowerShell Klassen revolutionieren die Art, wie du größere Projekte strukturierst. Seit PowerShell 5.0 verfügbar, bieten sie objektorientierte Programmierung mit Eigenschaften, Methoden, Konstruktoren und Method Overloading. Der anfängliche Mehraufwand zahlt sich durch sauberen, wartbaren Code aus &#8211; besonders bei Modulen und komplexeren Funktionssammlungen.</p>



<p class="wp-block-paragraph"><strong>Die wichtigsten Vorteile auf einen Blick</strong></p>



<ul class="wp-block-list">
<li>Stark typisierte Properties mit Validierung</li>



<li>gekapselte Funktionalität durch Methoden</li>



<li>elegante Überladung statt komplexer Parameter-Logik und eine Struktur, die auch nach Monaten noch verständlich bleibt</li>
</ul>



<p class="wp-block-paragraph">Für Daily-Doing-Scripts bleibt der klassische Ansatz praktischer, aber sobald Wiederverwendbarkeit und Struktur wichtig werden, führt für mich kein Weg mehr an Klassen vorbei. 🫡</p>



<div style="height:55px" aria-hidden="true" class="wp-block-spacer"></div>


<figure style="width:500px;height:500px" class="wp-block-post-featured-image"><img loading="lazy" decoding="async" width="256" height="256" src="https://dbavonnebenan.de/wp-content/uploads/2025/06/logo-powershell-meets-classes.png" class="attachment-post-thumbnail size-post-thumbnail wp-post-image" alt="PowerShell meets Classes" style="height:500px;object-fit:cover;" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/06/logo-powershell-meets-classes.png 256w, https://dbavonnebenan.de/wp-content/uploads/2025/06/logo-powershell-meets-classes-150x150.png 150w" sizes="auto, (max-width: 256px) 100vw, 256px" /></figure><p>The post <a href="https://dbavonnebenan.de/powershell-classes-for-beginner-de/">PowerShell Klassen für Anfänger – Strukturierter Code statt Funktions-Chaos</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://dbavonnebenan.de/powershell-classes-for-beginner-de/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>TRUNCATE TABLE Operationen im Detail &#8211; ROLLBACK ist doch möglich</title>
		<link>https://dbavonnebenan.de/sql-server-truncate-vs-delete-dml-or-ddl-de/</link>
					<comments>https://dbavonnebenan.de/sql-server-truncate-vs-delete-dml-or-ddl-de/#respond</comments>
		
		<dc:creator><![CDATA[Gabriel]]></dc:creator>
		<pubDate>Mon, 26 May 2025 08:16:15 +0000</pubDate>
				<category><![CDATA[Sql Server]]></category>
		<category><![CDATA[TSql]]></category>
		<category><![CDATA[DE]]></category>
		<category><![CDATA[PERFORMANCE]]></category>
		<category><![CDATA[ROLLBACK]]></category>
		<category><![CDATA[TABLE]]></category>
		<category><![CDATA[TRANSACTION]]></category>
		<category><![CDATA[TRUNCATE]]></category>
		<guid isPermaLink="false">https://dbavonnebenan.de/?p=428</guid>

					<description><![CDATA[<p>Kürzlich bin ich über einen LinkedIn-Post zu TRUNCATE vs. DELETE gestolpert, wie so viele andere auch. Typischerweise posten zufällige Mitglieder einfache SQL Server Tipps, schön verpackt in Grafiken und allgemein gehalten. Obwohl das für Anfänger absolut legitim ist &#8211; da diese einfachen Einstiegspunkte vielen Neulingen helfen können zu verstehen und einen großartigen Startpunkt bieten &#8211; [&#8230;]</p>
<p>The post <a href="https://dbavonnebenan.de/sql-server-truncate-vs-delete-dml-or-ddl-de/">TRUNCATE TABLE Operationen im Detail – ROLLBACK ist doch möglich</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">Kürzlich bin ich über einen LinkedIn-Post zu TRUNCATE vs. DELETE gestolpert, wie so viele andere auch. Typischerweise posten zufällige Mitglieder einfache SQL Server Tipps, schön verpackt in Grafiken und allgemein gehalten. Obwohl das für Anfänger absolut legitim ist &#8211; da<strong> </strong>diese einfachen Einstiegspunkte vielen Neulingen helfen können zu verstehen und einen großartigen Startpunkt bieten &#8211; ist die Tatsache, dass dieselben Themen dann viral über verschiedene Accounts gehen, klar eine ganz andere Sache.</p>



<p class="wp-block-paragraph">Wie auch immer. Wenn du beim Thema schon etwas weiter bist und bereits weißt, dass die Aussprache nicht &#8222;SQL&#8220; sondern &#8222;Sequel&#8220; ist <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f60a.png" alt="😊" class="wp-smiley" style="height: 1em; max-height: 1em;" />, solltest du diese Posts grundsätzlich hinterfragen, da sie meist verallgemeinern.</p>



<p class="wp-block-paragraph">So war es auch bei einem Post über die Top 3 SQL-Fehler, die &#8222;jeder&#8220; macht. Und hier kommen wir zum Punkt &#8211; TRUNCATE TABLE und seine Besonderheiten.</p>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image aligncenter size-full"><a href="https://www.linkedin.com/posts/samiul-fahim_sql-dataengineering-techtips-activity-7328642722653786112-OBfj?utm_source=share&amp;utm_medium=member_desktop&amp;rcm=ACoAACt1yIMBIZu3k0gdHLx7c_luIAnHYYBMtuc"><img loading="lazy" decoding="async" width="391" height="158" src="https://dbavonnebenan.de/wp-content/uploads/2025/05/2025-05-26_09-15.png" alt="TRUNCATE kann nicht zurückgerollt werden" class="wp-image-324" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/05/2025-05-26_09-15.png 391w, https://dbavonnebenan.de/wp-content/uploads/2025/05/2025-05-26_09-15-300x121.png 300w" sizes="auto, (max-width: 391px) 100vw, 391px" /></a></figure>



<p class="has-text-align-center wp-block-paragraph"><a href="https://www.linkedin.com/posts/samiul-fahim_sql-dataengineering-techtips-activity-7328642722653786112-OBfj?utm_source=share&amp;utm_medium=member_desktop&amp;rcm=ACoAACt1yIMBIZu3k0gdHLx7c_luIAnHYYBMtuc" target="_blank" rel="noreferrer noopener">LinkedIn</a></p>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">Dasselbe passierte mit <a href="https://www.linkedin.com/in/vedrankesegic/">Vedran Kesegic</a>, der den Hinweis gab, dass TRUNCATE TABLE anscheinend rollback-fähig ist, wenn es in einer Transaktion gekapselt wird. Dann sprang <a href="https://www.linkedin.com/in/saastamoinen/">Carsten Saastamoinen-Jakobsen</a> ein und die Sache wurde wild. So wild, dass mich das ganze Thema dermaßen gepackt hat, dass ich einen Deep Dive gemacht habe. Außerdem fiel in diesem Austausch so viel Wissen ab, dass es schade wäre, wenn es in einem Kommentarbereich verschwindet.</p>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Was dich hier erwartet &#8211; TRUNCATE TABLE im Detail</h2>



<p class="wp-block-paragraph">Ich habe den Austausch verglichen und mir die Kernaussagen angeschaut. Ich habe auch separat mit beiden etwas mehr getextet, und Carsten gab mir Auszüge aus einer seiner Publikationen, wo das Thema technisch behandelt wird.</p>



<p class="wp-block-paragraph">Technisch gesehen sind sich beide einig, auch wenn sie es nicht sagen <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f923.png" alt="🤣" class="wp-smiley" style="height: 1em; max-height: 1em;" />. Aber was am Ende wirklich wild war, war die Frage zum TRUNCATE TABLE Befehl:</p>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image aligncenter size-full"><img loading="lazy" decoding="async" width="595" height="159" src="https://dbavonnebenan.de/wp-content/uploads/2025/05/ddl_dml.png" alt="DML oder DDL - TRUNCATE TABLE Klassifizierung" class="wp-image-326" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/05/ddl_dml.png 595w, https://dbavonnebenan.de/wp-content/uploads/2025/05/ddl_dml-300x80.png 300w" sizes="auto, (max-width: 595px) 100vw, 595px" /></figure>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-461897f4 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:100%">
<div class="wp-block-group is-nowrap is-layout-flex wp-container-core-group-is-layout-461897f4 wp-block-group-is-layout-flex">
<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="246" height="470" src="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_standing_1.png" alt="" class="wp-image-330" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_standing_1.png 246w, https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_standing_1-157x300.png 157w" sizes="auto, (max-width: 246px) 100vw, 246px" /></figure>



<p class="wp-block-paragraph">Ich kann es vorwegnehmen. Diese Frage zur TRUNCATE TABLE Klassifizierung ist eine philosophische. Ich konnte mir basierend auf meiner Recherche eine Meinung bilden, mehr dazu später, aber grundsätzlich liegt es an jedem selbst zu entscheiden.</p>
</div>
</div>
</div>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Die Diskussion &#8211; TRUNCATE TABLE als DML oder DDL?</h2>



<h3 class="wp-block-heading">Vedran Kesegic</h3>



<p class="wp-block-paragraph"><strong>Kernposition: TRUNCATE TABLE ist DDL</strong></p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">&#8222;TRUNCATE modifies internal system tables sys.sysallocunits and sys.sysrowsets. DELETE modifies indexes and stats&#8220;</p>
</blockquote>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">&#8222;DML writes data (rows in user tables), while DDL writes metadata (rows in system tables, data that describes other/user objects). Truncate happens to write metadata (system tables) and thus is classified as DDL.&#8220;</p>
</blockquote>



<p class="wp-block-paragraph"><strong>Technische Argumente für TRUNCATE TABLE als DDL</strong></p>



<ul class="wp-block-list">
<li>Verwendet SCH-M (Schema Modification) Lock</li>



<li>Modifiziert System-Metadaten-Tabellen</li>



<li>Microsoft-Dokumentation klassifiziert es als DDL</li>



<li>Loggt Page-Deallocations, nicht Row-Deletions</li>
</ul>



<p class="wp-block-paragraph"><strong>Kernaussage zum TRUNCATE TABLE Befehl</strong></p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">&#8222;TRUNCATE modifies internal system tables sys.sysallocunits and sys.sysrowsets. DELETE modifies indexes and stats &#8211; see attached&#8220;</p>
</blockquote>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">Carsten Saastamoinen-Jakobsen</h3>



<p class="wp-block-paragraph"><strong>Kernposition: TRUNCATE TABLE ist DML</strong></p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">&#8222;It is not an argument to call TRUNCATE a DDL statement because a certain lock is used! When statements are categorized, it must be based on what happens and not how a given database system solves the task.&#8220;</p>
</blockquote>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">&#8222;We may have to recognize that a previous error categorization should not continue to call TRUNCATE a DDL, but recognize that it is a DML.&#8220;</p>
</blockquote>



<p class="wp-block-paragraph"><strong>Logische Argumente für TRUNCATE TABLE als DML</strong></p>



<ul class="wp-block-list">
<li>Daten werden gelöscht, Struktur bleibt erhalten</li>



<li>Keine Änderungen an der Tabellendefinition</li>



<li>Folgt semantischen Regeln der Datenmanipulation</li>



<li>Kann nur bestimmte Partitionen löschen (nicht alle Daten)</li>
</ul>



<p class="wp-block-paragraph"><strong>Kernaussage zur TRUNCATE TABLE Klassifizierung</strong></p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">&#8222;I say that MS is right in the documentations because they are not telling that TRUNCATE is as DDL, because they know that TRUNCATE is a DML!&#8220;</p>



<p class="wp-block-paragraph"><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-denary-color"><em>ANMERKUNG Gabriel <br></em></mark><em>Ich denke, das ist wirklich etwas fragwürdig, weil die Dokumentation absolut klar</em> <em>zu diesem Punkt ist</em> &#8211;&gt; <strong><em>DDL </em></strong>(<a href="https://learn.microsoft.com/en-us/sql/t-sql/statements/statements?view=sql-server-ver17#data-definition-language" target="_blank" rel="noreferrer noopener">microsoft.com</a>)<br></p>
</blockquote>



<p class="wp-block-paragraph"><strong>Zitat aus der Publikation zum TRUNCATE TABLE</strong></p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">&#8222;As of version 2016, it is possible to delete only some of the data in a table with TRUNCATE, so it is even more cor­rect to call it manipulation than definition, because it is changing some of the data but not changing the structure.&#8220;</p>
</blockquote>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">Einigungspunkte bei TRUNCATE TABLE</h3>



<ul class="wp-block-list">
<li>TRUNCATE TABLE kann in einer Transaktion zurückgerollt werden
<ul class="wp-block-list">
<li>allgemeine Demo bei <a href="https://blog.sqlauthority.com/2010/03/04/sql-server-rollback-truncate-command-in-transaction/" target="_blank" rel="noreferrer noopener">sqlauthority.com</a></li>



<li>detaillierte Demo <a href="https://www.sqlshack.com/the-internals-of-sql-truncate-and-sql-delete-statements/" target="_blank" rel="noreferrer noopener">sqlshack.com</a></li>
</ul>
</li>



<li>TRUNCATE TABLE ist eine geloggte Operation</li>



<li>TRUNCATE TABLE ist schneller als DELETE bei großen Datensätzen</li>



<li>Beide verstehen die technische Implementierung</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">Hauptstreitpunkt bei TRUNCATE TABLE</h3>



<ul class="wp-block-list">
<li><strong>Klassifizierungskriterien für TRUNCATE TABLE</strong>
<ul class="wp-block-list">
<li>Implementierungsdetails</li>
</ul>
</li>



<li><strong>Autorität</strong>
<ul class="wp-block-list">
<li>Vendor-Dokumentation (Vedran) vs. Logische Begründung (Carsten)</li>
</ul>
</li>



<li><strong>Perspektive zum TRUNCATE TABLE Befehl</strong>
<ul class="wp-block-list">
<li>Wie es intern funktioniert (Vedran) vs. Was es funktional macht (Carsten)</li>
</ul>
</li>
</ul>



<div style="height:50px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">Meine Einschätzung zu TRUNCATE TABLE</h3>



<p class="wp-block-paragraph">Wie du siehst, passt es technisch, aber wie betrachtest du das Ganze? Vielleicht ist es gut, dass ich nicht mit so viel Wissen vorab beladen bin wie beide, denn aus meiner Sicht ist es ziemlich klar: <strong>TRUNCATE TABLE ist DDL!!</strong></p>



<div class="wp-block-group is-nowrap is-layout-flex wp-container-core-group-is-layout-461897f4 wp-block-group-is-layout-flex">
<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="292" height="342" src="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_1.png" alt="" class="wp-image-344" style="width:98px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_1.png 292w, https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_1-256x300.png 256w" sizes="auto, (max-width: 292px) 100vw, 292px" /></figure>



<p class="wp-block-paragraph">Entgegen Carstens Aussage, dass keine Systemtabellen geändert werden, ist Vedrans Aussage absolut korrekt. Sieh warum&#8230; </p>
</div>



<p class="wp-block-paragraph">&#8230; und natürlich weil Microsoft sagt, dass TRUNCATE TABLE DDL ist <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f60a.png" alt="😊" class="wp-smiley" style="height: 1em; max-height: 1em;" /> ( <a href="https://learn.microsoft.com/en-us/sql/t-sql/statements/statements?view=sql-server-ver17#data-definition-language" target="_blank" rel="noreferrer noopener">microsoft.com</a> )</p>



<p class="wp-block-paragraph">Und Partitionierung ist DML… ich weiß nicht, auch hier sind es zumindest Systemspalten, die die Partitionierung definieren.</p>



<p class="wp-block-paragraph"></p>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Wie TRUNCATE TABLE funktioniert &#8211; Schritt für Schritt</h2>



<p class="wp-block-paragraph">Ich würde gerne sagen, dass ich es einfach beschreibe, aber das ist einfach meine normale Beschreibung. Das Thema TRUNCATE TABLE ist wirklich komplex <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/270c.png" alt="✌" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">1 &#8211; TRUNCATE TABLE Befehl initiiert</h3>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
TRUNCATE TABLE dbo.MyTable;
</pre></div>


<ul class="wp-block-list">
<li><strong>Erste Aktion</strong>: Akquiriert <strong>SCH-M (Schema Modification)</strong> Lock auf gesamte Tabelle</li>



<li><strong>Effekt</strong>: Tabelle ist komplett gesperrt &#8211; keine Reads, keine Writes, nichts</li>
</ul>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">2 &#8211; Extents identifizieren bei TRUNCATE TABLE</h3>



<ul class="wp-block-list">
<li>DEEP DIVE Extents &#8211;&gt; <a href="https://learn.microsoft.com/en-us/sql/relational-databases/pages-and-extents-architecture-guide?view=sql-server-ver17" target="_blank" rel="noreferrer noopener">microsoft.com</a></li>



<li>SQL Server liest IAM-Pages</li>



<li>Listet alle Extents auf, die zur Tabelle gehören</li>



<li><strong>Hält weiterhin</strong>: SCH-M Lock auf Tabelle</li>
</ul>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">3 &#8211; Einzelne Extents bei TRUNCATE TABLE sperren</h3>



<ul class="wp-block-list">
<li>Platziert <strong>X (Exclusive)</strong> Locks auf jeden Extent</li>



<li>Das sind reguläre Data-Locks, KEINE Schema-Locks</li>



<li><strong>Hält jetzt</strong>:
<ul class="wp-block-list">
<li>SCH-M Lock auf Tabelle (verhindert Zugriff)</li>



<li>X Locks auf Extents (markiert sie für Deallocation)</li>
</ul>
</li>
</ul>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">4 &#8211; TRUNCATE TABLE Operation loggen</h3>



<ul class="wp-block-list">
<li>Schreibt ins Transaction-Log: &#8222;Deallocate extents 8:8, 8:16, etc.&#8220;</li>



<li><strong>Hält weiterhin</strong>: Beide Lock-Typen</li>
</ul>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">5a &#8211; Bei COMMIT der TRUNCATE TABLE Operation</h3>



<ul class="wp-block-list">
<li>Aktualisiert IAM &#8211; markiert Extents als frei</li>



<li><strong>Gibt frei</strong> alle X Locks auf Extents</li>



<li><strong>Gibt frei</strong> SCH-M Lock auf Tabelle</li>



<li>Tabelle ist wieder zugänglich</li>
</ul>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">5b &#8211; Bei ROLLBACK der TRUNCATE TABLE Operation</h3>



<ul class="wp-block-list">
<li>Verwirft Deallocation-Marker</li>



<li><strong>Gibt frei</strong> alle X Locks auf Extents</li>



<li><strong>Gibt frei</strong> SCH-M Lock auf Tabelle</li>



<li>Alles bleibt unverändert</li>
</ul>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">Warum zwei verschiedene Lock-Typen bei TRUNCATE TABLE?</h3>



<p class="wp-block-paragraph"><strong>SCH-M Lock:</strong></p>



<ul class="wp-block-list">
<li>&#8222;Niemand rührt diese Tabelle ÜBERHAUPT an&#8220;</li>



<li>Typisch für DDL-Operationen</li>



<li>Verhindert sogar Metadaten-Abfragen</li>
</ul>



<p class="wp-block-paragraph"><strong>X Locks auf Extents:</strong></p>



<ul class="wp-block-list">
<li>&#8222;Diese spezifischen Datenblöcke werden modifiziert&#8220;</li>



<li>Typisch für Daten-Operationen</li>



<li>Stellt sicher, dass Extents nicht von anderen Sessions wiederverwendet werden bis commit/rollback</li>
</ul>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">Meine bescheidene Meinung zu TRUNCATE TABLE</h3>



<p class="wp-block-paragraph">Mit diesem Wissen ist der Fall eigentlich klar für mich, aber wie siehst du es? Tatsächlich werden hier keine Benutzerdaten manipuliert; stattdessen werden System-Level-Zuweisungen angepasst. Folglich erlaube ich mir, trotz meines neuen Halbwissens, den folgenden Vergleich zu ziehen:</p>



<div class="wp-block-group is-nowrap is-layout-flex wp-container-core-group-is-layout-461897f4 wp-block-group-is-layout-flex">
<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="307" height="328" src="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_2.png" alt="" class="wp-image-345" style="width:131px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_2.png 307w, https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_2-281x300.png 281w" sizes="auto, (max-width: 307px) 100vw, 307px" /></figure>



<p class="wp-block-paragraph">&#8222;Ich sage auch nicht, dass ich Text in einem Word-Dokument lösche, wenn ich die Festplatte schnell formatiere und nur die Block-Allocation lösche.&#8220;</p>
</div>



<p class="wp-block-paragraph"><br>Ja, ich denke das ist ein schöner Vergleich&#8230; Fall abgeschlossen, jetzt ein paar technisch interessante Aspekte und Lösungen zum TRUNCATE TABLE Befehl</p>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Technischer Teil, Hacks und TRUNCATE TABLE How-To</h2>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="643" height="388" src="https://dbavonnebenan.de/wp-content/uploads/2025/05/meme_trunc.jpg" alt="" class="wp-image-348" style="width:325px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/05/meme_trunc.jpg 643w, https://dbavonnebenan.de/wp-content/uploads/2025/05/meme_trunc-300x181.jpg 300w" sizes="auto, (max-width: 643px) 100vw, 643px" /></figure>



<p class="has-text-align-center has-medium-font-size wp-block-paragraph">&#8230; praktisch, nicht technisch <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f609.png" alt="😉" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">TRUNCATE TABLE mit Survivor Table (Temporary Table)</h3>



<p class="wp-block-paragraph">Daten, die noch benötigt werden, werden einfach in eine temporäre Tabelle verschoben und nach TRUNCATE TABLE wieder abgerufen</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
-- Step 1: Create temp table with rows to keep
	SELECT * INTO #SurvivorRows 
	FROM 
		MyTable 
	WHERE
		DateColumn &gt;= &#039;2024-01-01&#039;

-- Step 2: Truncate the entire table
	TRUNCATE TABLE MyTable

-- Step 3: Re-insert survivor rows
	INSERT INTO MyTable
	SELECT * FROM #SurvivorRows

-- Step 4: Clean up
	DROP TABLE #SurvivorRows
</pre></div>


<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">TRUNCATE TABLE mit PARTITION Option (2016++)</h3>



<p class="wp-block-paragraph">Seit SQL Server 2016 (jede Edition) ist es möglich, Partitionen oder Bereiche von Partitionen bei TRUNCATE TABLE zu spezifizieren</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
-- Truncate specific partitions
	TRUNCATE TABLE MyPartitionedTable 
	     WITH (PARTITIONS (2, 4 TO 6))

-- Check partition status
	SELECT 
		 partition_number
		,rows 
	FROM
		sys.partitions 
	WHERE 
		OBJECT_NAME(OBJECT_ID) = &#039;MyPartitionedTable&#039;
</pre></div>


<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">TRUNCATE TABLE ist immer eine Transaktion&#8230;</h3>



<p class="wp-block-paragraph">&#8230;. und muss beim RESTORE berücksichtigt werden. Carsten hat das ganze sehr schön in seiner Publikation illustriert. Ich beschreibe es nur, das muss an dieser Stelle reichen <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f61d.png" alt="😝" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p class="wp-block-paragraph">Obwohl wir nur <strong>TRUNCATE TABLE</strong> schreiben, verwendet SQL Server trotzdem <strong>BEGIN</strong> und <strong>COMMIT TRANSACTION</strong>, was natürlich im TLog aufgezeichnet wird. Entsprechend bleiben bei einer POINT IN TIME RECOVERY die Daten in der Tabelle verfügbar, bis das TLog mit der entsprechenden TRANSACTION wiederhergestellt wird, oder die Zeit der TRUNCATE TABLE Transaktion überschritten wurde.</p>



<p class="wp-block-paragraph">Du kannst also auch die Daten wiederherstellen <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f44c.png" alt="👌" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">Transaction Log File Daten kontinuierlich lesen mit fn_dblog und fn_dump_dblog</h3>



<p class="wp-block-paragraph">Vedran schrieb in einem seiner Posts, dass es eine undokumentierte Funktion gibt, wo du Log-Records zur Laufzeit lesen kannst. Es gibt auch einen sehr coolen Artikel dazu auf sqlshack.com</p>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<p class="has-text-align-center wp-block-paragraph"><em><mark style="background-color:rgba(0, 0, 0, 0)" class="has-inline-color has-septenary-color">NICHT empfohlen für Produktion, sondern nur für Test / Bildung.</mark></em></p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>


https://www.sqlshack.com/how-to-continuously-read-transaction-log-file-data-directly-in-a-sql-server-database-with-fn_dblog-and-fn_dump_dblog/


<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Fazit zu TRUNCATE TABLE</h2>



<div class="wp-block-group is-nowrap is-layout-flex wp-container-core-group-is-layout-461897f4 wp-block-group-is-layout-flex">
<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="284" height="334" src="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_3.png" alt="" class="wp-image-355" style="width:124px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_3.png 284w, https://dbavonnebenan.de/wp-content/uploads/2025/05/emotes_3-255x300.png 255w" sizes="auto, (max-width: 284px) 100vw, 284px" /></figure>



<p class="wp-block-paragraph">Kommen wir zu meinem eingangs erwähnten Punkt, dass all diese SQL Server Tipps von randoms hinterfragt werden sollten. Denn die Aussage, dass <strong>TRUNCATE TABLE nicht rollback-sicher</strong> ist, ist schlichtweg falsch. Der Weg zu dieser Erkenntnis war für mich lang, eingeleitet durch die Unterhaltung zwischen Vedran und Carsten. Ich habe wirklich viel links und rechts dazugelernt, was ich auch hier nochmal für die Nachwelt festhalten wollte.</p>
</div>



<p class="wp-block-paragraph">Am Ende ist es egal, ob TRUNCATE TABLE <strong>DDL</strong> oder <strong>DML</strong> ist. Im allgemeinen SQL Server Standard wird es beispielsweise als DML beschrieben, die meisten RDBMSs im Dialekt mit DDL. Wir einigen uns einfach auf <strong>DDL</strong> und gut ist. Viel spannender ist, was unter der Haube beim TRUNCATE TABLE Befehl passiert.</p>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph"><strong>Bester Kommentar von Vedran <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f923.png" alt="🤣" class="wp-smiley" style="height: 1em; max-height: 1em;" /></strong></p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="has-overpass-mono-font-family wp-block-paragraph">&#8222;Or you are trying to argue with SQL Certified Master about SQL? Good luck Mr :)&#8220;</p>
</blockquote>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Links aus dem Text</h2>


https://learn.microsoft.com/en-us/sql/t-sql/statements/statements?view=sql-server-ver17#data-definition-language

https://learn.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql?view=sql-server-ver17

https://blog.sqlauthority.com/2010/03/04/sql-server-rollback-truncate-command-in-transaction/

https://www.sqlshack.com/the-internals-of-sql-truncate-and-sql-delete-statements/

https://www.sqlshack.com/how-to-continuously-read-transaction-log-file-data-directly-in-a-sql-server-database-with-fn_dblog-and-fn_dump_dblog/

https://en.wikipedia.org/wiki/Truncate_(SQL)

https://learn.microsoft.com/en-us/sql/relational-databases/pages-and-extents-architecture-guide?view=sql-server-ver17


<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<div style="height:32px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Full Conversation</h2>



<details class="wp-block-details is-layout-flow wp-block-details-is-layout-flow"><summary>Expand</summary>
<p class="wp-block-paragraph"><strong>Vedran Kesegic</strong></p>



<p class="wp-block-paragraph">In sql server, truncate can be rolled back (if encapsulated within transaction). It is a logged operation.</p>



<p class="wp-block-paragraph"><strong>Carsten Saastamoinen-Jakobsen</strong></p>



<p class="wp-block-paragraph">With DELETE we can specify the WHERE clause and some or all rows can be removed. All deleted rows are logged with BEFORE IMAGE/the data values. With TRUNCATE we can delete all rows or only the rows in one or more partitions, if we have a partition table. The only information logged is the pageid. The physical removing/releasing to unused pages happens in a background task after COMMIT. No data is stored in the log, so less and faster! And it is not possible to restore to point in time because no data is logged. The pageid in the log can already have being &#8218;released&#8216; to unused pages and therefore already used to data from another object &#8211; table/index. Before COMMIT the pages is still allocated to the table and still with data. If no explicit transaction is specified the transaction is already committed!</p>



<p class="wp-block-paragraph"><strong>Vedran Kesegic</strong></p>



<p class="wp-block-paragraph">&#8222;No data is stored in the log&#8220; and &#8222;not possible to restore to point in time&#8220; is both incorrect for TRUNCATE. The difference is, truncate logs only deallocation of pages, which is multiple orders of magnitude less (basically insignifficant) compared to DELETE which logs data that is deleted. That is why truncate is &#8222;instant&#8220;. If you delete eg. 10GB of data, you wait for 10GB to be written to transaction log, which takes time. Truncate logs maybe 1KB for deallocation info, which is instant. If you do not want to delete all rows, you can still use TRUNCATE by storing survivor rows aside before truncating, and inserting them back after truncate. Even better, you can use SWITCH into some dummy table (even with not-partitioned tables) to remove rows quickly, and then insert survivor rows back from that dummy into original table. That is the fastest way to delete when a smaller percent of rows needs to survive.</p>



<p class="wp-block-paragraph"><strong>Vedran Kesegic</strong></p>



<p class="wp-block-paragraph">Obvious difference is that truncate removes all rows from table (&#8222;truncate table&#8220;) or from partition(s), when using &#8222;trancate table xy with partitions (&#8230;)&#8220;. You cannot choose which rows to delete, they all are gone. With delete you can have a WHERE clause to choose which rows to delete. Delete is more appropriate if you need to delete a small percentage of table rows (or partition rows).</p>



<p class="wp-block-paragraph"><strong>Vedran Kesegic</strong></p>



<p class="wp-block-paragraph">Truncate is a metadata operation, requires a different lock (SCH_M, schema modification lock). If also has some restrictins regarding foreign keys etc. Check the documentation for the list of limitations: https://learn.microsoft.com/en-us/sql/t-sql/statements/truncate-table-transact-sql</p>



<p class="wp-block-paragraph"><strong>Carsten Saastamoinen-Jakobsen</strong></p>



<p class="wp-block-paragraph">It is not an argument to call TRUNCATE a DDL statement because a certain lock is used! When statements are categorized, it must be based on what happens and not how a given database system solves the task. Data is deleted, the structure is preserved and only some data may be deleted. When one or more partitions are deleted, the condition is also well-known, but just limited. In previous versions of database systems &#8211; and perhaps still &#8211; a TRUNCATE was performed by a DROP TABLE + CREATE TABLE. Hence DDL rights. It just shows that it is problematic to categorize based on how it is performed and not based on what actually happens. If we look at the documentation for SQL Server, it is not mentioned that TRUNCATE is a DDL. We may have to recognize that a previous error categorization should not continue to call TRUNCATE a DDL, but recognize that it is a DML. We will also be free from erroneous claims about only deleting all rows, that space is not released, that the transaction cannot be rolled back, &#8230;. A DELETE operation without a user-defined transaction cannot be rolled back either! But DELETE is DML and not DDL. TRUNCATE is &#8218;created&#8216; for having af fast way of deleting rows, and extended for only deleting some of the rows &#8211; DML!!!</p>



<p class="wp-block-paragraph"><strong>Vedran Kesegic</strong></p>



<p class="wp-block-paragraph">No, the SCH-M lock (the &#8222;king of all locks&#8220;) is not the sole reason why TRUNCATE is DDL. DML writes data (rows in user tables), while DDL writes metadata (rows in system tables, data that describes other/user objects). Truncate happens to write metadata (system tables) and thus is classified as DDL. See the doc: https://learn.microsoft.com/en-us/previous-versions/sql/sql-server-2012/ff848799(v=sql.110)</p>



<p class="wp-block-paragraph"><strong>Carsten Saastamoinen-Jakobsen</strong></p>



<p class="wp-block-paragraph">Can you tell which data is written to the system tables that is not written when removing rows by DELETE? The only additional information is, that with TRUNCATE of all rows without specifying partitions is, that the &#8218;identity&#8216; is reset to the initial values. Removing some or all rows with specifying partitions, this value is not reset to the initial value. Empty page are handled in the same way as empty page after DELETE! All indexes are preserved, the table definition is NOT changed, &#8230;.</p>



<p class="wp-block-paragraph"><strong>Vedran Kesegic</strong></p>



<p class="wp-block-paragraph">Better than that &#8211; you can see for yourself! The exact difference and details are present in transaction log records after you do TRUNCATE and compare that to log records that DELETE generates. Use fn_dblog() function, but only on the test system because it is undocumented and might have an impact.</p>



<p class="wp-block-paragraph">Or you are trying to say that Microsoft is wrong in documentation? If so, report it to MS so doc can be fixed. But in this case there is nothing to fix.</p>



<p class="wp-block-paragraph">Or you are trying to argue with SQL Certified Master about SQL? Good luck Mr <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<p class="wp-block-paragraph"><strong>Carsten Saastamoinen-Jakobsen</strong></p>



<p class="wp-block-paragraph">I say that MS is right in the documentations because they are not telling that TRUNCATE is as DDL, because they know that TRUNCATE is a DML!. Of course there is less in the log &#8211; it is the idea wit TRUNCATE!!!!!!!! Just te functionality and te reason for implementing TRUNCATE as an alternative to DELETE. But the log is NOT the system tables!!!!! So please tell which system tables are updated as they are updated when CRAETE, ALTER and DROP but NOT updated when INSERT, DELETE or UPDATE.</p>



<p class="wp-block-paragraph"><strong>Vedran Kesegic</strong></p>



<p class="wp-block-paragraph">TRUNCATE modifies internal system tables sys.sysallocunits and sys.sysrowsets. DELETE modifies indexes and stats &#8211; see attached pictures. You could do that yourself using fn_dblog as instructed, but I did that for you so you don&#8217;t have to. If you want to know more about deep SQL Server internals you can attend one of my trainings, or trainings of other SQL Masters like Brent Ozar, Paul Randal (sqlskills), etc.</p>



<p class="wp-block-paragraph"><strong>Carsten Saastamoinen-Jakobsen</strong></p>



<p class="wp-block-paragraph">Of course they are modified as INSERT DELETE and UPDATE modify this informations. But INSERT, DELETE and UPDATE are still only DML and not DDL. And I am not impressed of your job by showing me this information. If you was showing the same for the other DML statements it would be fine. So maybe you should modify your course material and also shows the same for the other DML statements!!!!</p>
</details>



<p class="wp-block-paragraph"></p><p>The post <a href="https://dbavonnebenan.de/sql-server-truncate-vs-delete-dml-or-ddl-de/">TRUNCATE TABLE Operationen im Detail – ROLLBACK ist doch möglich</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://dbavonnebenan.de/sql-server-truncate-vs-delete-dml-or-ddl-de/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SysAdmin Role ignoriert das Default User Schema – Eine verwirrende Fehlersuche</title>
		<link>https://dbavonnebenan.de/sql-server-sysadmin-role-can-override-the-default-schema-de/</link>
					<comments>https://dbavonnebenan.de/sql-server-sysadmin-role-can-override-the-default-schema-de/#respond</comments>
		
		<dc:creator><![CDATA[Gabriel]]></dc:creator>
		<pubDate>Thu, 08 May 2025 10:13:05 +0000</pubDate>
				<category><![CDATA[TSql]]></category>
		<category><![CDATA[DBO]]></category>
		<category><![CDATA[DE]]></category>
		<category><![CDATA[schema]]></category>
		<category><![CDATA[SYSADMIN]]></category>
		<guid isPermaLink="false">https://dbavonnebenan.de/?p=434</guid>

					<description><![CDATA[<p>Heute hatte ich wieder einen Fall auf dem Tisch, bei dem eine Fehlermeldung einer Anwendung besagte, dass auf ein Objekt in der SQL Server DB nicht zugegriffen werden kann. Der Hersteller behauptete &#8222;Verbindungsproblem&#8220;, ich sagte &#8222;DB oder zumindest Server-Problem&#8220;, Kunde sagt &#8222;früher hat alles funktioniert&#8220;. Die Extended Events waren schnell aufgesetzt, &#8222;Remote Procedure Call Starting&#8220; [&#8230;]</p>
<p>The post <a href="https://dbavonnebenan.de/sql-server-sysadmin-role-can-override-the-default-schema-de/">SysAdmin Role ignoriert das Default User Schema – Eine verwirrende Fehlersuche</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">Heute hatte ich wieder einen Fall auf dem Tisch, bei dem eine Fehlermeldung einer Anwendung besagte, dass auf ein Objekt in der SQL Server DB nicht zugegriffen werden kann. Der Hersteller behauptete &#8222;Verbindungsproblem&#8220;, ich sagte &#8222;DB oder zumindest Server-Problem&#8220;, Kunde sagt &#8222;früher hat alles funktioniert&#8220;.</p>



<p class="wp-block-paragraph">Die Extended Events waren schnell aufgesetzt, &#8222;Remote Procedure Call Starting&#8220; zeigt mir ein Statement ohne vollqualifizierten Tabellennamen. Das Zielobjekt liegt in der Database unter dem db_owner Schema. Najaaa, das ist schon fragwürdig, aber OK. Warum nicht als dbo belassen?</p>



<p class="wp-block-paragraph">Jedenfalls war mir das Verhalten an diesem Punkt wirklich unverständlich, da alles korrekt konfiguriert war: Default Schema des Users, db_owner auf der DB. Und der SysAdmin. Das war mir aufgefallen, aber nun ja, Entscheidung des Kunden. Sowas notiere ich mir in einer Session, ändere aber nichts, um gewachsene Abhängigkeiten nicht zu gefährden.</p>



<p class="wp-block-paragraph">Nach langem Suchen gab ich fast auf, verwies an den Vendor und beendete den Call. Leider kann ich solche Sachen nicht loslassen, also feuerte ich das Lab an, baute die Umgebung nach und siehe da, fand den Fehler.</p>



<p class="wp-block-paragraph">Das Problem ist die SysAdmin-Rolle. Sie mappt den Login direkt auf den dbo-User der Database &#8211; egal ob der eigentliche User existiert oder nicht, erbt somit das Default Schema von dbo. Mindblowing, wusste ich nicht. 🙂</p>



<div style="height:13px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">SysAdmin Role inaktiv</h2>



<figure class="wp-block-image size-full is-resized has-custom-border"><img loading="lazy" decoding="async" width="655" height="449" src="https://dbavonnebenan.de/wp-content/uploads/2025/05/2025-05-08_11-49_1.png" alt="" class="has-border-color has-base-border-color wp-image-285" style="border-width:1px;width:641px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/05/2025-05-08_11-49_1.png 655w, https://dbavonnebenan.de/wp-content/uploads/2025/05/2025-05-08_11-49_1-300x206.png 300w" sizes="auto, (max-width: 655px) 100vw, 655px" /></figure>



<div style="height:13px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">SysAdmin Role aktiv</h2>



<figure class="wp-block-image size-full is-resized has-custom-border"><img loading="lazy" decoding="async" width="667" height="351" src="https://dbavonnebenan.de/wp-content/uploads/2025/05/2025-05-08_11-50.png" alt="" class="has-border-color has-base-border-color wp-image-287" style="border-width:1px;width:631px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/05/2025-05-08_11-50.png 667w, https://dbavonnebenan.de/wp-content/uploads/2025/05/2025-05-08_11-50-300x158.png 300w" sizes="auto, (max-width: 667px) 100vw, 667px" /></figure>



<figure class="wp-block-image size-large is-resized has-custom-border"><img loading="lazy" decoding="async" width="504" height="212" src="https://dbavonnebenan.de/wp-content/uploads/2025/05/2025-05-08_11-13.png" alt="" class="has-border-color has-base-border-color wp-image-281" style="border-width:1px;width:495px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/05/2025-05-08_11-13.png 504w, https://dbavonnebenan.de/wp-content/uploads/2025/05/2025-05-08_11-13-300x126.png 300w" sizes="auto, (max-width: 504px) 100vw, 504px" /></figure>



<div style="height:13px" aria-hidden="true" class="wp-block-spacer"></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<div style="height:13px" aria-hidden="true" class="wp-block-spacer"></div>



<details class="wp-block-details is-layout-flow wp-block-details-is-layout-flow"><summary>T-SQL CODE für die Screenshots</summary><div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
DECLARE @IsSysAdmin INT
SELECT @IsSysAdmin = IS_SRVROLEMEMBER(&#039;sysadmin&#039;)


SELECT SYSTEM_USER AS CurrentLoginName;

SELECT 
    &#039;Current User&#039;  AS Info,
    USER_NAME()     AS Username,
    SCHEMA_NAME()   AS CurrentSchema,
    CASE WHEN @IsSysAdmin = 1 THEN &#039;Yes&#039; ELSE &#039;No&#039; END 
                    AS IsSysAdmin


SELECT 
    &#039;Default Schema&#039;        AS Info,
    dp.name                 AS Username,
    dp.default_schema_name AS DefaultSchema
FROM 
    sys.database_principals dp
WHERE 
    dp.name = USER_NAME()


SELECT 
    &#039;DB Role Membership&#039;    AS Info,
    USER_NAME()             AS Username,
    r.name                  AS RoleName
FROM 
    sys.database_principals u
JOIN 
    sys.database_role_members m ON u.principal_id = m.member_principal_id
JOIN 
    sys.database_principals r ON m.role_principal_id = r.principal_id
WHERE 
    u.name = USER_NAME()

--------------------

-- select * from &#x5B;customSchema].&#x5B;userTable] &lt;&lt;&lt;&lt;&lt; ALWAYS WORKING !!!!!

 select * from &#x5B;userTable]



</pre></div></details>



<div style="height:13px" aria-hidden="true" class="wp-block-spacer"></div>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<div class="wp-block-group is-nowrap is-layout-flex wp-container-core-group-is-layout-461897f4 wp-block-group-is-layout-flex">
<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="283" height="292" src="https://dbavonnebenan.de/wp-content/uploads/2025/05/2025-05-08_11-43.png" alt="" class="wp-image-284"/></figure>



<ul class="wp-block-list">
<li>Verwende immer vollqualifizierte Objektnamen, irgendwann wirst du froh sein, dass du es getan hast. Das kannst du in vielen Blogs lesen.</li>



<li>Arbeite immer nach POLP (<a href="https://en.wikipedia.org/wiki/Principle_of_least_privilege">Principle of Least Privilege</a>), ein App-User sollte keine SysAdmin-Rechte haben! 🙂</li>
</ul>
</div>



<p class="has-text-align-center wp-block-paragraph"><strong>Gabriel, der DBAvonNebenan</strong></p>



<p class="wp-block-paragraph"></p><p>The post <a href="https://dbavonnebenan.de/sql-server-sysadmin-role-can-override-the-default-schema-de/">SysAdmin Role ignoriert das Default User Schema – Eine verwirrende Fehlersuche</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://dbavonnebenan.de/sql-server-sysadmin-role-can-override-the-default-schema-de/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Dokumentation von Blocks und Deadlocks &#8211; Wenn der 3rd-Party Vendor Beweise fordert</title>
		<link>https://dbavonnebenan.de/reporting-sql-server-blocks-de/</link>
					<comments>https://dbavonnebenan.de/reporting-sql-server-blocks-de/#respond</comments>
		
		<dc:creator><![CDATA[Gabriel]]></dc:creator>
		<pubDate>Mon, 28 Apr 2025 18:11:00 +0000</pubDate>
				<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[Sql Server]]></category>
		<category><![CDATA[BLOCK]]></category>
		<category><![CDATA[DE]]></category>
		<category><![CDATA[DEADLOCK]]></category>
		<category><![CDATA[REPORTING]]></category>
		<guid isPermaLink="false">https://dbavonnebenan.de/?p=411</guid>

					<description><![CDATA[<p>Zu wissen, dass SQL Server Blocking- oder Deadlock-Probleme hat, ist noch ziemlich einfach. Man merkt das normalerweise in der Anwendung und mit den richtigen Queries kann man auch die entsprechenden Blocking-Ketten anzeigen. Am einfachsten geht das mit http://whoisactive.com. Ich hab auch noch eine eigene Entwicklung dazu, die werde ich in einem späteren Blogpost vorstellen, die [&#8230;]</p>
<p>The post <a href="https://dbavonnebenan.de/reporting-sql-server-blocks-de/">Dokumentation von Blocks und Deadlocks – Wenn der 3rd-Party Vendor Beweise fordert</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">Zu wissen, dass SQL Server Blocking- oder Deadlock-Probleme hat, ist noch ziemlich einfach. Man merkt das normalerweise in der Anwendung und mit den richtigen Queries kann man auch die entsprechenden Blocking-Ketten anzeigen. Am einfachsten geht das mit <a href="http://whoisactive.com">http://whoisactive.com</a>.</p>



<div style="height:34px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
SELECT 
    blocking_session_id   AS blocking_session,
    session_id            AS blocked_session,
    wait_time / 1000.0    AS waiting_sec,
    wait_type             AS waittype,
    resource_description  AS ressource
FROM 
    sys.dm_os_waiting_tasks
WHERE 
    blocking_session_id IS NOT NULL
ORDER BY 
    wait_time DESC
</pre></div>


<div style="height:34px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">Ich hab auch noch eine eigene Entwicklung dazu, die werde ich in einem späteren Blogpost vorstellen, die meinen individuellen Anforderungen besser entspricht.</p>



<div style="height:33px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Das Problem</h2>



<p class="wp-block-paragraph">Jetzt kann ich AdHoc Blockings anzeigen, aber bei Deadlocks wird&#8217;s interessanter. Dafür muss die System Health Session gecheckt werden, die ist auch noch flüchtig.</p>



<div style="height:33px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
-- Query System Health Session for Deadlocks

SELECT
    event_data.value(&#039;(event/@timestamp)&#x5B;1]&#039;, &#039;datetime2&#039;) AS DeadlockDateTime,
    event_data.query(&#039;.&#039;)                                  AS DeadlockGraph
FROM
(
    SELECT 
        CAST(target_data AS XML) AS target_data
    FROM 
        sys.dm_xe_session_targets AS st
    JOIN 
        sys.dm_xe_sessions AS s ON s.address = st.event_session_address
    WHERE
             s.name = &#039;system_health&#039;
        AND st.target_name = &#039;ring_buffer&#039;
) AS data
CROSS APPLY
    target_data.nodes(&#039;//RingBufferTarget/event&#x5B;@name=&quot;xml_deadlock_report&quot;]&#039;) AS XEventData(event_data)
ORDER BY 
    DeadlockDateTime DESC;
</pre></div>


<div style="height:33px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">Dieses Problem hatte ich bei einem Kunden, der extrem mit Deadlocks in einer 3rd Party Anwendung zu kämpfen hatte, aber der Support war bisschen&#8230; sagen wir mal zurückhaltend. 😉</p>



<p class="wp-block-paragraph">Wir mussten denen alles transparent melden, damit die anfangen eine Lösung zu finden. Query-Resultsets kann man natürlich als transparent betrachten, wenn man sie versteht oder verstehen will. Leider sind die XML von Health Sessions oder Extended Events nicht gerade dafür berühmt, lesbar zu sein. Der Kunde wollte aber einfach nur, dass das Problem gelöst wird.</p>



<p class="wp-block-paragraph">Also haben wir gemeinsam eine Reporting-Lösung entwickelt.</p>



<div style="height:44px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Die Idee</h2>



<p class="wp-block-paragraph">Mit Extended Events hab ich XML-Daten und mit PowerShell kann ich XML parsen wie ich will, auch zu einem Report meiner Wahl.</p>



<div style="height:44px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Das Ziel</h2>



<p class="wp-block-paragraph">Wir wollten eine Möglichkeit, abseits von SQL Server Resultsets, Anwendungsprobleme mit Anomalien in den SQL Server Metriken zu überlagern, um schnell Lösungen herbeizuführen.</p>



<p class="wp-block-paragraph">In der Woche nach dem Livegang der Lösung gab&#8217;s viele, viele &#8222;Aha&#8220;-Momente. Von falsch konfigurierten ERP-Parametern bis hin zu schlechtem Transaction-Handling.</p>



<div style="height:38px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Der Weg</h2>



<h3 class="wp-block-heading">Persistente Daten</h3>



<p class="wp-block-paragraph">Das Wichtigste ist, Blocking Session und Deadlock Events persistent zu speichern. Das geht perfekt mit Extended Events. Dafür gibt&#8217;s zwei Bereiche:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
-- SNIPPET!!

ADD EVENT sqlserver.blocked_process_report(
    ACTION(sqlserver.client_app_name,
           sqlserver.client_hostname,
           sqlserver.database_name)
),
ADD EVENT sqlserver.xml_deadlock_report(
    ACTION(sqlserver.client_app_name,
           sqlserver.client_hostname,
           sqlserver.database_name)
)
</pre></div>


<div style="height:33px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">Das ganze wird in einem eigenen Extended Event konfiguriert und die Aufzeichnungen laufen bereits. <strong>Fast!</strong> Wir müssen noch SQL Server konfigurieren, damit Blocked Process als Extended Events behandelt werden.</p>



<div style="height:33px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
EXEC sp_configure &#039;show advanced options&#039;, 1;
GO
RECONFIGURE;
GO
EXEC sp_configure &#039;blocked process threshold&#039;, &#039;5&#039;; -- 5 Sekunden
RECONFIGURE
GO
</pre></div>


<div style="height:33px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading"><strong>Daten auslesen</strong></h3>



<h3 class="wp-block-heading">100% PowerShell</h3>



<p class="wp-block-paragraph">Nachdem die Events geloggt werden, müssen sie ausgewertet werden. Was mir sofort eingefallen ist, waren die dbatools mit <a href="https://docs.dbatools.io/Read-DbaXEFile.html">Read-DbaXEFile</a>. Das Problem ist, dass die aktuell geschriebene Datei damit nicht gelesen werden kann. Du kannst dir vorstellen, dass Blocking- und Deadlock-Events nicht in solchen Massen auftreten, dass dutzende File-Rotationen innerhalb von 24h durchgeführt werden. Falls doch, hast du definitiv ein anderes Problem als es nur zu reporten. Also haben wir den Umweg über SQL Server genommen, da auch hier die aktuelle Datei gelesen werden kann.</p>



<h3 class="wp-block-heading">PowerShell + TSql</h3>



<p class="wp-block-paragraph">XML mit Queries zu lesen, fällt mir immer noch sehr schwer, aber DANKE an Brent Ozar, uns allen bekannt, für die <a href="https://www.brentozar.com/archive/2014/03/extended-events-doesnt-hard/">grundlegende Query</a>, auf der ich aufbauen konnte. Zumindest hatte ich jetzt ein Resultset zur Orientierung und ein separates XML für jedes Event im Log.</p>



<div style="height:33px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
-- Snippet
select
    *
FROM sys.fn_xe_file_target_read_file
   (&#039;C:\temp\XEventSessions\blocked_process*.xel&#039;,
    &#039;C:\temp\XEventSessions\blocked_process*.xem&#039;,
     null, null)
CROSS APPLY 
    (SELECT CAST(event_data AS XML) AS event_data) as xevents
</pre></div>


<div style="height:33px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">Finalisierung mit PowerShell</h3>



<p class="wp-block-paragraph">Da ich endlich mein XML-Event hatte, konnte ich es mit PowerShell parsen, transformieren und letztendlich in einen schönen Text-Report verpacken.</p>



<div style="height:33px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
Sql Server Deadlock Analysis | Event Time: 2025-04-08T11:16:07.447
================================================================================

SQL STATEMENTS INVOLVED
--------------------------------------------------------------------------------

    Process ID:   process261b00cfc28
    SPID:         643
    Database:     DATABASENAME
    Query:        **DETAILED QUERY**

----------------------------------------

    Process ID:   process258294fbc28
    SPID:         650
    Database:     DATABASENAME
    Query:        **DETAILED QUERY**

----------------------------------------

VICTIM LIST
--------------------------------------------------------------------------------

    Process ID:   process261b00cfc28
    SPID:         643
    Database:     DATABASENAME


PROCESS LIST
--------------------------------------------------------------------------------

    Process ID:               process261b00cfc28
    SPID:                     643
    Isolation Level:          read uncommitted (1)
    Logical Reads:            700
    Wait Resource:            KEY: 6:72058005407662080 (fb04e1bc7788)
    Wait Time (ms):           2247
    Transaction:              user_transaction
    Last Transaction Started: 2025-04-08T11:16:07.447
    Client:                   &#039;dbserver_dll &amp;lt;ID@APPSERVER:34784&gt;&#039;
    Hostname:                 APPSERVER
    User:                     USERNAME


EXECUTED SQL STATEMENT
----------------------------------------

    **DETAILED QUERY**


Process Details
================================================================================


    Process ID:               process258294fbc28
    SPID:                     650
    Isolation Level:          read uncommitted (1)
    Logical Reads:            1904
    Wait Resource:            KEY: 6:72057996178358272 (2a9726d2d061)
    Wait Time (ms):           2278
    Transaction:              user_transaction
    Last Transaction Started: 2025-04-08T11:16:07.330
    Client:                   &#039;dbserver_dll &amp;lt;ID@APPSERVER:11664&gt;&#039;
    Hostname:                 APPSERVER
    User:                     USERNAME


EXECUTED SQL STATEMENT
----------------------------------------

    **DETAILED QUERY**


Process Details
================================================================================


RESOURCE LIST
--------------------------------------------------------------------------------

    Resource:
        Type:               keylock
        Database:           6
        Object:             DATABASENAME.dbo.tcprmp300100
        Index:              Itcprmp300100_1a
        Lock Mode:          X
        HOBT ID:            72058005407662080
        Associated Object ID: 72058005407662080

    Owners:
        Process ID:         process258294fbc28, Mode: X

    Waiting Processes:
        Process ID:         process261b00cfc28, Mode: U, Request Type: wait

================================================================================

    Resource:
        Type:               keylock
        Database:           6
        Object:             DATABASENAME.dbo.tcprrp011100
        Index:              Itcprrp011100_1a
        Lock Mode:          X
        HOBT ID:            72057996178358272
        Associated Object ID: 72057996178358272

    Owners:
        Process ID:         process261b00cfc28, Mode: X

    Waiting Processes:
        Process ID:         process258294fbc28, Mode: U, Request Type: wait

================================================================================
</pre></div>


<div style="height:33px" aria-hidden="true" class="wp-block-spacer"></div>



<h3 class="wp-block-heading">Umsetzung</h3>



<p class="wp-block-paragraph">Es wurde gewünscht, die Ergebnisse in den Daily Report zu integrieren, den wir bereits mit PowerShell umgesetzt hatten. Jetzt ist es nicht geil, potenziell jeden Morgen 200 Textdateien an eine E-Mail zu hängen, deshalb gibt das PowerShell-Script eine Tabelle zurück, die zeigt, wie Blocks und Locks in Zeitraum X aufgetreten sind, und bei Bedarf kann man proaktiv die Ordnerstruktur checken.</p>



<div style="height:33px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-full has-custom-border"><img loading="lazy" decoding="async" width="662" height="223" src="https://dbavonnebenan.de/wp-content/uploads/2025/04/2025-04-28_15-33.png" alt="" class="has-border-color has-base-border-color wp-image-261" style="border-width:1px" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/04/2025-04-28_15-33.png 662w, https://dbavonnebenan.de/wp-content/uploads/2025/04/2025-04-28_15-33-300x101.png 300w" sizes="auto, (max-width: 662px) 100vw, 662px" /></figure>



<div style="height:33px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">Für den dramatischen Effekt und schnellen Überblick wird auch noch eine Excel-Datei generiert. In diesem Beispiel gab&#8217;s einen schönen Kollateralfund, dass die DWH-Jobs massive Probleme hatten 🙂</p>



<div style="height:33px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image size-large has-custom-border is-style-default"><img loading="lazy" decoding="async" width="1024" height="471" src="https://dbavonnebenan.de/wp-content/uploads/2025/04/2025-04-27_17-18-1024x471.png" alt="" class="has-border-color wp-image-254" style="border-color:#000000;border-width:1px;border-radius:5px" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/04/2025-04-27_17-18-1024x471.png 1024w, https://dbavonnebenan.de/wp-content/uploads/2025/04/2025-04-27_17-18-300x138.png 300w, https://dbavonnebenan.de/wp-content/uploads/2025/04/2025-04-27_17-18-768x353.png 768w, https://dbavonnebenan.de/wp-content/uploads/2025/04/2025-04-27_17-18.png 1131w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:33px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">Und schon hatten wir ein paar Reports, die wir dem Vendor in die Hand drücken konnten, und dann ging&#8217;s sehr schnell.</p>



<div style="height:24px" aria-hidden="true" class="wp-block-spacer"></div>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
Die Ordnerstruktur

_report/
├── text_reports/
│   └── YYYY/
│       └── MM/
│           └── DD/
│               ├── 20231120_123456_dbname_blocked.txt
│               ├── 20231120_123456_dbname_blocked.xml
│               └── 20231120_123456_dbname_deadlock.txt
└── excel_reports/
    └── BlockReport_20231120_123456.xlsx
</pre></div>


<div style="height:22px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph"><strong>Leider erlebe ich immer wieder, dass Anwendungsvendoren denken, ihre Kunden sind zu blöd und können die Anwendung nicht bedienen. Hört auf damit und glaubt euren Kunden manchmal auch ohne 101% Daten, die man nur sammeln kann, wenn man bereits Sachkenntnis hat.</strong></p>



<div style="height:33px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Repository ansehen</h2>



<div style="height:33px" aria-hidden="true" class="wp-block-spacer"></div>


https://github.com/gabrielkoehl/get-snapBlockEvents


<div style="height:33px" aria-hidden="true" class="wp-block-spacer"></div>



<p class="wp-block-paragraph">Das Script hat verschiedene Parameter und Optionen, Details stehen in der README.MD. Falls du Fragen hast, lass sie in den Kommentaren oder kontaktier mich.</p>



<div style="height:37px" aria-hidden="true" class="wp-block-spacer"></div>



<h2 class="wp-block-heading">Anderes</h2>



<p class="wp-block-paragraph">Erik Darling hat auch eine Lösung entwickelt, um Extended Events lesbar zu machen, falls andere Ansätze gebraucht werden. (<a href="https://github.com/erikdarlingdata/DarlingData/tree/main/sp_HumanEvents">sp_HumanEvents</a>)</p>



<div style="height:74px" aria-hidden="true" class="wp-block-spacer"></div>



<figure class="wp-block-image aligncenter size-full is-resized"><img loading="lazy" decoding="async" width="295" height="294" src="https://dbavonnebenan.de/wp-content/uploads/2025/04/2025-04-21_16-19.png" alt="" class="wp-image-113" style="width:223px;height:auto" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/04/2025-04-21_16-19.png 295w, https://dbavonnebenan.de/wp-content/uploads/2025/04/2025-04-21_16-19-150x150.png 150w" sizes="auto, (max-width: 295px) 100vw, 295px" /></figure>



<p class="has-text-align-center wp-block-paragraph"><strong>Gabriel, der DBAvonNebenan</strong></p>



<p class="wp-block-paragraph"></p><p>The post <a href="https://dbavonnebenan.de/reporting-sql-server-blocks-de/">Dokumentation von Blocks und Deadlocks – Wenn der 3rd-Party Vendor Beweise fordert</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://dbavonnebenan.de/reporting-sql-server-blocks-de/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Offline Bilder mit Obsidian Website Clipper</title>
		<link>https://dbavonnebenan.de/improve-obsidian-website-scraper-de/</link>
					<comments>https://dbavonnebenan.de/improve-obsidian-website-scraper-de/#respond</comments>
		
		<dc:creator><![CDATA[Gabriel]]></dc:creator>
		<pubDate>Thu, 24 Apr 2025 14:58:59 +0000</pubDate>
				<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[DE]]></category>
		<category><![CDATA[obsidian]]></category>
		<guid isPermaLink="false">https://dbavonnebenan.de/?p=365</guid>

					<description><![CDATA[<p>Der Obsidian Website Clipper funktioniert echt gut. Aber es stört mich, dass Bilder nur als Links zum Web erscheinen. Das passt nicht zu meiner Vorstellung von einem lokalen Dump. Deshalb habe ich ein kleines PowerShell-Script geschrieben, das MD-Dateien im aktuellen Ordner nach Links durchsucht, sie in den Attachments-Ordner herunterlädt und die Links in den MD-Dateien [&#8230;]</p>
<p>The post <a href="https://dbavonnebenan.de/improve-obsidian-website-scraper-de/">Offline Bilder mit Obsidian Website Clipper</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></description>
										<content:encoded><![CDATA[<p class="wp-block-paragraph">Der <a href="https://obsidian.md/clipper">Obsidian Website Clipper</a> funktioniert echt gut. Aber es stört mich, dass Bilder nur als Links zum Web erscheinen. Das passt nicht zu meiner Vorstellung von einem lokalen Dump.</p>



<p class="wp-block-paragraph">Deshalb habe ich ein kleines PowerShell-Script geschrieben, das MD-Dateien im aktuellen Ordner nach Links durchsucht, sie in den Attachments-Ordner herunterlädt und die Links in den MD-Dateien dynamisch durch lokale Links ersetzt.<br></p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="240" src="https://dbavonnebenan.de/wp-content/uploads/2025/04/2025-04-25_14-40-1024x240.png" alt="" class="wp-image-234" srcset="https://dbavonnebenan.de/wp-content/uploads/2025/04/2025-04-25_14-40-1024x240.png 1024w, https://dbavonnebenan.de/wp-content/uploads/2025/04/2025-04-25_14-40-300x70.png 300w, https://dbavonnebenan.de/wp-content/uploads/2025/04/2025-04-25_14-40-768x180.png 768w, https://dbavonnebenan.de/wp-content/uploads/2025/04/2025-04-25_14-40.png 1065w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<div style="height:23px" aria-hidden="true" class="wp-block-spacer"></div>


https://gist.github.com/gabrielkoehl/c99b084c85521bcbb533994046e7aa7e


<p class="wp-block-paragraph"></p><p>The post <a href="https://dbavonnebenan.de/improve-obsidian-website-scraper-de/">Offline Bilder mit Obsidian Website Clipper</a> first appeared on <a href="https://dbavonnebenan.de">DBA von Nebenan</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://dbavonnebenan.de/improve-obsidian-website-scraper-de/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
