<?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>MS Teams - dbi Blog</title>
	<atom:link href="https://www.dbi-services.com/blog/category/ms-teams/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.dbi-services.com/blog/category/ms-teams/</link>
	<description></description>
	<lastBuildDate>Tue, 21 Jul 2026 15:32:51 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/05/cropped-favicon_512x512px-min-32x32.png</url>
	<title>MS Teams - dbi Blog</title>
	<link>https://www.dbi-services.com/blog/category/ms-teams/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Green SQL Server DBA Tips  #1 – Are unnecessary indexes cluttering up your database?</title>
		<link>https://www.dbi-services.com/blog/green-sql-server-dba-tips-1-are-unnecessary-indexes-cluttering-up-your-database/</link>
					<comments>https://www.dbi-services.com/blog/green-sql-server-dba-tips-1-are-unnecessary-indexes-cluttering-up-your-database/#respond</comments>
		
		<dc:creator><![CDATA[Stéphane Haby]]></dc:creator>
		<pubDate>Tue, 21 Jul 2026 15:32:49 +0000</pubDate>
				<category><![CDATA[Database Administration & Monitoring]]></category>
		<category><![CDATA[Database management]]></category>
		<category><![CDATA[MS Teams]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Technology Survey]]></category>
		<category><![CDATA[Microsoft]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=45650</guid>

					<description><![CDATA[<p>This summer I start a series of blog posts on the ‘green SQL Server DBA’, as it’s a topic that continues to interest me and one that hasn’t been covered very much so far. I hope you’ll enjoy my tips, starting with this one on unused indexes. When discussing SQL Server performance, we often hear [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/green-sql-server-dba-tips-1-are-unnecessary-indexes-cluttering-up-your-database/">Green SQL Server DBA Tips  #1 – Are unnecessary indexes cluttering up your database?</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">This summer I start a series of blog posts on the ‘green SQL Server DBA’, as it’s a topic that continues to interest me and one that hasn’t been covered very much so far. I hope you’ll enjoy my tips, starting with this one on unused indexes.</p>



<p class="wp-block-paragraph">When discussing SQL Server performance, we often hear our performance tool say, ‘<em>An index is missing</em>’, but never ‘<em>There are too many indexes</em>’.</p>



<p class="wp-block-paragraph">However, in many databases, certain indexes are never used by business queries and they are maintained with every INSERT, UPDATE or DELETE operation.</p>



<p class="wp-block-paragraph">The question is really simple:</p>



<ul class="wp-block-list">
<li>How much does an unused index actually cost?</li>



<li>Why is an unused index a problem?</li>
</ul>



<p class="wp-block-paragraph">Every time there is a change to the data, SQL Server must also update all the relevant indexes, and that is not free.</p>



<p class="wp-block-paragraph">As an example, I’m going to use a widely recognised database: the famous DynamicsBC.</p>



<h2 id="h-the-audit" class="wp-block-heading">The AUDIT</h2>



<p class="wp-block-paragraph">The first step in any study is always to carry out an inventory to gather all the data required for the analysis. I need the size of the database, size of each data file, size of the data and size of the indexes</p>



<p class="wp-block-paragraph">I will use a query using the DMV: <a href="https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-objects/sys-dm-db-partition-stats-transact-sql?view=sql-server-ver17" data-type="link" data-id="https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-objects/sys-dm-db-partition-stats-transact-sql?view=sql-server-ver17">sys.dm_db_partition_stats</a></p>



<pre class="wp-block-code"><code>USE DynamicsBC;
GO

SELECT
DB_NAME() AS DatabaseName,
CAST(SUM(reserved_page_count) * 8.0 / 1024 AS DECIMAL(18,2)) AS TotalSizeMB,
CAST(SUM(used_page_count) * 8.0 / 1024 AS DECIMAL(18,2)) AS UsedSizeMB,
CAST((SUM(reserved_page_count) - SUM(used_page_count)) * 8.0 / 1024 AS DECIMAL(18,2)) AS FreeSizeMB,
CAST(SUM(
CASE
WHEN index_id &lt; 2 THEN in_row_data_page_count
+ lob_used_page_count
+ row_overflow_used_page_count
ELSE 0
END
) * 8.0 / 1024 AS DECIMAL(18,2)) AS DataSizeMB,
CAST(SUM(
CASE
WHEN index_id &gt;= 2 THEN used_page_count
ELSE 0
END
) * 8.0 / 1024 AS DECIMAL(18,2)) AS IndexSizeMB
FROM sys.dm_db_partition_stats;
GO
</code></pre>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="818" height="561" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/07/unused-index-02.png" alt="" class="wp-image-45651" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/07/unused-index-02.png 818w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/07/unused-index-02-300x206.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/07/unused-index-02-768x527.png 768w" sizes="(max-width: 818px) 100vw, 818px" /></figure>



<p class="wp-block-paragraph">If we look at the ratio between the indexes and the data, we get:</p>



<p class="wp-block-paragraph">64966.91/92898.87 × 100 = ~70 %</p>



<p class="wp-block-paragraph">A ratio exceeding 50–60 % often requires an analysis of unused indexes, index overlap and compression.</p>



<p class="wp-block-paragraph"><strong>YES, we are good with this database! <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;" /></strong></p>



<p class="wp-block-paragraph">Now, let see if we have unused Indexes I use a script that I created years ago for DWH audit:</p>



<pre class="wp-block-code"><code>/******************************************************************************************/
--Summary
/******************************************************************************************/
USE &#091;master];

DECLARE @sqlcmd NVARCHAR(max);
DECLARE @ExcludeFlag BIT = 0;
DECLARE @IncludeDB VARCHAR(1000);
DECLARE @ExcludeDB VARCHAR(1000);


SET  @IncludeDB = 'DynamicsBC'

SELECT @ExcludeFlag = 0;
--SELECT @ExcludeDB = 'distribution,master,model,msdb,tempdb';
 

DECLARE @InDBList TABLE (indb SYSNAME);
DECLARE @ExDBList TABLE (exdb SYSNAME);
DECLARE @dbList TABLE (dbID INT NOT NULL PRIMARY KEY, dbName SYSNAME NOT NULL);


IF (@ExcludeFlag = 0)
BEGIN
	INSERT INTO @InDBList SELECT * FROM string_split(@IncludeDB,',');
	INSERT INTO @dbList (dbID, dbName) 
		SELECT d.database_id, d.name 
		FROM sys.databases d 
		WHERE d.name IN (SELECT indb FROM @InDBList);
END
ELSE
BEGIN
--	INSERT INTO @ExDBList SELECT * FROM string_split(@ExcludeDB,',');
	INSERT INTO @dbList (dbID, dbName) 
		SELECT d.database_id, d.name 
		FROM sys.databases d 
--		WHERE d.name NOT IN (SELECT exdb FROM @ExDBList);
--		WHERE d.name NOT IN ('distribution','master','model','msdb','tempdb');

END

/*********************************************************************
Creation of the result table variable to store the information
--*********************************************************************/

IF (SELECT OBJECT_ID('tempdb.dbo.#dbUnUsedIndex')) IS NOT NULL  
	DROP TABLE dbo.#dbUnUsedIndex
CREATE TABLE #dbUnUsedIndex (
	dbID INT NOT NULL,
	dbName SYSNAME NOT NULL,
	objName SYSNAME NULL,
	idxName SYSNAME NULL,
	idxID INT NULL,
	UserSeek BIGINT NULL,
	UserScans BIGINT NULL,
	UserLookups BIGINT NULL,
	UserUpdates BIGINT NULL,
	nbRows BIGINT NULL,
	dropStatement NVARCHAR(1000) NULL)

DECLARE @dbID INT;
DECLARE @dbName SYSNAME;
DECLARE cur_dblst CURSOR 
  LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR 
SELECT dbID, dbName
FROM @dbList;

OPEN cur_dblst
FETCH NEXT FROM cur_dblst INTO @dbID, @dbName
WHILE @@FETCH_STATUS = 0
BEGIN 
    --Do something with Id here

	SELECT @sqlcmd = N'
		USE &#091;' + @dbName + ']'
		+ ' SELECT
			@dbID
			, @dbName			
			, o.name AS objName
			, i.name AS idxName
			, i.index_id AS idxID
			, dm_ius.user_seeks AS UserSeek
			, dm_ius.user_scans AS UserScans
			, dm_ius.user_lookups AS UserLookups
			, dm_ius.user_updates AS UserUpdates
			, p.TableRows AS nbRows
			, ''DROP INDEX '' + QUOTENAME(i.name)
			+ '' ON '' + QUOTENAME(s.name) + ''.''
			+ QUOTENAME(OBJECT_NAME(dm_ius.OBJECT_ID)) AS ''dropStatement''
		FROM sys.dm_db_index_usage_stats dm_ius
			INNER JOIN sys.indexes i ON i.index_id = dm_ius.index_id AND dm_ius.OBJECT_ID = i.OBJECT_ID
			INNER JOIN sys.objects o ON dm_ius.OBJECT_ID = o.OBJECT_ID
			INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
			INNER JOIN (SELECT SUM(p.rows) TableRows, p.index_id, p.OBJECT_ID
			FROM sys.partitions p GROUP BY p.index_id, p.OBJECT_ID) p
			ON p.index_id = dm_ius.index_id AND dm_ius.OBJECT_ID = p.OBJECT_ID
		WHERE OBJECTPROPERTY(dm_ius.OBJECT_ID,''IsUserTable'') = 1
			AND dm_ius.database_id = DB_ID()
			AND i.type_desc = ''nonclustered''
			AND i.is_primary_key = 0
			AND i.is_unique_constraint = 0
		ORDER BY (dm_ius.user_seeks + dm_ius.user_scans + dm_ius.user_lookups) ASC; '

	INSERT INTO #dbUnUsedIndex (
					dbID,
					dbName,
					objName,
					idxName,
					idxID,
					UserSeek,
					UserScans,
					UserLookups,
					UserUpdates,
					nbRows,
					dropStatement)
	EXEC sp_executesql  @sqlCmd, N'@dbID INT, @dbName SYSNAME', @dbID, @dbName

    FETCH NEXT FROM cur_dblst INTO @dbID, @dbName
END
CLOSE cur_dblst
DEALLOCATE cur_dblst

SELECT Count(*) FROM #dbUnUsedIndex

SELECT * FROM #dbUnUsedIndex
ORDER BY dbName, objName


 IndexInfo;
GO
</code></pre>



<figure class="wp-block-image size-large"><img decoding="async" width="1024" height="617" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/07/unused-index-04-1024x617.png" alt="" class="wp-image-45655" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/07/unused-index-04-1024x617.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/07/unused-index-04-300x181.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/07/unused-index-04-768x463.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/07/unused-index-04.png 1153w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">For the total number of Indexes, we use this simple query:</p>



<pre class="wp-block-code"><code>SELECT COUNT(*) AS TotalIndexes
FROM dynamicsBC.sys.indexes i
INNER JOIN sys.tables t
ON i.object_id = t.object_id
WHERE i.index_id &gt; 0;</code></pre>



<figure class="wp-block-image size-full"><img decoding="async" width="351" height="249" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/07/unused-index-05.png" alt="" class="wp-image-45656" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/07/unused-index-05.png 351w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/07/unused-index-05-300x213.png 300w" sizes="(max-width: 351px) 100vw, 351px" /></figure>



<p class="wp-block-paragraph">We have 998 unused Index for a total of 19494 Index. This is 5% of the Indexes.<br>In this case, we will not win a lot. 5% is good but we can have a look because we never known&#8230;</p>



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



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



<h2 id="h-the-impact" class="wp-block-heading">The IMPACT</h2>



<p class="wp-block-paragraph"><strong>The first impact is on DML INSERT and UPDATE operations.</strong></p>



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



<p class="wp-block-paragraph">With every insertion, the data is written to the table and several index structures must also be updated, even when one of these indexes is never used by a query.</p>



<p class="wp-block-paragraph">So we have the following sequence:</p>



<p class="wp-block-paragraph">INSERT &#8211;&gt; write to the table &#8211;&gt; update index 1&nbsp; &#8211;&gt; update index 2&nbsp; &#8211;&gt; update index 3&nbsp; &#8211;&gt; update index 4 &#8230;.</p>



<p class="wp-block-paragraph">This is even more costly for this command because, for each operation, the sequence is as follows:</p>



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



<p class="wp-block-paragraph">UPDATE &#8211;&gt; Delete the old index entry + Create the new entry for each index</p>



<p class="wp-block-paragraph">The result of all of this is more I/O, more CPU usage and also more entries in the Transaction Log</p>



<p class="wp-block-paragraph">On heavily used transactional tables, a few unused indexes can account for several per cent of additional load.</p>



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



<p class="wp-block-paragraph"><strong>The second impact is the storage and the backup</strong></p>



<p class="wp-block-paragraph">If the unused index represents ~100B of the database.</p>



<p class="wp-block-paragraph">If 100 GB of indexes are never used, that’s 100 GB backed up unnecessarily, 100 GB restored unnecessarily and, above all, 100 GB stored unnecessarily and just for one environment.</p>



<p class="wp-block-paragraph">If you have Dev, Test, PreProd &amp; Prod and all in HA&#8230; I let you do the calculation but it’s near 1 TB!</p>



<p class="wp-block-paragraph"><strong>The third impact is on the maintenance plan</strong></p>



<p class="wp-block-paragraph">The following operations must also handle unused indexes:<br>&#8211; Rebuild Index<br>&#8211; Reorganise Index<br>&#8211; CheckDB<br>&#8211; Backups<br>&#8211; Restores</p>



<p class="wp-block-paragraph">Each additional index extends the maintenance windows&#8230;</p>



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



<h2 id="h-the-advise" class="wp-block-heading">The ADVISE</h2>



<p class="wp-block-paragraph">You really need to be careful before deleting anything, as an unused index is not necessarily useless.</p>



<p class="wp-block-paragraph">You should check several factors before you delete unused indexes:<br> &#8211; Full business cycle<br> &#8211; End/Begin of month<br> &#8211; Annual processing<br> &#8211; Reporting<br> &#8211; ETL<br> &#8211; SQL Agent jobs</p>



<p class="wp-block-paragraph">I generally recommend observing the index between 1 and 3 months before deleting it and without restart. A restart will reset the DMV used for the stats&#8230; And always keep the script for recreating the index to hand!</p>



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



<h2 id="h-the-green-sql-server-dba-score" class="wp-block-heading">The Green SQL Server DBA Score</h2>



<p class="wp-block-paragraph">Just for fun, we’re going to set up a ‘SQL Server DBA Score’ to use for my tips on the subject:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><td>Unused index rates (unused index/total index)</td><td>Score</td></tr><tr><td>&lt; 5%</td><td>10/10</td></tr><tr><td>5 to 10%</td><td>8/10</td></tr><tr><td>10 to 20 %</td><td>5/10</td></tr><tr><td>&gt; 20 %</td><td>2/10</td></tr></tbody></table></figure>



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



<h2 id="h-conclusion" class="wp-block-heading">Conclusion</h2>



<p class="wp-block-paragraph">An unused index is a bit like car that never gets driven:<br>it takes up space;it costs money;it requires maintenance;but it produces no value.</p>



<p class="wp-block-paragraph">The Green SQL Server DBA doesn’t just seek to add indexes.<br>They aim to retain only those that deliver measurable business value.</p>



<p class="wp-block-paragraph">See you soon for the next one!</p>



<p class="wp-block-paragraph"></p>
<p>L’article <a href="https://www.dbi-services.com/blog/green-sql-server-dba-tips-1-are-unnecessary-indexes-cluttering-up-your-database/">Green SQL Server DBA Tips  #1 – Are unnecessary indexes cluttering up your database?</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/green-sql-server-dba-tips-1-are-unnecessary-indexes-cluttering-up-your-database/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Data Point Prague 2026</title>
		<link>https://www.dbi-services.com/blog/data-point-prague-2026/</link>
					<comments>https://www.dbi-services.com/blog/data-point-prague-2026/#respond</comments>
		
		<dc:creator><![CDATA[Stéphane Savorgnano]]></dc:creator>
		<pubDate>Mon, 01 Jun 2026 10:41:37 +0000</pubDate>
				<category><![CDATA[Business Intelligence]]></category>
		<category><![CDATA[Database management]]></category>
		<category><![CDATA[MS Teams]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[Data Point Prague]]></category>
		<category><![CDATA[Fabric]]></category>
		<category><![CDATA[PowerBI]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=44870</guid>

					<description><![CDATA[<p>For the first time, I joined some days ago the Data Point Prague . The biggest International Data Conference in Prague. A two-day event dedicated to advancing knowledge in data technologies. Thanks to dbi-services to let me the possibility to attend this event. Thursday, May 28, 2026 The first day was dedicated to pre-conference Workshops. [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/data-point-prague-2026/">Data Point Prague 2026</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="410" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/05/WelcomeDataPointPrague-1024x410.jpeg" alt="" class="wp-image-44871" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/05/WelcomeDataPointPrague-1024x410.jpeg 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/05/WelcomeDataPointPrague-300x120.jpeg 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/05/WelcomeDataPointPrague-768x307.jpeg 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/05/WelcomeDataPointPrague-1536x614.jpeg 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/05/WelcomeDataPointPrague-2048x819.jpeg 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">For the first time, I joined some days ago the <a href="https://datapointprague.cz/">Data Point Prague</a> . The biggest International Data Conference in Prague. A two-day event dedicated to advancing knowledge in data technologies. <br>Thanks to <a href="https://www.dbi-services.com/">dbi-services</a> to let me the possibility to attend this event.</p>



<h2 class="wp-block-heading" id="h-thursday-may-28-2026">Thursday, May 28, 2026</h2>



<p class="wp-block-paragraph">The first day was dedicated to pre-conference Workshops. I choose the workshop &#8220;Performance optimization by identifying and correcting bad SQL code&#8221; with <a href="https://mvp.microsoft.com/en-us/mvp/profile/fc630ff3-3c9a-e411-93f2-9cb65495d3c4">Uwe Ricken</a> as a speaker.<br>During the full day, Uwe shown us through different realistic scenarios how to identify, analyze and optimize inefficient SQL Codes with usage of tools like Query Store, Windows Admin Center or even Perfmon.</p>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="768" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/05/UweDPP-1024x768.jpeg" alt="" class="wp-image-44873" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/05/UweDPP-1024x768.jpeg 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/05/UweDPP-300x225.jpeg 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/05/UweDPP-768x576.jpeg 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/05/UweDPP-1536x1152.jpeg 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/05/UweDPP-2048x1536.jpeg 2048w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<h2 class="wp-block-heading" id="h-friday-may-29-2026">Friday, May 29, 2026</h2>



<p class="wp-block-paragraph" id="h-friday-may-29-2026the-second-day-started-witht-the">The second day started with the Keynote session by Jorge Docampo Carro, Senior Program Manager at Microsoft.<br>He shown us that AI is no longer just used to create peace of codes, it is becoming an active collaborator in how Spark jobs, lakehouses, and data pipelines are designed, implemented, and operated.<br>Moreover Visual Studio Code is appearing as a preferred workspace for Fabric data engineering — bringing notebooks, Spark job definitions, environments, and lakehouse artifacts directly into the developer experience.<br></p>



<p class="wp-block-paragraph" id="h-friday-may-29-2026the-second-day-started-witht-the">I then followed a very interesting but quite intensive session of Torsten Strauss named &#8220;A Deep Dive into Optimized Locking in SQL Server 2025&#8221;.<br>During this session Torsten talked about the introduction with SQL Server 2025 of Optimized Locking and Lock After Qualification (LAQ) to reduce contention and improve concurrency.<br>He explained that LAQ defers lock acquisition until rows are fully evaluated, meaning only qualifying rows are locked instead of locking during the scan phase. This significantly lowers the number and duration of locks, reducing blocking, deadlocks, and lock escalation in high-concurrency workloads. <br>He shows us this new Locking mechanism through different concrete examples. The improvements are especially impactful for large updates, selective queries, and OLTP systems with heavy parallel activity. Combined with proper indexing, transaction design, and features like RCSI, these enhancements enable higher throughput and more stable performance.</p>



<p class="wp-block-paragraph">In the Afternoon I saw an interesting session from Uwe again about the Security Techniques for cross database access. It was a good refresh on how to grant access but keep the security at a high level to protect data. Discussions and examples turned around Synonyms, signed Stored Procedures and Trustworthy database options.</p>



<p class="wp-block-paragraph">I joined also some sessions more related to PowerBi, Fabric or AI which were also really interesting even if I&#8217;m not an expert in those domains.</p>



<p class="wp-block-paragraph">It was my first time at Data Point Prague #DataPointPrague but certainly not the last one as this conference is really well organized. The first full day workshop was really great and the second day with more than 30 sessions let you choose the ones you are interesting on.<br>Thank you very much for this event and see you next time <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>
<p>L’article <a href="https://www.dbi-services.com/blog/data-point-prague-2026/">Data Point Prague 2026</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/data-point-prague-2026/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to patch SQL Server instances with Qualys Patch Management</title>
		<link>https://www.dbi-services.com/blog/how-to-patch-sql-server-instances-with-qualys-patch-management/</link>
					<comments>https://www.dbi-services.com/blog/how-to-patch-sql-server-instances-with-qualys-patch-management/#respond</comments>
		
		<dc:creator><![CDATA[Stéphane Savorgnano]]></dc:creator>
		<pubDate>Tue, 30 Sep 2025 14:09:37 +0000</pubDate>
				<category><![CDATA[Database Administration & Monitoring]]></category>
		<category><![CDATA[Database management]]></category>
		<category><![CDATA[MS Teams]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[AlwaysOn]]></category>
		<category><![CDATA[patching]]></category>
		<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[Qualy Patch Management]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=40478</guid>

					<description><![CDATA[<p>By one of our Customers, I have to use Qualys to patch their production SQL Server instances. And to be more precise the Qualys Patch Management application which provides a solution to manage vulnerabilities and deploy patches to secure and keep assets up-to-date.I will use the job functionality of Qualys Patch Management to automate the [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/how-to-patch-sql-server-instances-with-qualys-patch-management/">How to patch SQL Server instances with Qualys Patch Management</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">By one of our Customers, I have to use <a href="https://www.qualys.com/">Qualys</a> to patch their production SQL Server instances. And to be more precise the Qualys Patch Management application which provides a solution to manage vulnerabilities and deploy patches to secure and keep assets up-to-date.<br>I will use the job functionality of Qualys Patch Management to automate the patching based on a specific schedule.</p>



<p class="wp-block-paragraph">My customer has the following context, a two nodes Always On active-active cluster with 4 instances where each instance owns between five and seven Availability Groups spread on the two cluster nodes.</p>



<p class="wp-block-paragraph">The first step is to connect to the Qualys subscription of my customer:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="922" height="469" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/image-18.png" alt="" class="wp-image-40483" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/image-18.png 922w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/image-18-300x153.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/image-18-768x391.png 768w" sizes="auto, (max-width: 922px) 100vw, 922px" /></figure>



<p class="wp-block-paragraph">And to navigate to the Patch Management application:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="922" height="537" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/image-19.png" alt="" class="wp-image-40484" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/image-19.png 922w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/image-19-300x175.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/image-19-768x447.png 768w" sizes="auto, (max-width: 922px) 100vw, 922px" /></figure>



<p class="wp-block-paragraph">Once in the Patch Management, I go in the job menu to create the first job which will patch the first node of my Always On cluster.<br>As this job will be executed first it will have to execute the following tasks:</p>



<ul class="wp-block-list">
<li>Save the Availability group configuration for all instances in order to be able to re-dispatch the Availability Groups on both nodes once the patching will be finished</li>



<li>Fail-over all Availability Groups from the node server1 to the node server2</li>



<li>Patch the instances on node server1 with the available patches</li>



<li>Reboot the server</li>



<li>Fail-over all Availability Groups from the node server2 to the node server1</li>
</ul>



<p class="wp-block-paragraph">In terms of Qualys job I have to create a new job with the following information:</p>



<ul class="wp-block-list">
<li>A name to identify the job, here SQLServer_server1_M_3rd_Saturday_2200 as this job will be executed the third Saturday of each month at 10PM</li>
</ul>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="633" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info1_noname-1024x633.jpg" alt="" class="wp-image-40488" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info1_noname-1024x633.jpg 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info1_noname-300x185.jpg 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info1_noname-768x475.jpg 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info1_noname.jpg 1429w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<ul class="wp-block-list">
<li>Select the asset (here the server) where the patches will be executed: server1</li>
</ul>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="636" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info2_noname-1024x636.jpg" alt="" class="wp-image-40489" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info2_noname-1024x636.jpg 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info2_noname-300x186.jpg 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info2_noname-768x477.jpg 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info2_noname.jpg 1413w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<ul class="wp-block-list">
<li>Pre-actions: this step provides some actions which can be executed before to patch the assets. It can be: to run a script, install a software, change a registry key, uninstall a software or a system reboot.<br>I will use it to execute my PowerShell script to:<br><strong>1.</strong> Save the Availability Groups configuration in a JSON file (you can find here an example of this file below)<br><strong>2.</strong> fail-over the Availability Groups which are primary on node server1 to server2. The PowerShell script is also logging in a file</li>
</ul>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="527" height="882" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/JSON_File.jpg" alt="" class="wp-image-40490" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/JSON_File.jpg 527w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/JSON_File-179x300.jpg 179w" sizes="auto, (max-width: 527px) 100vw, 527px" /></figure>
</div>


<pre class="wp-block-code"><code>###Logging functions
Function Out-Log() {
param(
&#091;ValidateSet('INFO','WARNING','ERROR')]
&#091;String] $Type = 'INFO',
&#091;String] $Message
)

    '&#091;'+(Get-Date -f 'yyyy-MM-dd HH:mm:ss') +'] ' + ' - &#091;' + $Type + '] - ' + $Message | Out-File -FilePath $LogFile -Append;
}

Function Add-Warning() {
param(
&#091;String] $Warning
)
    If ($Warning) {
    Out-Log -Type WARNING -Message $Warning;
    }
}

Function Add-Error() {
param(
&#091;String] $Message
)
    Out-Log -Type ERROR -Message $Message;
}

###AGs functions
#Function to save Availability Group configuration in a JSON file
Function AGsSaveConfiguration(){
param (
&#091;Array] $instances,
&#091;String] $ConfigFile
)
    Out-Log -Type INFO "The following configurations have been saved:"
    $initialState = @()
    foreach ($instance in $instances) {
        $ags = Get-DbaAvailabilityGroup -SqlInstance $instance
        foreach ($ag in $ags) {
            $initialState += &#091;PSCustomObject]@{
                Instance = $instance
                AGName   = $ag.Name
                Primary  = $ag.PrimaryReplica
            }
            $output = "Instance = $instance, AGName = $($ag.Name), Primary  = $($ag.PrimaryReplica)"
            Out-Log -Type INFO "$output" 
        }
    }
    #Add configuration to a JSON file
    $initialState | ConvertTo-Json | Out-File $ConfigFile
    $output = $initialState | Format-Table
}

#Function to save Availability Group configuration in a JSON file
Function AGsRestoreConfiguration(){
param (
&#091;Array] $instances,
&#091;String] $ConfigFile
)
    if (!(Test-Path -Path $ConfigFile)) {
        Add-Error "Configuration file is missing"
        Add-Error "Exit without having restore the AG configurations"
        Return
    }

    $initialState = Get-Content $ConfigFile | ConvertFrom-Json

    foreach ($entry in $initialState) {
        write-host $entry
        $ag = Get-DbaAvailabilityGroup -SqlInstance $entry.Instance -AvailabilityGroup $entry.AGName
        if (($ag.PrimaryReplica -ne $entry.Primary) -and ($ag.LocalReplicaRole -eq "Secondary")) {
            Write-Host "Failover of Availability Group $($entry.AGName) to $($entry.Primary)"
            Out-Log -Type INFO "Failover of Availability Group $($entry.AGName) to $($entry.Primary)"
            Try {
                Invoke-DbaAgFailover -SqlInstance $entry.Instance -AvailabilityGroup $entry.AGName -Confirm:$false
            }
            Catch {
                Write-Host "Error during failover of Availability Group $($entry.AGName) to $($entry.Primary)"
                Add-Error "Error during failover of Availability Group $($entry.AGName) to $($entry.Primary)"                
            }
        }
    }
}

#Function to failover a list of instances to a specific host
Function AGsFailoverTo(){
param (
&#091;Array] $instances,
&#091;String] $TargetNode
)
    foreach ($instance in $instances) {
        $ags = Get-DbaAvailabilityGroup -SqlInstance $instance
        foreach ($ag in $ags) {
            if (($ag.ComputerName -eq $TargetNode) -and ($ag.LocalReplicaRole -eq "Secondary")) {
                Write-Host "Failover of $($ag.Name) to $TargetNode from $($ag.PrimaryReplica)"
                Out-Log -Type INFO "Failover of $($ag.Name) to $TargetNode from $($ag.PrimaryReplica)"
                Try {
                    Invoke-DbaAgFailover -SqlInstance $instance -AvailabilityGroup $ag.Name -Confirm:$false
                }
                Catch {
                    Write-Host "Error during failover of Availability Group $($ag.Name) to $TargetNode from $($ag.PrimaryReplica)"
                    Add-Error "Error during failover of Availability Group $($ag.Name) to $TargetNode from $($ag.PrimaryReplica)"
                }
            }
        }
    }
    Out-Log -Type INFO "All Availability Groups have been failover to node $TargetNode"

}

#LOG file path and name
$LogFileName = "AGsFailoverForPatching.txt"
$LogFilePath = "\\ShareFolder\LOG"
$LogFile = "$LogFilePath\$LogFileName"

#Configuration file path and name
$AGsConfigFileName = "initial_state.json"
$AGsConfigFilePath = "\\ShareFolder\LOG"
$AGsConfigFile = "$AGsConfigFilePath\$AGsConfigFileName"

###################################################################
#Save Availability Group configuration for all instances
Out-Log -Type INFO "******************** START PROCESS ********************"
Out-Log -Type INFO "START TO SAVE AVAILABILITY GROUPS CONFIGURATION"
Out-Log -Type INFO "Configuraton will be saved in file $AGsConfigFile"

#Find instances by list
$instances = @('server1\Instance1','server1\Instance2','server1\Instance3','server1\Instance4','server2\Instance1','server2\Instance2','server2\Instance3','server2\Instance4')
Out-Log -Type INFO "Instances available: $instances"

#Call the function AGsSaveConfiguration
AGsSaveConfiguration -instances $instances -ConfigFile $AGsConfigFile

Out-Log -Type INFO "Configurations saved successfully"

###################################################################
#Failover all Availability group to a specify node
Out-Log -Type INFO "START TO FAILOVER ALL AVAILABILITY GROUPS"
#Target node
$targetNode = "server2"
Out-Log -Type INFO "Target node: $targetNode"

#Call the function AGsFailoverTo
AGsFailoverTo -instances $instances -TargetNode $targetNode</code></pre>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="632" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info3_noname-1024x632.jpg" alt="" class="wp-image-40495" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info3_noname-1024x632.jpg 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info3_noname-300x185.jpg 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info3_noname-768x474.jpg 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info3_noname.jpg 1417w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<ul class="wp-block-list">
<li>Select the patches to apply to the assets, here we will select then automatically based on the filter patch.appFamily: SQL Server</li>
</ul>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="484" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info6_noname-1024x484.jpg" alt="" class="wp-image-40496" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info6_noname-1024x484.jpg 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info6_noname-300x142.jpg 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info6_noname-768x363.jpg 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info6_noname-1536x726.jpg 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info6_noname.jpg 1836w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">Qualys automatically detects that SQL Server 2019 instances are installed on this server as well as SQL Server Management Studio and provides the available patches.<br>Once patched have been applied, the server will automatically reboot.</p>



<ul class="wp-block-list">
<li>Post-actions: the possible post actions are: run a script, install a software, change a registry key or uninstall a software. As the instances have been patched and the server rebooted, we need to fail-over all Availability Groups which are primary on node server2 to server1, as the node server2 will be patched by the second job. A PowerShell script is then executed to do that:</li>
</ul>



<pre class="wp-block-code"><code>###Logging functions
Function Out-Log() {
param(
&#091;ValidateSet('INFO','WARNING','ERROR')]
&#091;String] $Type = 'INFO',
&#091;String] $Message
)
    '&#091;'+(Get-Date -f 'yyyy-MM-dd HH:mm:ss') +'] ' + ' - &#091;' + $Type + '] - ' + $Message | Out-File -FilePath $LogFile -Append;
}

Function Add-Warning() {
param(
&#091;String] $Warning
)
    If ($Warning) {
    Out-Log -Type WARNING -Message $Warning;
    }
}

Function Add-Error() {
param(
&#091;String] $Message
)
    Out-Log -Type ERROR -Message $Message;
}

###AGs functions
#Function to save Availability Group configuration in a JSON file
Function AGsSaveConfiguration(){
param (
&#091;Array] $instances,
&#091;String] $ConfigFile
)
    Out-Log -Type INFO "The following configurations have been saved:"
    $initialState = @()
    foreach ($instance in $instances) {
        $ags = Get-DbaAvailabilityGroup -SqlInstance $instance
        foreach ($ag in $ags) {
            $initialState += &#091;PSCustomObject]@{
                Instance = $instance
                AGName   = $ag.Name
                Primary  = $ag.PrimaryReplica
            }
            $output = "Instance = $instance, AGName = $($ag.Name), Primary  = $($ag.PrimaryReplica)"
            Out-Log -Type INFO "$output" 
        }
    }
    #Add configuration to a JSON file
    $initialState | ConvertTo-Json | Out-File $ConfigFile
    $output = $initialState | Format-Table
}

#Function to save Availability Group configuration in a JSON file
Function AGsRestoreConfiguration(){
param (
&#091;Array] $instances,
&#091;String] $ConfigFile
)
    if (!(Test-Path -Path $ConfigFile)) {
        Add-Error "Configuration file is missing"
        Add-Error "Exit without having restore the AG configurations"
        Return
    }

    $initialState = Get-Content $ConfigFile | ConvertFrom-Json

    foreach ($entry in $initialState) {
        write-host $entry
        $ag = Get-DbaAvailabilityGroup -SqlInstance $entry.Instance -AvailabilityGroup $entry.AGName
        if (($ag.PrimaryReplica -ne $entry.Primary) -and ($ag.LocalReplicaRole -eq "Secondary")) {
            Write-Host "Failover of Availability Group $($entry.AGName) to $($entry.Primary)"
            Out-Log -Type INFO "Failover of Availability Group $($entry.AGName) to $($entry.Primary)"
            Try {
                Invoke-DbaAgFailover -SqlInstance $entry.Instance -AvailabilityGroup $entry.AGName -Confirm:$false
            }
            Catch {
                Write-Host "Error during failover of Availability Group $($entry.AGName) to $($entry.Primary)"
                Add-Error "Error during failover of Availability Group $($entry.AGName) to $($entry.Primary)"                
            }
        }
    }
}

#Function to failover a list of instances to a specific host
Function AGsFailoverTo(){
param (
&#091;Array] $instances,
&#091;String] $TargetNode
)
    foreach ($instance in $instances) {
        $ags = Get-DbaAvailabilityGroup -SqlInstance $instance
        foreach ($ag in $ags) {
            if (($ag.ComputerName -eq $TargetNode) -and ($ag.LocalReplicaRole -eq "Secondary")) {
                Write-Host "Failover of $($ag.Name) to $TargetNode from $($ag.PrimaryReplica)"
                Out-Log -Type INFO "Failover of $($ag.Name) to $TargetNode from $($ag.PrimaryReplica)"
                Try {
                    Invoke-DbaAgFailover -SqlInstance $instance -AvailabilityGroup $ag.Name -Confirm:$false
                }
                Catch {
                    Write-Host "Error during failover of Availability Group $($ag.Name) to $TargetNode from $($ag.PrimaryReplica)"
                    Add-Error "Error during failover of Availability Group $($ag.Name) to $TargetNode from $($ag.PrimaryReplica)"
                }
            }
        }
    }
    Out-Log -Type INFO "All Availability Groups have been failover to node $TargetNode"

}

#LOG file path and name
$LogFileName = "AGsFailoverForPatching.txt"
$LogFilePath = "\\ShareFolder\LOG"
$LogFile = "$LogFilePath\$LogFileName"

#Configuration file path and name
$AGsConfigFileName = "initial_state.json"
$AGsConfigFilePath = "\\ShareFolder\LOG"
$AGsConfigFile = "$AGsConfigFilePath\$AGsConfigFileName"

###################################################################
#Failover all Availability group to a specify node
Out-Log -Type INFO "START TO FAILOVER ALL AVAILABILITY GROUPS"
#Target node
$targetNode = "server1"
Out-Log -Type INFO "Target node: $targetNode"

#Find all instances
$instances = @('server1\Instance1','server1\Instance2','server1\Instance3','server1\Instance4','server2\Instance1','server2\Instance2','server2\Instance3','server2\Instance4')
Out-Log -Type INFO "Instances available: $instances"

#Call the function AGsFailoverTo
AGsFailoverTo -instances $instances -TargetNode $targetNode</code></pre>



<ul class="wp-block-list">
<li>Schedule is the third Saturday of each month at 10PM with a duration of 2 hours</li>
</ul>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="640" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info9_noname-1024x640.jpg" alt="" class="wp-image-40500" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info9_noname-1024x640.jpg 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info9_noname-300x188.jpg 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info9_noname-768x480.jpg 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info9_noname.jpg 1399w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<ul class="wp-block-list">
<li>There are also some possibles options that can be enabled or disabled for the job.<br>I select the Reboot Countdown to send a message to the users of the server before the reboot, a message is also sent to a distribution list at the execution start and after completion and the patches will try to be downloaded by the Qualys agent before the job schedule.</li>
</ul>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="795" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info10_noname-1024x795.jpg" alt="" class="wp-image-40501" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info10_noname-1024x795.jpg 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info10_noname-300x233.jpg 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info10_noname-768x597.jpg 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info10_noname.jpg 1039w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1024" height="881" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info11_noname-1024x881.jpg" alt="" class="wp-image-40502" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info11_noname-1024x881.jpg 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info11_noname-300x258.jpg 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info11_noname-768x660.jpg 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/09/Qualys_Basis_Info11_noname.jpg 1064w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></figure>



<p class="wp-block-paragraph">It&#8217;s finish for my first job, I need to create now the second one.<br>Qualys gives the possibility to clone job, so I&#8217;m using this feacture and change the needed information for my second job.<br>This job will patch my second server and will have to:</p>



<ul class="wp-block-list">
<li>Failover all Availability Groups from the node server2 to the node server1 (already done in the first job as post-action, but I do it again)</li>



<li>Patch the instances on node server2 with the available patches</li>



<li>Reboot the server</li>



<li>Redistribute the Availability Groups based on the JSON file created in the pre-action of the first job</li>
</ul>



<p class="wp-block-paragraph">In terms of Qualys, this job has the following information:</p>



<ul class="wp-block-list">
<li>A name to identify the job, here SQLServer_server2_M_3rd_Sunday_0001 as this job will be executed the third Sunday of each month at 00:01AM, after the completion of the first one</li>



<li>Select the asset (here the server) where the patches will be executed: server2</li>



<li>Pre-actions: it will be a PowerShell script which will:<br>o Failover the Availability Groups from node server2 to server1 with the following PowerShell script (the script is also logging in a file)</li>
</ul>



<pre class="wp-block-code"><code>###Logging functions
Function Out-Log() {
param(
&#091;ValidateSet('INFO','WARNING','ERROR')]
&#091;String] $Type = 'INFO',
&#091;String] $Message
)
    '&#091;'+(Get-Date -f 'yyyy-MM-dd HH:mm:ss') +'] ' + ' - &#091;' + $Type + '] - ' + $Message | Out-File -FilePath $LogFile -Append;
}

Function Add-Warning() {
param(
&#091;String] $Warning
)
    If ($Warning) {
    Out-Log -Type WARNING -Message $Warning;
    }
}

Function Add-Error() {
param(
&#091;String] $Message
)
    Out-Log -Type ERROR -Message $Message;
}

###AGs functions
#Function to save Availability Group configuration in a JSON file
Function AGsSaveConfiguration(){
param (
&#091;Array] $instances,
&#091;String] $ConfigFile
)
    Out-Log -Type INFO "The following configurations have been saved:"
    $initialState = @()
    foreach ($instance in $instances) {
        $ags = Get-DbaAvailabilityGroup -SqlInstance $instance
        foreach ($ag in $ags) {
            $initialState += &#091;PSCustomObject]@{
                Instance = $instance
                AGName   = $ag.Name
                Primary  = $ag.PrimaryReplica
            }
            $output = "Instance = $instance, AGName = $($ag.Name), Primary  = $($ag.PrimaryReplica)"
            Out-Log -Type INFO "$output" 
        }
    }
    #Add configuration to a JSON file
    $initialState | ConvertTo-Json | Out-File $ConfigFile
    $output = $initialState | Format-Table
}

#Function to save Availability Group configuration in a JSON file
Function AGsRestoreConfiguration(){
param (
&#091;Array] $instances,
&#091;String] $ConfigFile
)
    if (!(Test-Path -Path $ConfigFile)) {
        Add-Error "Configuration file is missing"
        Add-Error "Exit without having restore the AG configurations"
        Return
    }

    $initialState = Get-Content $ConfigFile | ConvertFrom-Json

    foreach ($entry in $initialState) {
        write-host $entry
        $ag = Get-DbaAvailabilityGroup -SqlInstance $entry.Instance -AvailabilityGroup $entry.AGName
        if (($ag.PrimaryReplica -ne $entry.Primary) -and ($ag.LocalReplicaRole -eq "Secondary")) {
            Write-Host "Failover of Availability Group $($entry.AGName) to $($entry.Primary)"
            Out-Log -Type INFO "Failover of Availability Group $($entry.AGName) to $($entry.Primary)"
            Try {
                Invoke-DbaAgFailover -SqlInstance $entry.Instance -AvailabilityGroup $entry.AGName -Confirm:$false
            }
            Catch {
                Write-Host "Error during failover of Availability Group $($entry.AGName) to $($entry.Primary)"
                Add-Error "Error during failover of Availability Group $($entry.AGName) to $($entry.Primary)"                
            }
        }
    }
}

#Function to failover a list of instances to a specific host
Function AGsFailoverTo(){
param (
&#091;Array] $instances,
&#091;String] $TargetNode
)
    foreach ($instance in $instances) {
        $ags = Get-DbaAvailabilityGroup -SqlInstance $instance
        foreach ($ag in $ags) {
            if (($ag.ComputerName -eq $TargetNode) -and ($ag.LocalReplicaRole -eq "Secondary")) {
                Write-Host "Failover of $($ag.Name) to $TargetNode from $($ag.PrimaryReplica)"
                Out-Log -Type INFO "Failover of $($ag.Name) to $TargetNode from $($ag.PrimaryReplica)"
                Try {
                    Invoke-DbaAgFailover -SqlInstance $instance -AvailabilityGroup $ag.Name -Confirm:$false
                }
                Catch {
                    Write-Host "Error during failover of Availability Group $($ag.Name) to $TargetNode from $($ag.PrimaryReplica)"
                    Add-Error "Error during failover of Availability Group $($ag.Name) to $TargetNode from $($ag.PrimaryReplica)"
                }
            }
        }
    }
    Out-Log -Type INFO "All Availability Groups have been failover to node $TargetNode"
}

#LOG file path and name
$LogFileName = "AGsFailoverForPatching.txt"
$LogFilePath = "\\ShareFolder\LOG"
$LogFile = "$LogFilePath\$LogFileName"

#Configuration file path and name
$AGsConfigFileName = "initial_state.json"
$AGsConfigFilePath = "\\ShareFolder\LOG"
$AGsConfigFile = "$AGsConfigFilePath\$AGsConfigFileName"

###################################################################
#Failover all Availability group to a specify node
Out-Log -Type INFO "START TO FAILOVER ALL AVAILABILITY GROUPS"
#Target node
$targetNode = "server1"
Out-Log -Type INFO "Target node: $targetNode"

#Find all instances
$instances = @('server1\Instance1','server1\Instance2','server1\Instance3','server1\Instance4','server2\Instance1','server2\Instance2','server2\Instance3','server2\Instance4')
Out-Log -Type INFO "Instances available: $instances"

#Call the function AGsFailoverTo
AGsFailoverTo -instances $instances -TargetNode $targetNode
</code></pre>



<ul class="wp-block-list">
<li>Select the patches to apply to the assets, here we will select then automatically based on the filter patch.appFamily: SQL Server (like for the first job)</li>



<li>Post-actions: once the instances have been patched and the server rebooted, we need to redistribute the Availability Groups over the 2 nodes based on the JSON file created at the beginning of this patch process. For that we execute the following PowerShell script:</li>
</ul>



<pre class="wp-block-code"><code>###Logging functions
Function Out-Log() {
param(
&#091;ValidateSet('INFO','WARNING','ERROR')]
&#091;String] $Type = 'INFO',
&#091;String] $Message
)
    '&#091;'+(Get-Date -f 'yyyy-MM-dd HH:mm:ss') +'] ' + ' - &#091;' + $Type + '] - ' + $Message | Out-File -FilePath $LogFile -Append;
}

Function Add-Warning() {
param(
&#091;String] $Warning
)
    If ($Warning) {
    Out-Log -Type WARNING -Message $Warning;
    }
}

Function Add-Error() {
param(
&#091;String] $Message
)
    Out-Log -Type ERROR -Message $Message;
}

###AGs functions
#Function to save Availability Group configuration in a JSON file
Function AGsSaveConfiguration(){
param (
&#091;Array] $instances,
&#091;String] $ConfigFile
)
    Out-Log -Type INFO "The following configurations have been saved:"
    $initialState = @()
    foreach ($instance in $instances) {
        $ags = Get-DbaAvailabilityGroup -SqlInstance $instance
        foreach ($ag in $ags) {
            $initialState += &#091;PSCustomObject]@{
                Instance = $instance
                AGName   = $ag.Name
                Primary  = $ag.PrimaryReplica
            }
            $output = "Instance = $instance, AGName = $($ag.Name), Primary  = $($ag.PrimaryReplica)"
            Out-Log -Type INFO "$output" 
        }
    }
    #Add configuration to a JSON file
    $initialState | ConvertTo-Json | Out-File $ConfigFile
    $output = $initialState | Format-Table
}

#Function to save Availability Group configuration in a JSON file
Function AGsRestoreConfiguration(){
param (
&#091;Array] $instances,
&#091;String] $ConfigFile
)
    if (!(Test-Path -Path $ConfigFile)) {
        Add-Error "Configuration file is missing"
        Add-Error "Exit without having restore the AG configurations"
        Return
    }

    $initialState = Get-Content $ConfigFile | ConvertFrom-Json

    foreach ($entry in $initialState) {
        write-host $entry
        $ag = Get-DbaAvailabilityGroup -SqlInstance $entry.Instance -AvailabilityGroup $entry.AGName
        if (($ag.PrimaryReplica -ne $entry.Primary) -and ($ag.LocalReplicaRole -eq "Secondary")) {
            Write-Host "Failover of Availability Group $($entry.AGName) to $($entry.Primary)"
            Out-Log -Type INFO "Failover of Availability Group $($entry.AGName) to $($entry.Primary)"
            Try {
                Invoke-DbaAgFailover -SqlInstance $entry.Instance -AvailabilityGroup $entry.AGName -Confirm:$false
            }
            Catch {
                Write-Host "Error during failover of Availability Group $($entry.AGName) to $($entry.Primary)"
                Add-Error "Error during failover of Availability Group $($entry.AGName) to $($entry.Primary)"                
            }
        }
    }
}

#Function to failover a list of instances to a specific host
Function AGsFailoverTo(){
param (
&#091;Array] $instances,
&#091;String] $TargetNode
)
    foreach ($instance in $instances) {
        $ags = Get-DbaAvailabilityGroup -SqlInstance $instance
        foreach ($ag in $ags) {
            if (($ag.ComputerName -eq $TargetNode) -and ($ag.LocalReplicaRole -eq "Secondary")) {
                Write-Host "Failover of $($ag.Name) to $TargetNode from $($ag.PrimaryReplica)"
                Out-Log -Type INFO "Failover of $($ag.Name) to $TargetNode from $($ag.PrimaryReplica)"
                Try {
                    Invoke-DbaAgFailover -SqlInstance $instance -AvailabilityGroup $ag.Name -Confirm:$false
                }
                Catch {
                    Write-Host "Error during failover of Availability Group $($ag.Name) to $TargetNode from $($ag.PrimaryReplica)"
                    Add-Error "Error during failover of Availability Group $($ag.Name) to $TargetNode from $($ag.PrimaryReplica)"
                }
            }
        }
    }
    Out-Log -Type INFO "All Availability Groups have been failover to node $TargetNode"

}

#LOG file path and name
$LogFileName = "AGsFailoverForPatching.txt"
$LogFilePath = "\\ShareFolderLOG"
$LogFile = "$LogFilePath\$LogFileName"

#Configuration file path and name
$AGsConfigFileName = "initial_state.json"
$AGsConfigFilePath = "\\ShareFolder\LOG"
$AGsConfigFile = "$AGsConfigFilePath\$AGsConfigFileName"

###################################################################
#Restore Availability Group configuration for all instances
Out-Log -Type INFO "START TO RESTORE AVAILABILITY GROUPS CONFIGURATION"

#Call the function AGsRestoreConfiguration
$instances = @('server1\Instance1','server1\Instance2','server1\Instance3','server1\Instance4','server2\Instance1','server2\Instance2','server2\Instance3','server2\Instance4')
AGsRestoreConfiguration -instances $instances -ConfigFile $AGsConfigFile

Out-Log -Type INFO "Availability Group configuratons restored successfully"
Out-Log -Type INFO "******************** END PROCESS ********************"
</code></pre>



<ul class="wp-block-list">
<li>Schedule is the third Sunday of each month at 00:01AM with a duration of 2 hours. With this schedule this job will be executed after the end of the first one. In Qualys there is for the moment no possibility to start a job when another one is completed…</li>



<li>For options, I select the Reboot Countdown to send a message to the users of the server before the reboot, a message is also sent to a distribution list at the execution start and after completion to admin and the patches will try to be downloaded by the Qualys agent before the job schedule.</li>
</ul>



<p class="wp-block-paragraph">Both jobs are now created. After their execution the two Always On cluster nodes will have been patched and the Availability Groups redistributed as it was before to start.</p>



<p class="wp-block-paragraph">Qualys Patch Management is relatively easy to use. All patches based on your selection are availables and can be applied easily.<br>The pre and post actions give the possibility to prepare the patching: here fail-over, save the inital configuration, redistribute the AGs at the end.<br>It was a good experience and I hope it can help <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;" /><br> </p>
<p>L’article <a href="https://www.dbi-services.com/blog/how-to-patch-sql-server-instances-with-qualys-patch-management/">How to patch SQL Server instances with Qualys Patch Management</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/how-to-patch-sql-server-instances-with-qualys-patch-management/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Starting with PowerShell 7 and parallelization</title>
		<link>https://www.dbi-services.com/blog/starting-with-powershell-7-and-parallelization/</link>
					<comments>https://www.dbi-services.com/blog/starting-with-powershell-7-and-parallelization/#respond</comments>
		
		<dc:creator><![CDATA[Stéphane Savorgnano]]></dc:creator>
		<pubDate>Wed, 26 Feb 2025 08:49:14 +0000</pubDate>
				<category><![CDATA[Database Administration & Monitoring]]></category>
		<category><![CDATA[Database management]]></category>
		<category><![CDATA[Development & Performance]]></category>
		<category><![CDATA[MS Teams]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Parallelization]]></category>
		<category><![CDATA[PowerShell]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=36946</guid>

					<description><![CDATA[<p>For the time being Windows PowerShell 5.1 is installed with Windows Server. It means that if you want to use or even test PowerShell 7 you need to install it by your own.To be honest, even if I&#8217;m using PowerShell as a DBA more or less every day, I did not take too much care [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/starting-with-powershell-7-and-parallelization/">Starting with PowerShell 7 and parallelization</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">For the time being Windows PowerShell 5.1 is installed with Windows Server. It means that if you want to use or even test PowerShell 7 you need to install it by your own.<br>To be honest, even if I&#8217;m using PowerShell as a DBA more or less every day, I did not take too much care of PowerShell 7 until I used it at a customer place with a new parallelization functionality that we will discuss later on.</p>



<h2 class="wp-block-heading" id="h-introduction">Introduction</h2>



<p class="wp-block-paragraph">For reminder, PowerShell 7 is an open-source and cross-platform edition of PowerShell. It means that it can be used on Windows platforms but also on MacOS or Linux.<br>The good point is that you can install it without removing Windows PowerShell 5.1. Both version can cohabit because of:</p>



<ul class="wp-block-list">
<li>Separate installation path and executable name
<ul class="wp-block-list">
<li>path with 5.1 like $env:WINDIR\System32\WindowsPowerShell\v1.0</li>



<li>path with 7 like $env:ProgramFiles\PowerShell\7</li>



<li>executable with PowerShell 5.1 is powershell.exe and with PowerShell 7 is pwsh.exe</li>
</ul>
</li>



<li>Separate PSModulePath</li>



<li>Separate profiles for each version
<ul class="wp-block-list">
<li>path with 5.1 is $HOME\Documents\WindowsPowerShell</li>



<li>path with 7 is $HOME\Documents\PowerShell</li>
</ul>
</li>



<li>Improved module compatibility</li>



<li>New remoting endpoints</li>



<li>Group policy support</li>



<li>Separate Event logs</li>
</ul>



<h2 class="wp-block-heading" id="h-installation">Installation</h2>



<p class="wp-block-paragraph">To install PowerShell 7 on Windows Servers the easiest way is to use a MSI package. The last version to download is the<a href="https://github.com/PowerShell/PowerShell/releases/download/v7.5.0/PowerShell-7.5.0-win-x64.msi"> 7.5.0</a>. Once downloaded, double click the msi file and follow the installation:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="491" height="382" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/PS7_install1.jpg" alt="" class="wp-image-36954" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/PS7_install1.jpg 491w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/PS7_install1-300x233.jpg 300w" sizes="auto, (max-width: 491px) 100vw, 491px" /></figure>



<p class="wp-block-paragraph">By default and as mentioned previously, PowerShell 7 will be installed on C:\Program Files\PowerShell\ :</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="489" height="385" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/PS7_install2.jpg" alt="" class="wp-image-36957" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/PS7_install2.jpg 489w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/PS7_install2-300x236.jpg 300w" sizes="auto, (max-width: 489px) 100vw, 489px" /></figure>



<p class="wp-block-paragraph">Some customization are possible, we will keep the default selections:<br><br></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="492" height="383" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/PS7_install3.jpg" alt="" class="wp-image-36959" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/PS7_install3.jpg 492w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/PS7_install3-300x234.jpg 300w" sizes="auto, (max-width: 492px) 100vw, 492px" /></figure>



<p class="wp-block-paragraph">Starting with PowerShell 7.2, it is possible to update PowerShell 7 with traditional Microsoft Update:<br></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="490" height="386" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/PS7_install4.jpg" alt="" class="wp-image-36961" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/PS7_install4.jpg 490w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/PS7_install4-300x236.jpg 300w" sizes="auto, (max-width: 490px) 100vw, 490px" /></figure>



<p class="wp-block-paragraph">After some seconds installation is done:<br></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="489" height="385" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/PS7_install7.jpg" alt="" class="wp-image-36965" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/PS7_install7.jpg 489w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/PS7_install7-300x236.jpg 300w" sizes="auto, (max-width: 489px) 100vw, 489px" /></figure>



<p class="wp-block-paragraph">We can start the PowerShell 7 with the cmd pwsh.exe. As we can see below both versions coexist on my Windows Server:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="686" height="619" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/PS7_install8.jpg" alt="" class="wp-image-36966" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/PS7_install8.jpg 686w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/PS7_install8-300x271.jpg 300w" sizes="auto, (max-width: 686px) 100vw, 686px" /></figure>



<h2 class="wp-block-heading" id="h-new-features">New features</h2>



<p class="wp-block-paragraph">PowerShell 7 introduces some interesting new features compare to PowerShell 5.</p>



<ul class="wp-block-list">
<li><a href="https://learn.microsoft.com/en-us/previous-versions/powershell/scripting/whats-new/what-s-new-in-powershell-70?view=powershell-7.3#parallel-execution-added-to-foreach-object">ForEach-Object with parallel execution</a><br>Execute the script block in parallel for each object. A parameter ThrottleLimit limits the number of script blocks running at the same time, default value is 5.<br>Here we search the instance properties by server and limit the parallelization to 2 server at the same time.<br>$computers = &#8216;thor90&#8242;,&#8217;thor91&#8242;,&#8217;thor10&#8242;,&#8217;thor11&#8217;<br>$InstanceProperties = $computers | ForEach-Object -Parallel {<br>$instances = (Find-DbaInstance -ComputerName $_).SqlInstance;<br>Get-DbaInstanceProperty -SqlInstance $instances<br>} -ThrottleLimit 2<br></li>



<li><a href="https://learn.microsoft.com/en-us/previous-versions/powershell/scripting/whats-new/what-s-new-in-powershell-70?view=powershell-7.3#ternary-operator">Ternary operator</a><br>A simplified if-else statement with &lt;condition&gt; ? &lt;condition true&gt; : &lt;condition false&gt;<br></li>



<li><a href="https://learn.microsoft.com/en-us/previous-versions/powershell/scripting/whats-new/what-s-new-in-powershell-70?view=powershell-7.3#pipeline-chain-operators">Pipeline chain operators</a><br>The &amp;&amp; operator executes the right-hand pipeline, if the left-hand pipeline succeeded. Reverse, the || operator executes the right-hand pipeline if the left-hand pipeline failed.<br></li>



<li><a href="https://learn.microsoft.com/en-us/previous-versions/powershell/scripting/whats-new/what-s-new-in-powershell-70?view=powershell-7.3#null-coalescing-assignment-and-conditional-operators">coalescence, assignment and conditional operators</a><br>PowerShell 7 includes Null coalescing operator ??, Null conditional assignment ??=, and Null conditional member access operators ?. and ?[]<br></li>



<li><a href="https://learn.microsoft.com/en-us/previous-versions/powershell/scripting/whats-new/what-s-new-in-powershell-70?view=powershell-7.3#new-view-conciseview-and-cmdlet-get-error">New management of error message and new cmdlet Get-Error</a><br>This new cmdlet Get-Error displays the full detailed of the last error with inner exceptions.<br>A parameter Newest allows to select the number of error you would like to display</li>
</ul>



<p class="wp-block-paragraph">On this blog, post I would to concentrate to the parallelization with the ForEach-Object -Parallel</p>



<h2 class="wp-block-heading" id="h-powershell-7-parallelization">PowerShell 7 parallelization</h2>



<p class="wp-block-paragraph">This new feature comes with the know ForEach-Object cmdlet which performs an operation on each item in a collection of input objects.<br>Starting with PowerShell 7.0 a new parameter set, called &#8220;Parallel&#8221;, gives the possibility to run each script block in parallel instead of sequentially. The &#8220;ThrottleLimit&#8221; parameter, if used, limits the number of script blocks which will run at the same time, if it is not specified the default value is 5.</p>



<p class="wp-block-paragraph"> ForEach-Object -Parallel &lt;scriptblock&gt; -ThrottleLimit</p>



<p class="wp-block-paragraph">We can test this new feature with a small example.<br>If we execute the following script as before, the script block is executed sequentially:</p>



<pre class="wp-block-code"><code>PS C:\Users\administrator.ADSTS&gt; 1..16 | ForEach-Object { Get-Date; sleep 10 }

Friday, February 14, 2025 9:00:02 AM
Friday, February 14, 2025 9:00:12 AM
Friday, February 14, 2025 9:00:22 AM
Friday, February 14, 2025 9:00:32 AM
Friday, February 14, 2025 9:00:42 AM
Friday, February 14, 2025 9:00:52 AM
Friday, February 14, 2025 9:01:02 AM
Friday, February 14, 2025 9:01:12 AM
Friday, February 14, 2025 9:01:22 AM
Friday, February 14, 2025 9:01:32 AM
Friday, February 14, 2025 9:01:42 AM
Friday, February 14, 2025 9:01:52 AM
Friday, February 14, 2025 9:02:02 AM
Friday, February 14, 2025 9:02:12 AM
Friday, February 14, 2025 9:02:22 AM
Friday, February 14, 2025 9:02:32 AM</code></pre>



<p class="wp-block-paragraph">Each line as 10 seconds more than the previous one.<br>But, if we execute this script with the new parameter Parallel and use a throttle limit of 4 we have:</p>



<pre class="wp-block-code"><code>PS C:\Users\administrator.ADSTS&gt; 1..16 | ForEach-Object -Parallel { Get-Date; sleep 10 } -ThrottleLimit 4

Friday, February 14, 2025 8:59:01 AM
Friday, February 14, 2025 8:59:01 AM
Friday, February 14, 2025 8:59:01 AM
Friday, February 14, 2025 8:59:01 AM
Friday, February 14, 2025 8:59:11 AM
Friday, February 14, 2025 8:59:11 AM
Friday, February 14, 2025 8:59:11 AM
Friday, February 14, 2025 8:59:11 AM
Friday, February 14, 2025 8:59:21 AM
Friday, February 14, 2025 8:59:21 AM
Friday, February 14, 2025 8:59:21 AM
Friday, February 14, 2025 8:59:21 AM
Friday, February 14, 2025 8:59:31 AM
Friday, February 14, 2025 8:59:31 AM
Friday, February 14, 2025 8:59:31 AM
Friday, February 14, 2025 8:59:31 AM</code></pre>



<p class="wp-block-paragraph">Here we have 4 groups of 4 lines with the same time as we executed the script block in parallel with limitation of the parallelization to 4.<br>Of course, the different commands included in the script block are executed sequentially.</p>



<p class="wp-block-paragraph">This feature uses the PowerShell runspaces to execute script blocks in parallel.<br>Variables can be passed into the script block with the $using: keyword, the only variable automatically passed is the pipe object.<br>Each runspace will execute a script block in a thread, so the ThrottleLimit parameter needs to be set according to the number of core of the server where you are running. If you VM has 2 cores, it makes no sense to put the limit to 4&#8230;</p>



<p class="wp-block-paragraph">This new script will execute a maintenance job on different instances of the same server, passing the job name in the block script with the $using: keyword:</p>



<pre class="wp-block-code"><code>PS C:\Users\administrator.ADSTS&gt; $ThrottleLimit = 2
PS C:\Users\administrator.ADSTS&gt; $JobName = 'DBI_MAINTENANCE_MAINTENANCE_USER_DATABASES'
PS C:\Users\administrator.ADSTS&gt; $computers = 'thor90'
PS C:\Users\administrator.ADSTS&gt; $SqlInstances = Find-DbaInstance -ComputerName $computers -EnableException
PS C:\Users\administrator.ADSTS&gt; $SqlInstances

ComputerName InstanceName SqlInstance    Port  Availability Confidence ScanTypes
------------ ------------ -----------    ----  ------------ ---------- ---------
thor90       CMS          thor90\CMS     50074 Available    High       Default
thor90       SQL16_1      thor90\SQL16_1 62919 Available    High       Default
thor90       SQL19_1      thor90\SQL19_1 1433  Available    High       Default
thor90       SQL22_1      thor90\SQL22_1 50210 Available    High       Default
thor90       MSSQLSERVER  thor90         1434  Available    High       Default

PS C:\Users\administrator.ADSTS&gt; $SqlInstances | ForEach-Object -Parallel {
&gt;&gt;     $Out = "Starting Job on $using:JobName &#091;" + $_.SqlInstance + "]"
&gt;&gt;     Write-Host $Out
&gt;&gt;     $res = Start-DbaAgentJob -SqlInstance $_.SqlInstance -Job $using:JobName -Wait
&gt;&gt; } -ThrottleLimit $ThrottleLimit
Starting Job on DBI_MAINTENANCE_MAINTENANCE_USER_DATABASES &#091;thor90\CMS]
Starting Job on DBI_MAINTENANCE_MAINTENANCE_USER_DATABASES &#091;thor90\SQL16_1]
Starting Job on DBI_MAINTENANCE_MAINTENANCE_USER_DATABASES &#091;thor90\SQL19_1]
Starting Job on DBI_MAINTENANCE_MAINTENANCE_USER_DATABASES &#091;thor90\SQL22_1]
Starting Job on DBI_MAINTENANCE_MAINTENANCE_USER_DATABASES &#091;thor90]
PS C:\Users\administrator.ADSTS&gt;</code></pre>



<p class="wp-block-paragraph">We use this kind of script at a customer place to execute SQL Server Agent Jobs in parallel on instances of a big physical servers with more than 10 instances.</p>



<h2 class="wp-block-heading" id="h-conclusion">Conclusion</h2>



<p class="wp-block-paragraph">This PowerShell 7 parallelization feature can improve performance in lots of different scenarios. But test it and don&#8217;t think that because of parallelization all your scripts will be executed quickly as running a script in parallel adds some overhead which will decrease execution of trivial script.</p>



<p class="wp-block-paragraph"></p>
<p>L’article <a href="https://www.dbi-services.com/blog/starting-with-powershell-7-and-parallelization/">Starting with PowerShell 7 and parallelization</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/starting-with-powershell-7-and-parallelization/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>MacOS: Reduce fan noise when using Microsoft Teams</title>
		<link>https://www.dbi-services.com/blog/macos-reduce-fan-noise-when-using-microsoft-teams/</link>
					<comments>https://www.dbi-services.com/blog/macos-reduce-fan-noise-when-using-microsoft-teams/#respond</comments>
		
		<dc:creator><![CDATA[Clemens Bleile]]></dc:creator>
		<pubDate>Tue, 28 Dec 2021 21:55:01 +0000</pubDate>
				<category><![CDATA[MS Teams]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/2021/12/28/macos-reduce-fan-noise-when-using-microsoft-teams/</guid>

					<description><![CDATA[<p>The Laptop I&#8217;m using for the business is a MacBook Pro, model 2019 (2.6 GHz 6-Core Intel Core i7). I&#8217;m thankful to be able to choose the Hardware and OS of my business laptop at dbi services. We have collegues using Laptops with Windows, some use Laptops with Linux and a considerable amout of people [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/macos-reduce-fan-noise-when-using-microsoft-teams/">MacOS: Reduce fan noise when using Microsoft Teams</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>The Laptop I&#8217;m using for the business is a MacBook Pro, model 2019 (2.6 GHz 6-Core Intel Core i7). I&#8217;m thankful to be able to choose the Hardware and OS of my business laptop at dbi services. We have collegues using Laptops with Windows, some use Laptops with Linux and a considerable amout of people use MacBooks. However, what annoyed me with my Laptop were MS Teams meetings when the fan noise went up considerably. Many people have  reported this several times to Apple and Microsoft but the issue has not been fixed yet. Users of Microsoft Windows have also reported that the load on their systems sometimes go up as well when they are in a MS Teams meeting. As tools like ZOOM or Webex do not have such an issue, it can be concluded that this issue is caused by the MS Teams software. Don&#8217;t get me wrong, MS Teams is a good software, but I desparately searched for methods to get my fan noise under better control. The issue may be even worse when working with an external display &#8211; most people obviously use external displays in their home offices.</p>
<p>Googling on this, one can find many suggestions on how to fix this issue, but most of them just recommend to make sure no dust is in the Laptop or suggest to not put too much load on the Mac. I.e. things most people check first and hence those suggestions are useless &#8211; especially because there is not much CPU-load on my Laptop when using MS Teams.</p>
<p>I tried many other things myself to fix this (like replacing an USB-3 cable to the external display with a DVI-cable as suggested by someone), but most of the recommendations did not help. One suggestion was to manually reduce/customize the fan speed with a tool like &#8220;Macs Fan Control&#8221;, but that caused the Mac to become so slow that it finally becomes unusable. </p>
<p>Here the 4 things which helped me to get this under better control:</p>
<p>1. Disable GPU hardware acceleration<br />
Go to: Microsoft Teams -&gt; Preferences -&gt; General in the section Application and make a mark at:<br />
Disable GPU hardware acceleration (requires restarting Teams)<br />
As mentioned in that line you have to restart MS Teams after the change to disable GPU hardware acceleration.</p>
<p>2. Disable the Intel Turbo Boost<br />
Install a tool like &#8220;Turbo Boost Switcher for Intel Macs&#8221; and disable Turbo Boost while using MS Teams.</p>
<p>3. Work with the external display &#8220;only&#8221; in your home office. I.e. using the MacBook built-in display and an external display together resulted in the fan being more active compared to running with the external display only and keeping the MacBook closed.</p>
<p>4. Some people also reported that the issue went away after using MS Teams Web (in the Browser) instead of the dedicated application. </p>
<p>With above modifications my fan sometimes still starts when using MS Teams, but it is OK and does not become too noisy.</p>
<p>L’article <a href="https://www.dbi-services.com/blog/macos-reduce-fan-noise-when-using-microsoft-teams/">MacOS: Reduce fan noise when using Microsoft Teams</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.dbi-services.com/blog/macos-reduce-fan-noise-when-using-microsoft-teams/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 
Lazy Loading (feed)

Served from: www.dbi-services.com @ 2026-10-20 17:09:25 by W3 Total Cache
-->