<?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>Hocine Mechara, auteur/autrice sur dbi Blog</title>
	<atom:link href="https://www.dbi-services.com/blog/author/hocinemechara/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.dbi-services.com/blog/author/hocinemechara/</link>
	<description></description>
	<lastBuildDate>Tue, 09 Jun 2026 15:19:26 +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>Hocine Mechara, auteur/autrice sur dbi Blog</title>
	<link>https://www.dbi-services.com/blog/author/hocinemechara/</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>SQL Server Always On Availability Groups and Database Master Keys: A Hidden Failover Pitfall</title>
		<link>https://www.dbi-services.com/blog/sql-server-always-on-availability-groups-and-database-master-keys-a-hidden-failover-pitfall/</link>
					<comments>https://www.dbi-services.com/blog/sql-server-always-on-availability-groups-and-database-master-keys-a-hidden-failover-pitfall/#respond</comments>
		
		<dc:creator><![CDATA[Hocine Mechara]]></dc:creator>
		<pubDate>Tue, 09 Jun 2026 15:01:28 +0000</pubDate>
				<category><![CDATA[Database management]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[Data Security]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[disaster recovery]]></category>
		<category><![CDATA[encryption]]></category>
		<category><![CDATA[High availability]]></category>
		<category><![CDATA[SQL-Server]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=44986</guid>

					<description><![CDATA[<p>I recently came across an interesting case involving a client whose application used SQL Server symmetric keys to encrypt sensitive data. The database was hosted in an Always On Availability Group environment for high-availability and disaster-recovery. The interesting and challenging aspect of this setup is ensuring that the encryption and decryption remains intact after a [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/sql-server-always-on-availability-groups-and-database-master-keys-a-hidden-failover-pitfall/">SQL Server Always On Availability Groups and Database Master Keys: A Hidden Failover Pitfall</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">I recently came across an interesting case involving a client whose application used SQL Server symmetric keys to encrypt sensitive data. The database was hosted in an Always On Availability Group environment for high-availability and disaster-recovery.</p>



<p class="wp-block-paragraph">The interesting and challenging aspect of this setup is ensuring that the encryption and decryption remains intact after a failover. </p>



<p class="wp-block-paragraph">This challenge originates from SQL Server&#8217;s encryption hierarchy. In a typical setup, the Service Master Key (SMK) on server level protects the Database Master Key (DMK) on database level. The Database Master Key, in turn, protects certificates and asymmetric keys, while certificates are commonly used to protect symmetric keys. These symmetric keys may then be used by the application to encrypt and decrypt sensitive data.</p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="509" height="529" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image.png" alt="" class="wp-image-44987" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image.png 509w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-289x300.png 289w" sizes="(max-width: 509px) 100vw, 509px" /></figure>



<p class="wp-block-paragraph">When a failover to a secondary replica occurs, the Service Master Key (SMK) is no longer the same as on the previous primary replica. Since the Database Master Key (DMK) is typically encrypted by the local Service Master Key, SQL Server may no longer be able to open the DMK transparently after the failover. As a result, any encryption objects that depend on the DMK, such as certificates and symmetric keys, may become inaccessible, potentially causing application failures when encrypted data needs to be read or written.</p>



<p class="wp-block-paragraph">Let&#8217;s now explore the built-in approaches in SQL Server to ensure that the encryption hierarchy remains functional across replicas and that applications continue to operate seamlessly after a failover.</p>



<p class="wp-block-paragraph">First, let&#8217;s create a test database and a Database Master Key:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
-- Create Demo DB
CREATE DATABASE TestDMK;
GO
--Backup DB
BACKUP DATABASE TestDMK
TO DISK = &#039;C:\SQLServer_mnt\BACKUP\TestDMK.bak&#039;;
go
-- Create Database Master Key
CREATE MASTER KEY
ENCRYPTION BY PASSWORD = &#039;MyVeryStrongPassword_123!&#039;;
GO
-- Check Master Key
SELECT DB_NAME() as DB, *
FROM sys.symmetric_keys
WHERE name = &#039;##MS_DatabaseMasterKey##&#039;;
GO

</pre></div>


<figure class="wp-block-image size-full"><img decoding="async" width="1022" height="265" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-1.png" alt="" class="wp-image-44988" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-1.png 1022w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-1-300x78.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-1-768x199.png 768w" sizes="(max-width: 1022px) 100vw, 1022px" /></figure>



<p class="wp-block-paragraph">It is worth noting that you can verify whether a Database Master Key is also encrypted by the Service Master Key by querying the <strong>sys.databases</strong> catalog view. When a Database Master Key is created, SQL Server typically adds encryption by the Service Master Key automatically. This behaviour can be changed by explicitly removing the Service Master Key encryption.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
select is_master_key_encrypted_by_server, * from sys.databases where name = &#039;TestDMK&#039;
</pre></div>


<figure class="wp-block-image size-full"><img decoding="async" width="961" height="272" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-2.png" alt="" class="wp-image-44989" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-2.png 961w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-2-300x85.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-2-768x217.png 768w" sizes="(max-width: 961px) 100vw, 961px" /></figure>



<p class="wp-block-paragraph">With the Database Master Key in place, let&#8217;s create a certificate and a symmetric key that will be used throughout the remainder of this demonstration.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
CREATE CERTIFICATE &#x5B;mainDBCert] --&gt; This cert is going to be encrypted by the dmk
WITH SUBJECT = &#039;test Cert&#039;;
go
CREATE SYMMETRIC KEY mainKey
WITH ALGORITHM = AES_256
ENCRYPTION BY CERTIFICATE mainDBCert;
go
SELECT * FROM sys.certificates;
SELECT * FROM sys.symmetric_keys WHERE name &lt;&gt; &#039;##MS_DatabaseMasterKey##&#039;;
SELECT * FROM sys.symmetric_keys;
go

</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="961" height="444" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-3.png" alt="" class="wp-image-44990" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-3.png 961w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-3-300x139.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-3-768x355.png 768w" sizes="auto, (max-width: 961px) 100vw, 961px" /></figure>



<p class="wp-block-paragraph">To demonstrate the encryption and decryption process, let&#8217;s create a simple table that will contain sensitive information and insert some data.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
--create table with sensitive data
CREATE TABLE CreditCards
(
    Id int,
    CreditCardNr varbinary(max),
    CVC varbinary(max),
    ExpirationDate varbinary(max)
);
go
--open symmetric key
OPEN SYMMETRIC KEY mainKey
DECRYPTION BY CERTIFICATE mainDBCert;
--insert sensitive data
INSERT INTO CredîtCards
VALUES
(
    1,
    EncryptByKey(Key_GUID(&#039;mainKey&#039;), &#039;1234-5678-9012-3456&#039;),
    EncryptByKey(Key_GUID(&#039;mainKey&#039;), &#039;123&#039;),
    EncryptByKey(Key_GUID(&#039;mainKey&#039;), &#039;1233&#039;)
);
CLOSE SYMMETRIC KEY mainKey;
GO
Select * from CreditCards

</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="961" height="575" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-21.png" alt="" class="wp-image-45008" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-21.png 961w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-21-300x180.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-21-768x460.png 768w" sizes="auto, (max-width: 961px) 100vw, 961px" /></figure>



<p class="wp-block-paragraph">If we query the table without opening the symmetric key and decrypting the values, we can see that we won’t see the data.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
--select data without decryption
SELECT
    Id,
    CONVERT(varchar(50), CreditCardNr) AS CreditCardNr,
    CONVERT(varchar(10), CVC) AS CVC,
    CONVERT(varchar(10), ExpirationDate) AS ExpirationDate
FROM CreditCards;

</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="893" height="559" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-5.png" alt="" class="wp-image-44992" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-5.png 893w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-5-300x188.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-5-768x481.png 768w" sizes="auto, (max-width: 893px) 100vw, 893px" /></figure>



<p class="wp-block-paragraph">However, if we open the symmetric key first and use it to decrypt the data, we are able to retrieve the original sensitive information:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
OPEN SYMMETRIC KEY mainKey
DECRYPTION BY CERTIFICATE mainDBCert;
GO
SELECT
    Id,
    CONVERT(varchar(50), DecryptByKey(CreditCardNr)) AS CreditCardNr,
    CONVERT(varchar(10), DecryptByKey(CVC)) AS CVC,
    CONVERT(varchar(10), DecryptByKey(ExpirationDate)) AS ExpirationDate
FROM CredîtCards;
GO
CLOSE SYMMETRIC KEY mainKey;
GO
</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="961" height="705" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-6.png" alt="" class="wp-image-44993" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-6.png 961w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-6-300x220.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-6-768x563.png 768w" sizes="auto, (max-width: 961px) 100vw, 961px" /></figure>



<p class="wp-block-paragraph">Now let&#8217;s add our database to an Always On Availability Group to provide high availability and disaster recovery. If you follow the Availability Group wizard, you may notice that the wizard asks you to enter a password. What the wizards is asking for here is the password of the Database Master Key (DMK). The DMK protects the certificate in our encryption hierarchy, and the certificate, in turn, protects the symmetric key used to encrypt and decrypt the sensitive data.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="961" height="705" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-7.png" alt="" class="wp-image-44994" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-7.png 961w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-7-300x220.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-7-768x563.png 768w" sizes="auto, (max-width: 961px) 100vw, 961px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="961" height="254" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-9.png" alt="" class="wp-image-44996" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-9.png 961w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-9-300x79.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-9-768x203.png 768w" sizes="auto, (max-width: 961px) 100vw, 961px" /></figure>



<p class="wp-block-paragraph">On the right-hand side of the wizard, there is a small and easily overlooked text box where you can enter the Database Master Key password.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="961" height="207" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-10.png" alt="" class="wp-image-44997" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-10.png 961w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-10-300x65.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-10-768x165.png 768w" sizes="auto, (max-width: 961px) 100vw, 961px" /></figure>



<p class="wp-block-paragraph">And now comes the tricky &#8211; and at the same time somewhat surprising &#8211; part. If you use automatic seeding, which is my preferred option whenever the database size allows it, the process will skip the part where the Master Key password is verified and applied on the secondary replicas.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="904" height="343" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-11.png" alt="" class="wp-image-44998" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-11.png 904w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-11-300x114.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-11-768x291.png 768w" sizes="auto, (max-width: 904px) 100vw, 904px" /></figure>



<p class="wp-block-paragraph">As a result, when a failover occurs, SQL Server would no longer be able to open the Database Master Key (DMK) transparently on the new primary replica. Consequently, the application would no longer be able to encrypt or decrypt data unless the Database Master Key is explicitly opened first with the password.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="961" height="591" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-12.png" alt="" class="wp-image-44999" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-12.png 961w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-12-300x184.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-12-768x472.png 768w" sizes="auto, (max-width: 961px) 100vw, 961px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="961" height="759" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-13.png" alt="" class="wp-image-45000" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-13.png 961w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-13-300x237.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-13-768x607.png 768w" sizes="auto, (max-width: 961px) 100vw, 961px" /></figure>



<p class="wp-block-paragraph">In such a scenario, the issue can be resolved by storing the Database Master Key password as a credential on each replica using the <strong>sp_control_dbmasterkey_password </strong>stored procedure. SQL Server can then use this credential to automatically open the Database Master Key after a failover, allowing the encryption hierarchy to remain intact and ensuring that certificates, symmetric keys, and encrypted data remain accessible transparently.</p>



<p class="wp-block-paragraph"><strong>!!Note that you must do that on each replica!!</strong></p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
sp_control_dbmasterkey_password @db_name = N&#039;TestDMK&#039;
    , @password = N&#039;MyVeryStrongPassword_123!&#039;
    , @action = N&#039;add&#039;;
select * from sys.master_key_passwords;
select * from sys.credentials;

</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="961" height="368" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-14.png" alt="" class="wp-image-45001" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-14.png 961w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-14-300x115.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-14-768x294.png 768w" sizes="auto, (max-width: 961px) 100vw, 961px" /></figure>



<p class="wp-block-paragraph">Once the credential has been created, SQL Server is again able to open the Database Master Key transparently. As a result, the application can continue to encrypt and decrypt data without having to explicitly open the Master Key with the password.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="961" height="562" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-15.png" alt="" class="wp-image-45002" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-15.png 961w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-15-300x175.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-15-768x449.png 768w" sizes="auto, (max-width: 961px) 100vw, 961px" /></figure>



<p class="wp-block-paragraph">There is also an option by which SQL Server automatically creates the Database Master Key credential across all replicas when a database is joined to an Availability Group. This happens when <strong>&#8220;Full Database Backup and Log Backup&#8221;</strong> is selected as the initial data synchronization option in the Availability Group wizard.</p>



<p class="wp-block-paragraph">To demonstrate this, let&#8217;s remove the previously created credential using the same stored procedure.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
sp_control_dbmasterkey_password @db_name = N&#039;TestDMK&#039;
    , @password = N&#039;MyVeryStrongPassword_123!&#039;
    , @action = N&#039;drop&#039;;
select * from sys.master_key_passwords;
select * from sys.credentials;

</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="961" height="823" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-16.png" alt="" class="wp-image-45003" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-16.png 961w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-16-300x257.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-16-768x658.png 768w" sizes="auto, (max-width: 961px) 100vw, 961px" /></figure>



<p class="wp-block-paragraph">Now, let&#8217;s select <strong>&#8220;Full Database and Log Backup&#8221;</strong> as the initial data synchronization option in the Availability Group wizard.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="961" height="875" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-17.png" alt="" class="wp-image-45004" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-17.png 961w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-17-300x273.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-17-768x699.png 768w" sizes="auto, (max-width: 961px) 100vw, 961px" /></figure>



<p class="wp-block-paragraph">As you can see, the process is no longer skipping the Database Master Key password validation step.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="961" height="899" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-18.png" alt="" class="wp-image-45005" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-18.png 961w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-18-300x281.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-18-768x718.png 768w" sizes="auto, (max-width: 961px) 100vw, 961px" /></figure>



<p class="wp-block-paragraph">In the summary, you can see that the process automatically created the Database Master Key password credential on each replica.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="961" height="884" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-19.png" alt="" class="wp-image-45006" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-19.png 961w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-19-300x276.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-19-768x706.png 768w" sizes="auto, (max-width: 961px) 100vw, 961px" /></figure>



<p class="wp-block-paragraph">If you check the credentials on each replica afterwards, you can verify that they were created automatically.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="910" height="412" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-20.png" alt="" class="wp-image-45007" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-20.png 910w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-20-300x136.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2026/06/image-20-768x348.png 768w" sizes="auto, (max-width: 910px) 100vw, 910px" /></figure>



<p class="wp-block-paragraph">As the Database Master Key password credential has been added to all replicas, SQL Server can transparently open the DMK after a failover. As a result, the application can continue to encrypt and decrypt data without having to explicitly open the Database Master Key.</p>



<p class="wp-block-paragraph"><strong>But one question still puzzles me:</strong> why the same operation isn&#8217;t performed automatically when automatic seeding is used?</p>



<p class="wp-block-paragraph">Honestly, I have no clue yet. If you know more let me know it in the comments section. I&#8217;d love to learn more about it.</p>



<p class="wp-block-paragraph">Thanks for reading – Hocine 😉</p>
<p>L’article <a href="https://www.dbi-services.com/blog/sql-server-always-on-availability-groups-and-database-master-keys-a-hidden-failover-pitfall/">SQL Server Always On Availability Groups and Database Master Keys: A Hidden Failover Pitfall</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/sql-server-always-on-availability-groups-and-database-master-keys-a-hidden-failover-pitfall/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SQL-Server 2025: Building a RAG Shopping Assistant with T-SQL</title>
		<link>https://www.dbi-services.com/blog/sql-server-2025-building-a-rag-shopping-assistant-with-t-sql/</link>
					<comments>https://www.dbi-services.com/blog/sql-server-2025-building-a-rag-shopping-assistant-with-t-sql/#respond</comments>
		
		<dc:creator><![CDATA[Hocine Mechara]]></dc:creator>
		<pubDate>Thu, 09 Oct 2025 16:19:59 +0000</pubDate>
				<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[RAG]]></category>
		<category><![CDATA[RAG-Search]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=40822</guid>

					<description><![CDATA[<p>In this post, we continue our AI journey with SQL Server 2025 (Preview). In previous articles, I already showed how to call the GPT-4o chat-completion model in Azure directly from SQL Server to generate product descriptions, how to create and store embeddings for semantic search, and how to use the (preview) acceleration features for semantic [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/sql-server-2025-building-a-rag-shopping-assistant-with-t-sql/">SQL-Server 2025: Building a RAG Shopping Assistant with T-SQL</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>In this post, we continue our AI journey with SQL Server 2025 (Preview). In previous articles, I already showed how to call the GPT-4o chat-completion model in Azure directly from SQL Server to generate product descriptions, how to create and store embeddings for semantic search, and how to use the (preview) acceleration features for semantic search workloads. If you want to dive into any of those topics, check out the posts below:</p>


<ul class="wp-block-list">
<li>Calling GPT-4o from SQL Server to generate product content: <a href="https://www.dbi-services.com/blog/lift-your-application-in-the-age-of-ai-with-sql-server-2025/">https://www.dbi-services.com/blog/lift-your-application-in-the-age-of-ai-with-sql-server-2025/</a></li>



<li>Generating &amp; storing embeddings in SQL Server 2025: <a href="https://www.dbi-services.com/blog/sql-server-2025-generate-data-embeddings-for-semantic-search/">https://www.dbi-services.com/blog/sql-server-2025-generate-data-embeddings-for-semantic-search/</a></li>



<li>Accelerating Semantic Search workloads with vector Indexes: <a href="https://www.dbi-services.com/blog/sql-server-2025-vector-indexes-semantic-search-performance/">https://www.dbi-services.com/blog/sql-server-2025-vector-indexes-semantic-search-performance/</a></li>
</ul>



<p class="wp-block-paragraph">In this post now, we will combine some of these functionalities to implement a simple RAG based shopping assistant with SQL Server 2025 (Preview).</p>



<h2 class="wp-block-heading" id="h-what-is-rag">What is RAG?</h2>


<p>But first of all I will introduce you in the concept of RAG. As you may already noticed in recent years, AI chat models are great. They are able to understand your prompts, they can perform reasoning to “think” about what you ask them and they can provide you an individual answer. But the model alone is limited to the data on which it was trained on.</p>

<p>So when I ask a chat model like GPT-5 a very general question like “What is the capital of Australia?” the model is able to answer this question without any problem because it’s general knowledge and the model certainly came into touch with this knowledge through its training data. But when I ask the model something which is only known through access on specific ( usually private) data sources  the model is not able to answer such a question at least not alone. For example  when I ask the model how many vacation days  I have left for this year  the model  is obviously not able to answer such a question because such information’s are stored in  company internal tools:</p>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="616" height="443" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild1.png" alt="" class="wp-image-40823" style="width:626px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild1.png 616w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild1-300x216.png 300w" sizes="auto, (max-width: 616px) 100vw, 616px" /></figure>


<p>This  is the point where Retrieval  Augmented Generation (RAG) comes into play. RAG is an approach that searches external data sources for information relevant to the user&#8217;s prompt. The retrieved data is then used to augment the generation process of the AI model, allowing it to produce a context-aware and more accurate response.</p>

<p>And this is what we will implement within this blog post. We will build a simple chat service  which will help the customers of the ShopAI Online Shop to find the product of their desire.</p>


<h2 class="wp-block-heading" id="h-rag-architecture">RAG Architecture:</h2>


<p>Let’s take a look at the RAG  architecture for our purpose.  Therefore take a look at the  schema below  and on the numbers which are representing steps in the RAG-process:</p>


<ol class="wp-block-list">
<li>The customer sends a prompt  to the ShopAI chat service where he describes which product he or she is looking for.</li>



<li>The prompt is taken by the chat service and is sent to the embedding  model hosted on azure to generate  and return an embedding for that prompt.</li>



<li>The embedding of the customers prompt is then used to perform a semantic search on the products data in the ShopAI database.</li>



<li>The Top N semantic matching products are then sent to the chat model hosted on Azure for augmenting the generation of the response for the customers prompt.</li>



<li>The augmented response is then sent to the customer.</li>
</ol>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="944" height="366" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild2.png" alt="" class="wp-image-40824" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild2.png 944w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild2-300x116.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild2-768x298.png 768w" sizes="auto, (max-width: 944px) 100vw, 944px" /></figure>


<p>We will be able to handle the whole data logic inside T-SQL with the latest features of SQL-Server 2025 (Preview).</p>


<h2 class="wp-block-heading" id="h-preparing-the-ai-models">Preparing the AI Models:</h2>


<p>So far so good. Let’s start now with the practical implementation of our vision.</p>

<p>The AI models I’m using are the gpt-4o-mini model for the chat completion tasks and the text-embedding-3-small model to create embeddings for the users prompt. Embeddings for product data are already persisted in our database (refer to the earlier blog for details). When generating embeddings for user queries, use the exact same embedding model and settings used for the stored data.</p>

<p>To learn how to deploy an AI model in Azure you should also check out my previous blog posts where I’m guiding you through the process. For this article my models are already deployed in Azure AI Foundry:</p>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="770" height="391" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild3.png" alt="" class="wp-image-40825" style="width:800px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild3.png 770w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild3-300x152.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild3-768x390.png 768w" sizes="auto, (max-width: 770px) 100vw, 770px" /></figure>


<p>To use now the models inside T-SQL we have to create first a database scoped credential for the chat completion model. You can do that with the following statement. You’ll find the API key inside Azure AI Foundry (Check out the previous Blog posts for detailed instructions):</p>

<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
Use ShopAI
Go
-- Create database scoped credential:
CREATE DATABASE SCOPED CREDENTIAL &#x5B;https://shopai-blog.openai.azure.com/openai/deployments/gpt-4o-mini/] /* always youse an url which is more generic*/
    WITH IDENTITY = &#039;HTTPEndpointHeaders&#039;, secret = &#039;{&quot;api-key&quot;:&quot;YOUR-API-KEY&quot;}&#039;;
GO

</pre></div>

<p>Then we will do the same for the embedding model:</p>

<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
Use ShopAI
Go
-- Create database scoped credential:
CREATE DATABASE SCOPED CREDENTIAL &#x5B; https://shopai-blog.openai.azure.com/openai/deployments/text-embedding-3-small/] /* always youse an url which is more generic*/
    WITH IDENTITY = &#039;HTTPEndpointHeaders&#039;, secret = &#039;{&quot;api-key&quot;:&quot;YOUR-API-KEY&quot;}&#039;;
GO

</pre></div>

<p>And then we will register our embedding model to be able to generate data embeddings with the T-SQL built in functions:</p>

<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
Use ShopAI
-- Create EXTERNAL MODEL
CREATE EXTERNAL MODEL OpenAITextEmbedding3Small
AUTHORIZATION dbo
WITH (
      LOCATION = &#039;https://shopai-blog.openai.azure.com/openai/deployments/text-embedding-3-small/embeddings?api-version=2023-05-15&#039;,
      API_FORMAT = &#039;Azure OpenAI&#039;,
      MODEL_TYPE = EMBEDDINGS,
      MODEL = &#039;text-embedding-3-small&#039;,
      CREDENTIAL = &#x5B;https://shopai-blog.openai.azure.com/openai/deployments/text-embedding-3-small/],
      PARAMETERS = &#039;{&quot;Dimensions&quot;:1536}&#039;
);

</pre></div>


<h2 class="wp-block-heading" id="h-rag-stored-procedure">RAG Stored Procedure:</h2>


<p>As we have now an AI model for chat completion and one for generating data embeddings, we are now ready to write our code for processing the data logic. We will handle all the data logic and interaction with the AI models within a T-SQL stored procedure. I will explain the stored procedure I wrote for that in this chapter.</p>

<p>We have two Input Parameters which is the <strong>@SearchText</strong> parameter and the <strong>@ChatHistory</strong> Parameter. The <strong>@SearchText</strong> parameter represents the input text which the user is giving to our shopping assistant and the <strong>@ChatHistory</strong> is representing the history of the conversation and is per default set to NULL. We will handle the history of the conversation outside of the stored procedure.</p>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="564" height="170" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild4.png" alt="" class="wp-image-40826" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild4.png 564w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild4-300x90.png 300w" sizes="auto, (max-width: 564px) 100vw, 564px" /></figure>


<p>Then the stored procedure will generate an embedding of the user query which is stored in the <strong>@SearchText</strong> variable and it will perform an exact vector distance search based on the product catalog data and the user query. Then it stores the top 5 findings as a list of JSON objects with the attributes product name, product description and product price:</p>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="770" height="494" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild5.png" alt="" class="wp-image-40827" style="width:840px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild5.png 770w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild5-300x192.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild5-768x493.png 768w" sizes="auto, (max-width: 770px) 100vw, 770px" /></figure>


<p>In the next step the Stored procedure generates a prompt for the AI chat completion model without a previous chat history (in case the <strong>@ChatHistory</strong> parameter is NULL):</p>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="770" height="435" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild6.png" alt="" class="wp-image-40828" style="width:840px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild6.png 770w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild6-300x169.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild6-768x434.png 768w" sizes="auto, (max-width: 770px) 100vw, 770px" /></figure>


<p>If there is a previous chat history (the <strong>@ChatHistory</strong> parameter is not NULL) the stored procedure is generating a prompt which is also including the provided chat history data:</p>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="770" height="468" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild7.png" alt="" class="wp-image-40829" style="width:840px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild7.png 770w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild7-300x182.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild7-768x467.png 768w" sizes="auto, (max-width: 770px) 100vw, 770px" /></figure>


<p>And finally the stored procedure calls the API of the chat completion model with the generated prompt and returns/selects the response from the model:</p>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="770" height="280" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild8.png" alt="" class="wp-image-40830" style="width:840px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild8.png 770w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild8-300x109.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild8-768x279.png 768w" sizes="auto, (max-width: 770px) 100vw, 770px" /></figure>


<p>The process of RAG isn’t that complicated, right? To recap:</p>


<ol class="wp-block-list">
<li>You take the user query and generate an embedding for it.</li>



<li>You use that embedding to semantically search your relevant context data.</li>



<li>You prompt a chat completion model, augmenting your prompt with the retrieved context.</li>
</ol>


<p>Of course, in practice things can get more complex but at a high level, that’s the essence of how RAG works.</p>

<p>Let’s test the stored procedure with a sample user request and let’s see if we get a nice answer back with some product recommendations:</p>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="770" height="212" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild9.png" alt="" class="wp-image-40831" style="width:840px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild9.png 770w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild9-300x83.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild9-768x211.png 768w" sizes="auto, (max-width: 770px) 100vw, 770px" /></figure>


<p>Not bad for some few lines of code. This is the text I got back for my request:</p>


<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><td>Great choice! 💙 Here are some stylish blue t-shirts that you might love:  1. **Nike V-Neck Tees (Blue)** &#8211; **21.20 CHF**    (Thoughts: This tee is perfect for sunny days with its breathable fabric and flattering V-neck cut, making it a great choice for both comfort and style!)  2. **Boohoo Graphic Tees (Blue)** &#8211; **20.86 CHF**    (Thoughts: If you want to stand out, this tee features bold graphics and a vibrant color, ideal for expressing your unique personality during casual outings!)  3. **Fast Retailing V-Neck Tees (Blue)** &#8211; **23.93 CHF**    (Thoughts: This lightweight tee is designed for women who crave comfort and style, making it perfect for pairing with jeans or skirts for a chic summer look!)  4. **Madewell V-Neck Tees (Blue)** &#8211; **58.35 CHF**    (Thoughts: For a more premium option, this tee offers a refreshing hue and modern design that instantly elevates your casual attire while keeping you cool!)  5. **Armani Exchange V-Neck Tees (Blue)** &#8211; **55.08 CHF**    (Thoughts: If you&#8217;re looking for a touch of designer elegance, this tee is crafted for comfort and style, ensuring a flattering fit for warm days!)  Let me know if you need more information or help with your purchase! 😊</td></tr></tbody></table></figure>



<h2 class="wp-block-heading" id="h-shop-assistant-web-app">Shop Assistant Web app</h2>


<p>So we have now a stored procedure which is able to generate context aware product recommendations. Now let’s wrap that up in a better-looking front end.</p>

<p>I’m building that it in Python with Streamlit, a framework for quickly spinning up web apps, perfect for rapid prototyping. Make therefore sure that you have streamlit installed in your environment. So let’s take a look at the python code.</p>

<p>First I’m importing the modules which we need for our ShopAI Assistant. This is “pyodbc” for interacting with our database, “os” to read environment variables from operating system, “streamlit” itself, the “time” module to work efficient with time data, the “load_dotenv” method to load our environment variables into the operating system and “json” to work with json data:</p>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="420" height="233" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild10.png" alt="" class="wp-image-40833" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild10.png 420w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild10-300x166.png 300w" sizes="auto, (max-width: 420px) 100vw, 420px" /></figure>


<p>Then I’m creating a .env file in my project directory to store our environment variables, which are the Server and Instance Name where our ShopAI database is running on, a SQL user with a password to connect to the database (make sure the user exists and has the appropriate rights):</p>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="513" height="200" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild11.png" alt="" class="wp-image-40834" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild11.png 513w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild11-300x117.png 300w" sizes="auto, (max-width: 513px) 100vw, 513px" /></figure>


<p>In my python code I then wrote a function as a wrapper around the SQL stored procedure:</p>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="770" height="315" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild12.png" alt="" class="wp-image-40836" style="width:840px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild12.png 770w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild12-300x123.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild12-768x314.png 768w" sizes="auto, (max-width: 770px) 100vw, 770px" /></figure>


<p>Then I’m loading the environment variables from our file into the operating system and I’m building our pyodbc connection string:</p>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="549" height="270" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild13.png" alt="" class="wp-image-40837" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild13.png 549w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild13-300x148.png 300w" sizes="auto, (max-width: 549px) 100vw, 549px" /></figure>


<p>Then we continue with Streamlit. We start by initializing the first assistant message in the st.session_state variable, a built-in Streamlit object that stores session data such as the conversation history. After that, we loop through all messages in the session state and display them to the user. Finally, we wait for new user input through the chat input field, so the conversation can continue interactively.</p>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="770" height="163" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild14.png" alt="" class="wp-image-40838" style="width:840px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild14.png 770w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild14-300x64.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild14-768x163.png 768w" sizes="auto, (max-width: 770px) 100vw, 770px" /></figure>


<p>Next, we handle the user input. When the user enters a message, it’s captured by <strong>st.chat_input()</strong>.</p>

<p>We then check if there are already previous messages stored in <strong>st.session_state</strong>. If yes, we convert the chat history (except for the very first assistant message) into JSON format and store it in the variable <strong>chat_history</strong>. Otherwise, we set <strong>chat_history</strong> to None. We do this for handling the conversation history inside python and pass it to the SQL stored procedure.</p>

<p>We will then construct the input parameters for our <strong>get_product_recommendation</strong> function and we store the parameters inside a tuple which we call &#8220;<strong>p&#8221;</strong>.</p>

<p>Then we append and display the user prompt to the chat front end.</p>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="738" height="378" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild15.png" alt="" class="wp-image-40839" style="width:840px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild15.png 738w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild15-300x154.png 300w" sizes="auto, (max-width: 738px) 100vw, 738px" /></figure>


<p>Finally we invoke the SQL stored procedure through our python function and we will display the response from the AI model in a typing like manner:</p>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="770" height="326" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild23.png" alt="" class="wp-image-40855" style="width:840px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild23.png 770w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild23-300x127.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild23-768x325.png 768w" sizes="auto, (max-width: 770px) 100vw, 770px" /></figure>



<h2 class="wp-block-heading" id="h-testing-the-shopai-assistant">Testing the ShopAI assistant</h2>


<p>We have now wrote a stored procedure for processing the data logic and interacting with the gpt embedding and chat completion model and we have a simple Streamlit app to handle the conversation history and for the front end.</p>

<p>Let’s test what we did. To start the Streamlit web app I simple run the command &#8220;streamlit run app.py2:</p>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="541" height="190" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild24.png" alt="" class="wp-image-40857" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild24.png 541w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild24-300x105.png 300w" sizes="auto, (max-width: 541px) 100vw, 541px" /></figure>


<p>A browser window should open and you will be able to see the front end of the shopping assistant:</p>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="496" height="427" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild18.png" alt="" class="wp-image-40842" style="width:559px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild18.png 496w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild18-300x258.png 300w" sizes="auto, (max-width: 496px) 100vw, 496px" /></figure>


<p>Fine! let’s ask him who he is and how he can helps us …</p>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="498" height="432" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild19.png" alt="" class="wp-image-40843" style="width:570px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild19.png 498w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild19-300x260.png 300w" sizes="auto, (max-width: 498px) 100vw, 498px" /></figure>


<p>Looks like he knows his purpose😉 let’s see if he is able to find the products of our desire. I’m looking for some sport shoes …</p>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="525" height="523" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild20.png" alt="" class="wp-image-40844" style="width:573px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild20.png 525w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild20-300x300.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild20-150x150.png 150w" sizes="auto, (max-width: 525px) 100vw, 525px" /></figure>


<p>Awesome! We are getting a suitable response! The only weakness I see is, that we are getting twice the product “New Look Sneakers (Black)” recommended but with different prices.</p>

<p>But when looking at the data below it’s actually not the fault of our assistant because we have this product two times in our product catalogue. Good point to see how data quality matters 😉:</p>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="616" height="167" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild21.png" alt="" class="wp-image-40845" style="width:721px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild21.png 616w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild21-300x81.png 300w" sizes="auto, (max-width: 616px) 100vw, 616px" /></figure>


<p>I’m interested in the &#8220;Skechers Sneakers (Black)&#8221; but I’m not fully convinced yet. Let’s see if the assistant is doing a great job convincing me:</p>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="530" height="528" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild22.png" alt="" class="wp-image-40846" style="width:592px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild22.png 530w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild22-300x300.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/10/Bild22-150x150.png 150w" sizes="auto, (max-width: 530px) 100vw, 530px" /></figure>


<p>Sounds good for my! I’m totally convinced that these sneakers would perfectly match to my desire. Assistant you made a good job!</p>


<h2 class="wp-block-heading" id="h-key-takeaways">Key Takeaways</h2>


<p>As you’ve seen in this post, we were able to easily build an AI shopping assistant, with all the data processing and RAG logic running directly inside SQL Server 2025 (Preview)! We only used python for handling the conversation history and for a nice looking front end with Streamlit.</p>

<p>If you are looking for more inspiration regarding this topic I can highly recommend you to visit the following GitHub page from Microsoft: <a href="https://github.com/Azure-Samples/azure-sql-db-chatbot/">https://github.com/Azure-Samples/azure-sql-db-chatbot/</a></p>

<p>Note that you will find all the code I wrote and explained in this blog on my GitHub: <a href="https://github.com/HocineMechara/ShopAI-AIAssistantBlog.git">https://github.com/HocineMechara/ShopAI-AIAssistantBlog.git</a></p>

<p>Let me know your thoughts on AI, and especially on RAG, in the comments section, I’d be delighted to read your insights and discuss this topic further!</p>

<p>Thanks for reading, Hocine 😉</p><p>L’article <a href="https://www.dbi-services.com/blog/sql-server-2025-building-a-rag-shopping-assistant-with-t-sql/">SQL-Server 2025: Building a RAG Shopping Assistant with T-SQL</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/sql-server-2025-building-a-rag-shopping-assistant-with-t-sql/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SQL-Server 2025: Vector Indexes &#038; Semantic Search Performance</title>
		<link>https://www.dbi-services.com/blog/sql-server-2025-vector-indexes-semantic-search-performance/</link>
					<comments>https://www.dbi-services.com/blog/sql-server-2025-vector-indexes-semantic-search-performance/#respond</comments>
		
		<dc:creator><![CDATA[Hocine Mechara]]></dc:creator>
		<pubDate>Wed, 20 Aug 2025 15:53:40 +0000</pubDate>
				<category><![CDATA[Database management]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[data]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=40147</guid>

					<description><![CDATA[<p>When performing search operations on large datasets, performance is a key consideration for ensuring that an application remains efficient and user-friendly. This consideration equally applies when working with embedded data. That’s why in this blog post we’ll explore the different possibilities for vector search and show how to leverage the latest features of SQL Server [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/sql-server-2025-vector-indexes-semantic-search-performance/">SQL-Server 2025: Vector Indexes &amp; Semantic Search Performance</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>When performing search operations on large datasets, performance is a key consideration for ensuring that an application remains efficient and user-friendly. This consideration equally applies when working with embedded data. That’s why in this blog post we’ll explore the different possibilities for vector search and show how to leverage the latest features of SQL Server 2025 Preview to accelerate your semantic search workloads.</p>


<h2 class="wp-block-heading" id="h-exact-and-approximate-search">Exact and Approximate Search:</h2>



<p class="wp-block-paragraph">First, let’s talk about the different functions available in SQL-Server 2025 Preview and the concepts behind them.</p>



<p class="wp-block-paragraph">If you read my last <a href="https://www.dbi-services.com/blog/sql-server-2025-generate-data-embeddings-for-semantic-search/">blog </a>post, you may remember that we used the VECTOR_DISTANCE() function to search for black pants I might wear during my summer vacation. This function calculates the Exact Nearest Neighbors (ENN) for the vector of the search text. In practice, it calculates the vector distance between the given vector and all other vectors, then sorts the results and returns the top (K-) Nearest Neighbors (NN). In the context of OLTP systems and B-tree indexes, you can compare this process to an index scan operation with additional sorting and computation overhead. As you can already anticipate, calculating the Exact Nearest Neighbors can be quite resource-intensive depending on the amount of data. In the Microsoft documentation, the general recommendation is to perform an exact search when you have fewer than 50’000 vectors &#8211; noting that you can also prefilter the number of vectors using predicates.</p>



<p class="wp-block-paragraph">When working with large vector datasets the VECTOR_SEARCH() function comes into play. Instead of scanning the entire dataset and calculating the distance for each vector, the VECTOR_SEARCH() function approximates the nearest neighbors (ANN) for a given vector using vector index structures. While ANN requires far fewer resources than exact K-NN, it comes with a trade-off in terms of accuracy.</p>



<p class="wp-block-paragraph">This trade-off in accuracy is measured by the recall value. Recall represents the overlap between the approximate neighbors found and the true K-NN set. For example, if we search for the 10 nearest neighbors of a vector and ANN returns 7 results that are also in the exact K-NN set, the recall would be 0.7 (7 /10 = 0.9). The closer the recall is to 1, the more accurate the ANN results are.</p>



<p class="wp-block-paragraph">In SQL-Server 2025 Preview, a vector index is required to perform ANN searches with the VECTOR_SEARCH() function. SQL-Server 2025 Preview uses the DiskANN algorithm to create these graph-based vector indexes. DiskANN is designed to leverage SSDs and minimal memory resources for storing and handling graph indexes, while still maintaining high accuracy with a recall of around 0.95.</p>



<p class="wp-block-paragraph">Note: While the VECTOR_DISTANCE&nbsp; function is available in SQL-Server 2025 preview and Azure SQL, the VECTOR_SEARCH function is currently only available in SQL-Server 2025 Preview.</p>



<p class="wp-block-paragraph">You can get more information&#8217;s from Microsoft&#8217;s <a href="https://learn.microsoft.com/en-us/sql/relational-databases/vectors/vectors-sql-server?view=sql-server-ver17">documentation</a>.</p>



<h2 class="wp-block-heading" id="h-let-s-take-a-practical-look-at-the-features">Let’s take a practical look at the features:</h2>


<p>First of all we have to enable some trace flags to be able to use the preview features:</p>

<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
	DBCC TRACEON(466, 474, 13981, -1);
	DBCC TRACESTATUS;
</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="383" height="270" src="http://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/1-2.png" alt="" class="wp-image-40148" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/1-2.png 383w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/1-2-300x211.png 300w" sizes="auto, (max-width: 383px) 100vw, 383px" /></figure>


<p>Then I’m creating a vector index on my “embedding” column in my “dbo.products” table. I’m using the DiskANN algorithm which is currently the only one which is supported. Further I’m using the “cosine” metric for the similarity calculation. You can also use “Euclidean Distance” or “Dot Product” as similarity metrics.</p>

<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
CREATE VECTOR INDEX vec_idx ON &#x5B;dbo].&#x5B;products](&#x5B;embedding]) 
WITH (METRIC = &#039;cosine&#039;, TYPE = &#039;diskann&#039;);
</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="502" height="257" src="http://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/2-2.png" alt="" class="wp-image-40149" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/2-2.png 502w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/2-2-300x154.png 300w" sizes="auto, (max-width: 502px) 100vw, 502px" /></figure>



<p class="wp-block-paragraph">Then we are ready to go and we can test the VECTOR_SEARCH function with the query below:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
--Test Approximate Search

DECLARE @SemanticSearchText Nvarchar(max) = &#039;Im looking for black pants for men which I can wear during my summer vacation.&#039;
DECLARE @qv VECTOR(1536) = AI_GENERATE_EMBEDDINGS(@SemanticSearchText USE MODEL OpenAITextEmbedding3Small);

SELECT
		product_id,
		product_name, 
		Description,
		price,
		s.season,
		g.gender
		FROM
			VECTOR_SEARCH(
				TABLE = &#x5B;dbo].&#x5B;products] AS p, 
				COLUMN = &#x5B;embedding], 
				SIMILAR_TO = @qv, 
				METRIC = &#039;cosine&#039;, 
				TOP_N = 10
			) AS vs
		inner join dbo.season s on p.season_id = s.season_id
		inner join dbo.gender g on p.gender_id = g.gender_id
	ORDER BY vs.distance

</pre></div>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="616" height="477" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/3-4.png" alt="" class="wp-image-40158" style="width:667px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/3-4.png 616w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/3-4-300x232.png 300w" sizes="auto, (max-width: 616px) 100vw, 616px" /></figure>


<p>As you can see we are getting a pretty accurate result. Lets compare the results of the exact search using the VECTOR_DISTANCE function and the approximate search using the VECTOR_SEARCH function with the code below:</p>

<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
DECLARE @SemanticSearchText Nvarchar(max) = &#039;Im looking for black pants for men which I can wear during my summer vacation.&#039;
  
    DECLARE @qv VECTOR(1536) = AI_GENERATE_EMBEDDINGS(@SemanticSearchText USE MODEL OpenAITextEmbedding3Small);
 
 --Exact Search

    SELECT TOP(10)
		product_id as ENN_product_id,
		product_name as ENN_product_name, 
		Description as ENN_description,
		price as ENN_price,
		s.season as ENN_price,
		g.gender as ENN_gender,
		VECTOR_DISTANCE(&#039;cosine&#039;, @qv, embedding) AS distance 
	FROM dbo.products p
		inner join dbo.season s on p.season_id = s.season_id
		inner join dbo.gender g on p.gender_id = g.gender_id
		ORDER BY   distance;

--Approximate Search

	SELECT
		product_id as ANN_product_id,
		product_name as ANN_product_name, 
		Description as ANN_Description,
		price as ANN_price,
		s.season as ANN_season,
		g.gender as ANN_gender
		FROM
			VECTOR_SEARCH(
				TABLE = &#x5B;dbo].&#x5B;products] AS p, 
				COLUMN = &#x5B;embedding], 
				SIMILAR_TO = @qv, 
				METRIC = &#039;cosine&#039;, 
				TOP_N = 10
			) AS vs
		inner join dbo.season s on p.season_id = s.season_id
		inner join dbo.gender g on p.gender_id = g.gender_id
	ORDER BY vs.distance

</pre></div>

<p>As you can see, we are getting a fairly accurate ANN result. This corresponds to a recall of 0.8, since only the products with IDs 1292 and 2167 are missing from the exact search result set:</p>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="616" height="346" src="http://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/4-3.png" alt="" class="wp-image-40154" style="width:739px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/4-3.png 616w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/4-3-300x169.png 300w" sizes="auto, (max-width: 616px) 100vw, 616px" /></figure>



<p class="wp-block-paragraph">Of course, in this example we only have 3’000 products in our database, so the savings in resource consumption when using ANN are not very significant. However, when processing much larger datasets, using ANN becomes highly relevant.</p>



<h2 class="wp-block-heading" id="h-limitations-with-vector-indexes-in-sql-server-2025-preview">Limitations with Vector indexes – in SQL-Server 2025 Preview</h2>


<p>Interesting to know is that tables with a vector index are read only in the preview edition so you can’t manipulate data at the moment:</p>

<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
	update dbo.products
	set price = price * 0.8
	where product_id = 13
</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="576" height="259" src="http://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/5-2.png" alt="" class="wp-image-40155" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/5-2.png 576w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/5-2-300x135.png 300w" sizes="auto, (max-width: 576px) 100vw, 576px" /></figure>


<p>In addition to support a vector index, the table must have a clustered index defined on a single column that is a primary key with the integer data type.  In the preview edition, vector indexes do also not support partitioning and are not replicated to subscribers when using replication. According to Microsoft, these limitations apply to the current preview edition of SQL Server 2025. I’m excited to see if these limitations will be removed in the GA release.</p>
<p>Let me know your thoughts in the comment section below and thanks for reading!</p>
<p>Hocine</p>


<p class="wp-block-paragraph"></p>
<p>L’article <a href="https://www.dbi-services.com/blog/sql-server-2025-vector-indexes-semantic-search-performance/">SQL-Server 2025: Vector Indexes &amp; Semantic Search Performance</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/sql-server-2025-vector-indexes-semantic-search-performance/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SQL-Server 2025: Generate Data Embeddings for Semantic Search</title>
		<link>https://www.dbi-services.com/blog/sql-server-2025-generate-data-embeddings-for-semantic-search/</link>
					<comments>https://www.dbi-services.com/blog/sql-server-2025-generate-data-embeddings-for-semantic-search/#respond</comments>
		
		<dc:creator><![CDATA[Hocine Mechara]]></dc:creator>
		<pubDate>Tue, 12 Aug 2025 16:36:43 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[data]]></category>
		<category><![CDATA[data driven]]></category>
		<category><![CDATA[Data platform]]></category>
		<category><![CDATA[embeddings]]></category>
		<category><![CDATA[vectors]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=40060</guid>

					<description><![CDATA[<p>In this Blog we will continue with our AI journey in SQL-Server 2025 preview. If you didn’t read the first Blog you’ll find it here: dbi Blog  In this article, we will explore the concept of data embeddings, a key concept for modern AI-driven applications and platforms. We will then examine the latest capabilities introduced [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/sql-server-2025-generate-data-embeddings-for-semantic-search/">SQL-Server 2025: Generate Data Embeddings for Semantic Search</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p>In this Blog we will continue with our AI journey in SQL-Server 2025 preview. If you didn’t read the first Blog you’ll find it here: <a href="https://www.dbi-services.com/blog/lift-your-application-in-the-age-of-ai-with-sql-server-2025/">dbi Blog</a> </p>
<p>In this article, we will explore the concept of data embeddings, a key concept for modern AI-driven applications and platforms. We will then examine the latest capabilities introduced in SQL Server 2025 (preview) and Azure SQL, focusing on their new support for vector data types. Finally, we will generate embeddings using an AI model and store them directly in our database, demonstrating how to integrate AI-powered semantic abstraction into your SQL workloads.</p>


<h2 class="wp-block-heading" id="h-but-first-what-exactly-are-data-embeddings">But First: What Exactly Are Data Embeddings?</h2>


<p>Let’s start from the back. From a linguistic perspective, written text consists of syntax and semantics. While syntax refers to the structure of the text and the arrangement of words and letters, semantics refers to the actual meaning conveyed by the text. You can have text with a similar syntax but a different semantic and you can also have text with a similar or the same semantic but different syntax. For example:</p>


<ul class="wp-block-list">
<li>Sentence 1: Our customer satisfaction has increased.</li>



<li>Sentence 2: The contentment of our clients is better than before.</li>
</ul>



<p class="wp-block-paragraph">While both sentences have basically the same meaning, they differ in structure and the way the words and letters are arranged.</p>



<p class="wp-block-paragraph">In IT, we have different simple ways to search for syntax matches, such as exact string matching, wildcard searches or regular expressions (Regex) for more sophisticated syntax matches.</p>



<p class="wp-block-paragraph">But when we are looking for semantic matches, simple programming functions that only evaluate and compare the arrangement of letters are not enough. In such cases, we need a human, or another form of intelligence, that can interpret those arranged letters and understand their meaning.</p>



<p class="wp-block-paragraph">That’s exactly where AI comes into play. The AI model basically reads and understands the written text, then it generates a numerical representation for the semantic of that text which we call “<strong>embedding</strong>”.</p>



<p class="wp-block-paragraph">When I first heard about embeddings, I asked myself: <em>Okay, but what is the data embedded in?</em> </p>



<p class="wp-block-paragraph">The answer is that the data is embedded in a high-dimensional mathematical space, with the vector data representing its coordinates within that space. Since the semantics of embedded data are represented as coordinates in a mathematical space, we can calculate the distance between embeddings and use that to search for data with similar meaning.</p>



<p class="wp-block-paragraph">If you want to learn more about the theory of vectors and embeddings, I can highly recommend the <a href="https://www.dbi-services.com/blog/future-data-driven-2024-introduction-to-vector-databases/">article </a>by my colleague <a href="https://www.dbi-services.com/blog/author/aminehaloui/">Amine Haloui </a>– he explains the basic mathematical concepts in an understandable way, even if you’re not a mathematician.</p>



<h2 class="wp-block-heading" id="h-enough-theory-for-now-let-s-get-practical">Enough Theory for Now – Let’s Get Practical!</h2>



<p class="wp-block-paragraph">Lets take a look how we can generate data embeddings with the latest functions and features of SQL-Server 2025 and Azure SQL directly within our database through T-SQL.</p>



<p class="wp-block-paragraph">For that I’m using the same sample database which we already used in the previous blog post. But in the meantime I created a new column in my “<strong>dbo.products</strong>” table and I inserted in that column the AI generated product description for each product. If you are wondering how I did that – you can read it <a href="https://www.dbi-services.com/blog/lift-your-application-in-the-age-of-ai-with-sql-server-2025/">here</a>.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="498" height="650" src="http://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/1-1.png" alt="" class="wp-image-40061" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/1-1.png 498w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/1-1-230x300.png 230w" sizes="auto, (max-width: 498px) 100vw, 498px" /></figure>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
select p.product_id, p.product_name, p.Description, p.currency, p.price, c.category_name, b.brand_name, co.color_name, g.gender, s.season from dbo.products p
inner join dbo.category c on p.category_id = c.category_id
inner join dbo.brand b on p.brand_id = b.brand_id
inner join dbo.color co on p.color_id = co.color_id
inner join dbo.gender g on p.gender_id = g.gender_id
inner join dbo.season s on p.season_id = s.season_id

</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1044" height="583" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/2-1.png" alt="" class="wp-image-40062" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/2-1.png 1044w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/2-1-300x168.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/2-1-1024x572.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/2-1-768x429.png 768w" sizes="auto, (max-width: 1044px) 100vw, 1044px" /></figure>



<h3 class="wp-block-heading" id="h-deploy-an-embedding-model">Deploy an embedding Model:</h3>



<p class="wp-block-paragraph">For generating embeddings for our product data we need first an AI model which is capable to do that. Therefore I go into my Azure OpenAI Resource and I’m searching in the Model catalog in the Azure Foundry Portal for embedding models (I show you exactly who I can go there and deploy resources in the previous <a href="https://www.dbi-services.com/blog/lift-your-application-in-the-age-of-ai-with-sql-server-2025/">blog</a>). There are currently three embedding models available from OpenAI. I’m choosing for this blog post the model 3-small and I’m deploying it with a click on “<strong>text-embedding-3-small</strong>” <strong>-&gt;</strong> “<strong>use this model</strong>”<strong>-&gt;</strong>”<strong>Deploy</strong>” :</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1040" height="499" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/3-1.png" alt="" class="wp-image-40063" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/3-1.png 1040w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/3-1-300x144.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/3-1-1024x491.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/3-1-768x368.png 768w" sizes="auto, (max-width: 1040px) 100vw, 1040px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1039" height="205" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/4-1.png" alt="" class="wp-image-40064" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/4-1.png 1039w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/4-1-300x59.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/4-1-1024x202.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/4-1-768x152.png 768w" sizes="auto, (max-width: 1039px) 100vw, 1039px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="624" height="602" src="http://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/5-1.png" alt="" class="wp-image-40065" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/5-1.png 624w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/5-1-300x289.png 300w" sizes="auto, (max-width: 624px) 100vw, 624px" /></figure>



<p class="wp-block-paragraph">Then I’ll find the model in the Azure AI Foundry Portal under the section “<strong>Deployments</strong>”:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1039" height="437" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/6-1.png" alt="" class="wp-image-40066" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/6-1.png 1039w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/6-1-300x126.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/6-1-1024x431.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/6-1-768x323.png 768w" sizes="auto, (max-width: 1039px) 100vw, 1039px" /></figure>



<p class="wp-block-paragraph">When clicking on the model I’m able to see my models API endpoint and the access key:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1039" height="532" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/7-1.png" alt="" class="wp-image-40067" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/7-1.png 1039w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/7-1-300x154.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/7-1-1024x524.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/7-1-768x393.png 768w" sizes="auto, (max-width: 1039px) 100vw, 1039px" /></figure>



<h3 class="wp-block-heading" id="h-create-a-database-scoped-credential">Create a Database Scoped Credential:</h3>



<p class="wp-block-paragraph">Okay so far so good, let’s connect to our SQL-Server 2025 Instance where the ShopAI database is running on. To register the external model in our database and access it through the appropriate T-SQL functions, I first create a database-scoped credential. You can create the credential with a managed identity or with an API Key. I’m using the API Key which I copied from the Azure AI Foundry Portal. Note that the credential name must point to a path which is more generic than the request URL:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
-- Create database scoped credential:
CREATE DATABASE SCOPED CREDENTIAL &#x5B;https://shopai-blog.openai.azure.com/]
	WITH IDENTITY = &#039;HTTPEndpointHeaders&#039;, secret = &#039;{&quot;api-key&quot;:&quot;YOUR-API-KEY&quot;}&#039;;
GO
</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1039" height="96" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/8-1.png" alt="" class="wp-image-40068" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/8-1.png 1039w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/8-1-300x28.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/8-1-1024x95.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/8-1-768x71.png 768w" sizes="auto, (max-width: 1039px) 100vw, 1039px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1039" height="212" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/9-1.png" alt="" class="wp-image-40069" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/9-1.png 1039w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/9-1-300x61.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/9-1-1024x209.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/9-1-768x157.png 768w" sizes="auto, (max-width: 1039px) 100vw, 1039px" /></figure>



<h3 class="wp-block-heading" id="h-register-the-external-ai-model">Register the External AI Model:</h3>



<p class="wp-block-paragraph">As far as the database scoped credential is created we can register the external model with the “<strong>Create External Model</strong>” command, which is currently available in SQL-Server 2025 Preview, in our database. You can find more information’s about this brand new command under the following link from Microsoft: <a href="https://learn.microsoft.com/en-us/sql/t-sql/statements/create-external-model-transact-sql?view=sql-server-ver17">https://learn.microsoft.com/en-us/sql/t-sql/statements/create-external-model-transact-sql?view=sql-server-ver17</a></p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
-- Create EXTERNAL MODEL
CREATE EXTERNAL MODEL OpenAITextEmbedding3Small
AUTHORIZATION dbo
WITH (
      LOCATION = &#039;YOUR-ENDPOINT_URL&#039;,
      API_FORMAT = &#039;Azure OpenAI&#039;,
      MODEL_TYPE = EMBEDDINGS,
      MODEL = &#039;text-embedding-3-small&#039;,
      CREDENTIAL = &#x5B;YOUR-CREDENTIAL],
      PARAMETERS = &#039;{&quot;Dimensions&quot;:1536}&#039;
);
</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1038" height="218" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/10-1.png" alt="" class="wp-image-40070" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/10-1.png 1038w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/10-1-300x63.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/10-1-1024x215.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/10-1-768x161.png 768w" sizes="auto, (max-width: 1038px) 100vw, 1038px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1036" height="224" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/11-1.png" alt="" class="wp-image-40071" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/11-1.png 1036w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/11-1-300x65.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/11-1-1024x221.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/11-1-768x166.png 768w" sizes="auto, (max-width: 1036px) 100vw, 1036px" /></figure>



<h3 class="wp-block-heading" id="h-test-the-embedding-function">Test the Embedding Function</h3>


<p>As far as we created the external model successfully we can generate the embeddings with the “<strong>AI_Generate_Embeddings</strong>” function. This function is currently available in SQL-Server 2025 Preview.</p>
<p>The function requires the text to be embedded and the name of a pre-created external model as arguments, with optional model-specific parameters that can be appended in JSON format. You can get more information’s about this brand new function under the following link from Microsoft: <a href="https://learn.microsoft.com/en-us/sql/t-sql/functions/ai-generate-embeddings-transact-sql?view=sql-server-ver17">https://learn.microsoft.com/en-us/sql/t-sql/functions/ai-generate-embeddings-transact-sql?view=sql-server-ver17</a></p>


<p class="wp-block-paragraph">Let’s test the registered model and the embedding function with the command below. As you can see, the result is a JSON array containing multiple numerical values:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1037" height="154" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/12-1.png" alt="" class="wp-image-40072" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/12-1.png 1037w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/12-1-300x45.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/12-1-1024x152.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/12-1-768x114.png 768w" sizes="auto, (max-width: 1037px) 100vw, 1037px" /></figure>



<h3 class="wp-block-heading" id="h-generate-and-store-product-embeddings">Generate and Store Product Embeddings:</h3>



<p class="wp-block-paragraph">Having deployed the embedding model in Azure, registered it in our database, and successfully tested the embedding function, our next step is to generate embeddings for our products.</p>



<p class="wp-block-paragraph">Therefore I add an additional column called “<strong>embedding</strong>” to our “<strong>dbo.products</strong>” table. For the column I’m using the vector data type which is currently available in Azure SQL and in SQL-Server 2025 preview. Note the number in the brackets of the datatype definition is representing the amount of dimensions which are used for the embedding. Who many dimensions you need is depending on the model you are using. The “<strong>text-embedding3-small</strong>” model from OpenAI is using 1536 dimensions per default:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
ALTER TABLE dbo.products
ADD embedding vector(1536);
</pre></div>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="372" height="115" src="http://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/13-1.png" alt="" class="wp-image-40073" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/13-1.png 372w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/13-1-300x93.png 300w" sizes="auto, (max-width: 372px) 100vw, 372px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1037" height="357" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/14-1.png" alt="" class="wp-image-40074" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/14-1.png 1037w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/14-1-300x103.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/14-1-1024x353.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/14-1-768x264.png 768w" sizes="auto, (max-width: 1037px) 100vw, 1037px" /></figure>



<p class="wp-block-paragraph">I now want to generate embeddings for all products in my ShopAI database and store them in the newly created column, taking the following product attributes into account when generating the embeddings:</p>



<ul class="wp-block-list">
<li>Product Name</li>



<li>Product Description</li>



<li>Product Price</li>



<li>Product Season</li>



<li>Product Gender</li>
</ul>



<p class="wp-block-paragraph">To accomplish this, I use the code below to generate the embeddings:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
UPDATE p
SET p.embedding = AI_GENERATE_EMBEDDINGS(
    CONCAT(
        N&#039;Product Name: &#039;,            COALESCE(p.product_name, N&#039;Unknown&#039;),
        N&#039; | Product Description: &#039;,  COALESCE(p.&#x5B;description], N&#039;Unknown&#039;),
        N&#039; | Product Price (CHF): &#039;,  COALESCE(CONVERT(NVARCHAR(32), p.price), N&#039;Unknown&#039;),
        N&#039; | Product Season: &#039;,
        CASE p.season_id
            WHEN 1 THEN N&#039;Spring&#039;
            WHEN 2 THEN N&#039;Summer&#039;
            WHEN 3 THEN N&#039;Autumn&#039;
            WHEN 4 THEN N&#039;Winter&#039;
            WHEN 5 THEN N&#039;All Seasons&#039;
            ELSE N&#039;Unknown&#039;
        END,
        N&#039; | Product Gender: &#039;,
        CASE p.gender_id
            WHEN 1 THEN N&#039;Male&#039;
            WHEN 2 THEN N&#039;Female&#039;
            WHEN 3 THEN N&#039;Unisex&#039;
            ELSE N&#039;Unknown&#039;
        END
    ) USE MODEL OpenAITextEmbedding3Small
)
FROM dbo.products AS p
WHERE p.embedding IS NULL;

</pre></div>


<p class="wp-block-paragraph">The query ran for over four minutes and successfully generated embeddings for all products:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1035" height="752" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/15-1.png" alt="" class="wp-image-40075" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/15-1.png 1035w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/15-1-300x218.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/15-1-1024x744.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/15-1-768x558.png 768w" sizes="auto, (max-width: 1035px) 100vw, 1035px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="975" height="228" src="http://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/16-1.png" alt="" class="wp-image-40076" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/16-1.png 975w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/16-1-300x70.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/16-1-768x180.png 768w" sizes="auto, (max-width: 975px) 100vw, 975px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1035" height="494" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/17-1.png" alt="" class="wp-image-40077" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/17-1.png 1035w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/17-1-300x143.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/17-1-1024x489.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/17-1-768x367.png 768w" sizes="auto, (max-width: 1035px) 100vw, 1035px" /></figure>



<h3 class="wp-block-heading" id="h-test-semantic-similarity-functions">Test Semantic Similarity Functions:</h3>



<p class="wp-block-paragraph">So far so good, we were able to deploy an embedding model in Azure, we successfully created a credential for the access to the model, we registered the embedding model with the latest T-SQL command and we were able to generate embeddings for our products.</p>



<p class="wp-block-paragraph">Let’s see how we can use now the embedding model and the embedded data for semantic search capabilities.</p>



<p class="wp-block-paragraph">Therefore I’m using the “<strong>Vector_Distance</strong>” function which is available in SQL-Server 2025 preview and in Azure SQL. The function requires 3 arguments: The first is the metric you want to use for calculating the distance between two embeddings, and the other two are the vectors you want to compare.You can get more information’s about this brand new function under the following link from Microsoft: <a href="https://learn.microsoft.com/en-us/sql/t-sql/functions/vector-distance-transact-sql?view=sql-server-ver17">https://learn.microsoft.com/en-us/sql/t-sql/functions/vector-distance-transact-sql?view=sql-server-ver17</a></p>



<p class="wp-block-paragraph">Okay, let’s see how we can use this function &#8211; It’s summer and I would like to go on summer vacation (again). Therefore I’m looking for some pants which I can wear during my summer holidays. I don’t like anything flashy, so I’d like the pants in black.</p>



<p class="wp-block-paragraph">Let’s test with the code below if we can find a matching product for me:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
DECLARE @SemanticSearchText Nvarchar(max) = &#039;Im looking for black pants for men which I can wear during my summer vacation.&#039;

	
	DECLARE @qv VECTOR(1536) = AI_GENERATE_EMBEDDINGS(@SemanticSearchText USE MODEL OpenAITextEmbedding3Small);

	SELECT TOP(10) 
	  product_name, 
	  Description,
	  price,
	  s.season,
	  g.gender,
	  VECTOR_DISTANCE(&#039;cosine&#039;, @qv, embedding) AS distance 
	FROM dbo.products p
	  inner join dbo.season s on p.season_id = s.season_id
	  inner join dbo.gender g on p.gender_id = g.gender_id
ORDER BY   distance;
</pre></div>


<p class="wp-block-paragraph">As you can see, the results are quite accurate. All of the products are pants, except for the one with the highest distance, which is a full swimsuit. Every product is black in color, and most are categorized for the summer season, except for two that are suited for all seasons:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="894" height="562" src="http://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/18-2.png" alt="" class="wp-image-40078" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/18-2.png 894w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/18-2-300x189.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/18-2-768x483.png 768w" sizes="auto, (max-width: 894px) 100vw, 894px" /></figure>



<p class="wp-block-paragraph">As you can see by combining SQL Server 2025’s new vector capabilities with external embedding models, we can seamlessly integrate semantic understanding directly into our data workflows. This enables intelligent, context-aware queries that go far beyond simple keyword matching.</p>



<p class="wp-block-paragraph">You can find all the code again on <a href="https://github.com/HocineMechara/SQL2025-AI-Product-Embeddings-Blog-/tree/main">GitHub </a>and test it by your own. Feel free to share your thoughts with me in the comment section and stay tuned for upcoming posts from my colleagues and me. 😉</p>



<p class="wp-block-paragraph">Thanks for reading,<br>Hocine</p>



<p class="wp-block-paragraph"></p>
<p>L’article <a href="https://www.dbi-services.com/blog/sql-server-2025-generate-data-embeddings-for-semantic-search/">SQL-Server 2025: Generate Data Embeddings for Semantic Search</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/sql-server-2025-generate-data-embeddings-for-semantic-search/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SQL Server 2025: Where Data Meets Intelligence</title>
		<link>https://www.dbi-services.com/blog/lift-your-application-in-the-age-of-ai-with-sql-server-2025/</link>
					<comments>https://www.dbi-services.com/blog/lift-your-application-in-the-age-of-ai-with-sql-server-2025/#comments</comments>
		
		<dc:creator><![CDATA[Hocine Mechara]]></dc:creator>
		<pubDate>Tue, 05 Aug 2025 15:23:04 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Development & Performance]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[ai]]></category>
		<category><![CDATA[data]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=39981</guid>

					<description><![CDATA[<p>The evolution of AI is booming and brings many new possibilities to enhance efficiency and convenience with many different use cases. While AI chatbots have had an almost revolutionary influence in recent years, we are now entering the next phase of this evolution with context-aware AI assistants and autonomous AI agents that will have even [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/lift-your-application-in-the-age-of-ai-with-sql-server-2025/">SQL Server 2025: Where Data Meets Intelligence</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">The evolution of AI is booming and brings many new possibilities to enhance efficiency and convenience with many different use cases. While AI chatbots have had an almost revolutionary influence in recent years, we are now entering the next phase of this evolution with context-aware AI assistants and autonomous AI agents that will have even more impact.</p>



<p class="wp-block-paragraph">Providers of modern and future-oriented platforms and applications seem to be aware of this development and are integrating AI in a targeted manner to increase efficiency and improve the user experience.</p>



<p class="wp-block-paragraph">With SQL Server 2025 being the most developer-oriented release in over a decade, Microsoft is clearly targeting the AI evolution making it easier than ever to integrate AI with your data.</p>



<p class="wp-block-paragraph">In this article I will introduce you in two of the latest AI oriented features which are now available in SQL-Server 2025 preview and I will show you an example how you can interact with an external AI-model programmatically.</p>



<h1 class="wp-block-heading" id="h-sp-invoke-external-rest-endpoint">sp_invoke_external_rest_endpoint:</h1>



<p class="wp-block-paragraph">While the stored procedure sp_invoke_external_rest_endpoint was already available in Azure SQL Database and Managed Instance it’s now also available in &nbsp;SQL-Server 2025 Preview and allows you to communicate with external REST services from On-Premise.</p>



<p class="wp-block-paragraph">This is significant in terms of AI because AI-models which are hosted on Cloud Platforms or on Premise (for example with Ollama) are usually REST compatible. This stored procedure allows you so to interact with an external AI model directly and programmatically through T-SQL.</p>



<p class="wp-block-paragraph">You can find more information about the stored procedure and the specific parameters on the following Link from Microsoft: <a href="https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-invoke-external-rest-endpoint-transact-sql?view=sql-server-ver17&amp;tabs=request-headers">https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-invoke-external-rest-endpoint-transact-sql?view=sql-server-ver17&amp;tabs=request-headers</a></p>



<h1 class="wp-block-heading" id="h-json-datatype">JSON Datatype:</h1>



<p class="wp-block-paragraph">In the previous version of SQL-Server JSON Data was stored in the varchar and nvarchar datatypes. With SQL-Server 2025 you can now store JSON data in a JSON datatype. The JSON datatype which is also available in Azure SQL Database and Azure SQL Managed Instance brings multiple advantages over working with JSON Data in nvarchar and varchar datatypes.</p>



<p class="wp-block-paragraph">As the new JSON Datatype is compatible with all JSON functions no code change is necessary while benefiting from the following advantages:</p>



<ul class="wp-block-list">
<li>reads are faster because with the JSON datatype your JSON data is already parsed</li>



<li>you can update also individual values without accessing the whole JSON data</li>



<li>the JSON datatype is optimized for compression which makes the storage more efficient</li>
</ul>



<p class="wp-block-paragraph">In terms of AI this is significant because external AI-models are usually transferring data in the JSON format. With the new JSON datatype you can handle this data efficiently.</p>



<p class="wp-block-paragraph">To get more detailed information’s about handling JSON data with T-SQL and about the new JSON datatype, you can access the following Link from Microsoft: <a href="https://learn.microsoft.com/en-us/sql/relational-databases/json/json-data-sql-server?view=sql-server-ver17">https://learn.microsoft.com/en-us/sql/relational-databases/json/json-data-sql-server?view=sql-server-ver17</a></p>



<h1 class="wp-block-heading" id="h-hands-on-generating-product-descriptions-using-ai">Hands On &#8211; Generating Product Descriptions using AI:</h1>



<p class="wp-block-paragraph">Enough theory for now! let’s get practical. For demonstration purposes, I’ve prepared a sample shop database for a clothing reseller running on SQL-Server 2025.</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="757" height="495" src="http://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/1.png" alt="" class="wp-image-40024" style="width:446px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/1.png 757w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/1-300x196.png 300w" sizes="auto, (max-width: 757px) 100vw, 757px" /></figure>



<p class="wp-block-paragraph">The database has some few tables which are containing product information’s:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="479" height="456" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/18-1.png" alt="" class="wp-image-40053" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/18-1.png 479w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/18-1-300x286.png 300w" sizes="auto, (max-width: 479px) 100vw, 479px" /></figure>



<p class="wp-block-paragraph">With the following query you can see that we have 3000 products with different categories from different brands etc.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
select p.product_id, p.product_name, p.currency, p.price, c.category_name, b.brand_name, co.color_name, g.gender, s.season 
from dbo.products p 
inner join dbo.category c on p.category_id = c.category_id 
inner join dbo.brand b on p.brand_id = b.brand_id 
inner join dbo.color co on p.color_id = co.color_id 
inner join dbo.gender g on p.gender_id = g.gender_id
inner join dbo.season s on p.season_id = s.season_id
</pre></div>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="827" height="633" src="http://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/3.png" alt="" class="wp-image-40028" style="width:552px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/3.png 827w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/3-300x230.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/3-768x588.png 768w" sizes="auto, (max-width: 827px) 100vw, 827px" /></figure>



<p class="wp-block-paragraph">What we don’t have is a product description for attracting potential buyers. This is something we will do now using AI. Therefore I need an AI model which I can integrate with my product data. I deploy this model in Azure.</p>



<h2 class="wp-block-heading" id="h-deploy-an-ai-model-in-azure">Deploy an AI Model in Azure:</h2>



<p class="wp-block-paragraph">In the Azure Portal I’m looking therefore for Azure AI Foundry.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="457" height="517" src="http://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/4.png" alt="" class="wp-image-40031" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/4.png 457w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/4-265x300.png 265w" sizes="auto, (max-width: 457px) 100vw, 457px" /></figure>



<p class="wp-block-paragraph">Then under AI Foundry I create a new resource of Azure OpenAI:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="917" height="343" src="http://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/5.png" alt="" class="wp-image-40032" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/5.png 917w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/5-300x112.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/5-768x287.png 768w" sizes="auto, (max-width: 917px) 100vw, 917px" /></figure>



<p class="wp-block-paragraph">The deployment is pretty much straight forward and after the deployment is finished I access my newly created resource:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="920" height="337" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/6.png" alt="" class="wp-image-40033" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/6.png 920w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/6-300x110.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/6-768x281.png 768w" sizes="auto, (max-width: 920px) 100vw, 920px" /></figure>



<p class="wp-block-paragraph">In my new Azure OpenAI resource I’m accessing now the Azure AI Foundry portal for deploying a new AI-model:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1148" height="352" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/7.png" alt="" class="wp-image-40034" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/7.png 1148w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/7-300x92.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/7-1024x314.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/7-768x235.png 768w" sizes="auto, (max-width: 1148px) 100vw, 1148px" /></figure>



<p class="wp-block-paragraph">In the AI Foundry Portal I’m looking under the Model catalog for chat completion models and I select the gpt-4o model which is the “allrounder” AI-model for chat completion tasks from OpenAI(It’s in fact currently the model you are using with ChatGPT by default if you don’t choose anything else specifically):</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1271" height="332" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/8.png" alt="" class="wp-image-40035" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/8.png 1271w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/8-300x78.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/8-1024x267.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/8-768x201.png 768w" sizes="auto, (max-width: 1271px) 100vw, 1271px" /></figure>



<p class="wp-block-paragraph">After clicking on the gpt-4o model, I clik on “Use this model” and finally on “deploy” for deploying the model:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1146" height="356" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/9.png" alt="" class="wp-image-40036" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/9.png 1146w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/9-300x93.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/9-1024x318.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/9-768x239.png 768w" sizes="auto, (max-width: 1146px) 100vw, 1146px" /></figure>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="721" height="702" src="http://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/10.png" alt="" class="wp-image-40037" style="width:402px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/10.png 721w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/10-300x292.png 300w" sizes="auto, (max-width: 721px) 100vw, 721px" /></figure>



<p class="wp-block-paragraph">As far as I deployed the model successfully I’m able to find it in the Azure AI Foundry Portal under “Deployments”:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1070" height="371" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/11.png" alt="" class="wp-image-40038" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/11.png 1070w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/11-300x104.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/11-1024x355.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/11-768x266.png 768w" sizes="auto, (max-width: 1070px) 100vw, 1070px" /></figure>



<p class="wp-block-paragraph">After clicking on the newly deployed model I can also find my https URL and API key for accessing the model:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1068" height="557" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/12.png" alt="" class="wp-image-40039" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/12.png 1068w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/12-300x156.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/12-1024x534.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/12-768x401.png 768w" sizes="auto, (max-width: 1068px) 100vw, 1068px" /></figure>



<h2 class="wp-block-heading" id="h-interacting-with-the-ai-model-using-t-sql">Interacting with the AI Model using T-SQL:</h2>



<p class="wp-block-paragraph">As the model is now successfully deployed we are ready to interact with it using T-SQL. Therefore I wrote a small stored procedure around the stored procedure sp_invoke_external_rest_endpoint which is doing the following stuff:</p>



<p class="wp-block-paragraph">It takes the Product ID, API Key, API Endpoint URL and the desired description language as input parameters:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="733" height="317" src="http://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/13.png" alt="" class="wp-image-40042" style="width:559px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/13.png 733w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/13-300x130.png 300w" sizes="auto, (max-width: 733px) 100vw, 733px" /></figure>



<p class="wp-block-paragraph">It selects necessary product data for the provided Product ID from the database and stores it as a single string:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1177" height="260" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/14.png" alt="" class="wp-image-40043" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/14.png 1177w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/14-300x66.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/14-1024x226.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/14-768x170.png 768w" sizes="auto, (max-width: 1177px) 100vw, 1177px" /></figure>



<p class="wp-block-paragraph">It generates a prompt for the chat completion API and dynamically interpolates the selected product data into that prompt:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1758" height="461" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/AI_prompt.png" alt="" class="wp-image-40015" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/AI_prompt.png 1758w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/AI_prompt-300x79.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/AI_prompt-1024x269.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/AI_prompt-768x201.png 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/AI_prompt-1536x403.png 1536w" sizes="auto, (max-width: 1758px) 100vw, 1758px" /></figure>



<p class="wp-block-paragraph">Finally it invokes the external AI model using the stored procedure sp_invoke_external_rest_endpoint with the generated prompt:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1075" height="431" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/15.png" alt="" class="wp-image-40044" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/15.png 1075w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/15-300x120.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/15-1024x411.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/15-768x308.png 768w" sizes="auto, (max-width: 1075px) 100vw, 1075px" /></figure>



<p class="wp-block-paragraph">With the code below I’m then executing the stored procedure above for a specific product in my database and I’m extracting the product description from the AI models JSON response:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1072" height="561" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/16.png" alt="" class="wp-image-40045" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/16.png 1072w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/16-300x157.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/16-1024x536.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/16-768x402.png 768w" sizes="auto, (max-width: 1072px) 100vw, 1072px" /></figure>



<p class="wp-block-paragraph">As a random example I have this swim shorts in the color magenta in my Shop database with the product ID 2354:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1072" height="310" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/17.png" alt="" class="wp-image-40046" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/17.png 1072w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/17-300x87.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/17-1024x296.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/17-768x222.png 768w" sizes="auto, (max-width: 1072px) 100vw, 1072px" /></figure>



<p class="wp-block-paragraph">I’m now able to generate a product description using the gpt-4o model hosted in Azure for this specific product:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1297" height="437" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/Image_AI_response_english.png" alt="" class="wp-image-40018" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/Image_AI_response_english.png 1297w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/Image_AI_response_english-300x101.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/Image_AI_response_english-1024x345.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/Image_AI_response_english-768x259.png 768w" sizes="auto, (max-width: 1297px) 100vw, 1297px" /></figure>



<p class="wp-block-paragraph">You can see here the full product description which was returned by the gpt-4o model:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><td>Make a bold splash this summer with these vibrant magenta swim shorts from River Island. Designed for the modern man, they combine style and comfort, ensuring you stand out at the beach or poolside. Don&#8217;t just swim—make a statement. Grab yours now and own the summer vibe!</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">The description is pretty good isn&#8217;t it? If magenta suited me, I would definitely buy one <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 class="wp-block-paragraph">The GPT-4o model is also quite a language expert. For example, you can even generate your product descriptions in Chinese.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="1298" height="576" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/Image_AI_response_chinese.png" alt="" class="wp-image-40020" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/Image_AI_response_chinese.png 1298w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/Image_AI_response_chinese-300x133.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/Image_AI_response_chinese-1024x454.png 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/08/Image_AI_response_chinese-768x341.png 768w" sizes="auto, (max-width: 1298px) 100vw, 1298px" /></figure>



<p class="wp-block-paragraph">In case that I have Chinese speaking blog readers or if someone wants to translate it, here is again the full description:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><tbody><tr><td>亮眼的紫红色泳裤，让您在夏日海滩上成为焦点！River Island精心设计，兼具时尚与舒适，完美贴合男性身形。无论是游泳还是晒日光浴，这款泳裤都是您的理想选择。立即下单，开启您的夏日魅力之旅！</td></tr></tbody></table></figure>



<p class="wp-block-paragraph">If you want to test it by your own you can find the whole code and a backup of the ShopAI database on GitHub: <a href="https://github.com/HocineMechara/SQL2025-AI-Product-Description-Blog">https://github.com/HocineMechara/SQL2025-AI-Product-Description-Blog</a></p>



<p class="wp-block-paragraph">Of course, this is just a simple example. You can do much more. For instance, you could provide additional context to the chat completion model, such as the user&#8217;s recent orders or behavioral data, to generate personalized product descriptions tailored to individual customers.</p>



<p class="wp-block-paragraph">Feel free to share your thoughts in the comments, and stay tuned for upcoming posts from my colleagues and me. <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 class="wp-block-paragraph">Thanks for reading,<br>Hocine</p>
<p>L’article <a href="https://www.dbi-services.com/blog/lift-your-application-in-the-age-of-ai-with-sql-server-2025/">SQL Server 2025: Where Data Meets Intelligence</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/lift-your-application-in-the-age-of-ai-with-sql-server-2025/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Exploring the Future of Data at SQLBits 2025 in London</title>
		<link>https://www.dbi-services.com/blog/exploring-the-future-of-data-at-sqlbits-2025-in-london/</link>
					<comments>https://www.dbi-services.com/blog/exploring-the-future-of-data-at-sqlbits-2025-in-london/#respond</comments>
		
		<dc:creator><![CDATA[Hocine Mechara]]></dc:creator>
		<pubDate>Sat, 21 Jun 2025 12:02:05 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Big Data]]></category>
		<category><![CDATA[Business Intelligence]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[Database Administration & Monitoring]]></category>
		<category><![CDATA[Database management]]></category>
		<category><![CDATA[SQL Server]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=39218</guid>

					<description><![CDATA[<p>I’m looking back on three amazing days of interesting technical insights from the SQLBits event 2025 in London. It’s actually my first time having the pleasure of attending one of Europe’s biggest data platform events and I can sincerely say, that I enjoyed every minute of interesting workshops and sessions all around Data management, Data [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/exploring-the-future-of-data-at-sqlbits-2025-in-london/">Exploring the Future of Data at SQLBits 2025 in London</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">I’m looking back on three amazing days of interesting technical insights from the SQLBits event 2025 in London. It’s actually my first time having the pleasure of attending one of Europe’s biggest data platform events and I can sincerely say, that I enjoyed every minute of interesting workshops and sessions all around Data management, Data engineering and analytics with the latest technologies from Microsoft.</p>



<p class="wp-block-paragraph">The sessions and workshops on the event mainly focused on the evolution of AI and a lot about Business Intelligence and Data Engineering with a big focus on Microsoft Fabric the unifying data platform from Microsoft. The choice of topics reflects the direction in which I believe the industry is heading, and I took the opportunity to gain valuable insights from renowned experts.</p>



<p class="wp-block-paragraph">On the first day, I attended a one-day workshop on the latest AI functionalities in SQL Server and Azure SQL, focusing on how these features support the integration of external AI models with your data, enabling the development of context- and data-aware AI assistants for your application&#8217;s users based on the RAG (Retrieval-Augmented Generation) pattern. It was very interesting, the workshop was delivered in a high quality and it was even my personal highlight of the event.</p>



<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="768" height="1024" data-id="39223" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-19-at-18.56.17-768x1024.jpeg" alt="" class="wp-image-39223" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-19-at-18.56.17-768x1024.jpeg 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-19-at-18.56.17-225x300.jpeg 225w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-19-at-18.56.17-1152x1536.jpeg 1152w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-19-at-18.56.17.jpeg 1536w" sizes="auto, (max-width: 768px) 100vw, 768px" /></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="768" height="1024" data-id="39224" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.27.16-768x1024.jpeg" alt="" class="wp-image-39224" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.27.16-768x1024.jpeg 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.27.16-225x300.jpeg 225w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.27.16-1152x1536.jpeg 1152w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.27.16.jpeg 1536w" sizes="auto, (max-width: 768px) 100vw, 768px" /></figure>
</figure>



<p class="wp-block-paragraph">On the second day I joined again a one-day workshop about Microsoft Fabric. I was very curious about Microsoft Fabric because I heard a lot of advantages about this end to end SaaS Data Platform and I was happy to learn more about it. The workshop covered a lot of topics regarding Microsoft Fabric and at the end of the day I had a good feeling of understanding the concept and the core elements about Microsoft Fabric.</p>



<p class="wp-block-paragraph">During the break I enjoyed a fancy coffee served at the Microsoft booth <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>



<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-2 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="768" height="1024" data-id="39226" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.32.16-1-768x1024.jpeg" alt="" class="wp-image-39226" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.32.16-1-768x1024.jpeg 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.32.16-1-225x300.jpeg 225w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.32.16-1-1152x1536.jpeg 1152w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.32.16-1.jpeg 1536w" sizes="auto, (max-width: 768px) 100vw, 768px" /></figure>



<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="768" height="1024" data-id="39227" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.32.161-768x1024.jpeg" alt="" class="wp-image-39227" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.32.161-768x1024.jpeg 768w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.32.161-225x300.jpeg 225w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.32.161-1152x1536.jpeg 1152w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.32.161.jpeg 1536w" sizes="auto, (max-width: 768px) 100vw, 768px" /></figure>
</figure>



<p class="wp-block-paragraph">On the last day I attended again a one-day workshop about Microsoft Fabric but with more focus on the administration of a Fabric Tenant including workspace management, security, Git Integration etc. The workshop was again delivered in a good quality and I really enjoyed to attend. The Kahoot Quiz at the end of the workshop confirmed my Impression that I’m on track of understanding this platform quite well.</p>



<figure class="wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-3 is-layout-flex wp-block-gallery-is-layout-flex">
<figure class="wp-block-image size-large"><img loading="lazy" decoding="async" width="1536" height="1152" data-id="39229" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.39.44-edited.jpeg" alt="" class="wp-image-39229" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.39.44-edited.jpeg 1536w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.39.44-edited-300x225.jpeg 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.39.44-edited-1024x768.jpeg 1024w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/06/WhatsApp-Image-2025-06-21-at-13.39.44-edited-768x576.jpeg 768w" sizes="auto, (max-width: 1536px) 100vw, 1536px" /></figure>
</figure>



<p class="wp-block-paragraph">The event was awesome, I learned a lot about the latest technologies and I hope that I will be able to apply the gained knowledge in upcoming projects. I can highly recommend this event to every data professional working with Microsoft technologies!</p>
<p>L’article <a href="https://www.dbi-services.com/blog/exploring-the-future-of-data-at-sqlbits-2025-in-london/">Exploring the Future of Data at SQLBits 2025 in London</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/exploring-the-future-of-data-at-sqlbits-2025-in-london/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Set SQL Server Trace Flags Automatically and Smoothly</title>
		<link>https://www.dbi-services.com/blog/set-sql-server-trace-flags-automatically-and-smoothly/</link>
					<comments>https://www.dbi-services.com/blog/set-sql-server-trace-flags-automatically-and-smoothly/#respond</comments>
		
		<dc:creator><![CDATA[Hocine Mechara]]></dc:creator>
		<pubDate>Tue, 29 Apr 2025 15:56:09 +0000</pubDate>
				<category><![CDATA[Database management]]></category>
		<category><![CDATA[Development & Performance]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Automation]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[databases]]></category>
		<category><![CDATA[PowerShell]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=38241</guid>

					<description><![CDATA[<p>Managing SQL Server trace flags effectively is a common task for DBAs and system engineers, especially when tuning the SQL Server behavior for specific use cases or performance optimizations. While trace flags can be enabled programmatically using T-SQL commands such as DBCC TRACEON, this approach has an inherent limitation: the flags are session-based or global [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/set-sql-server-trace-flags-automatically-and-smoothly/">Set SQL Server Trace Flags Automatically and Smoothly</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">Managing SQL Server trace flags effectively is a common task for DBAs and system engineers, especially when tuning the SQL Server behavior for specific use cases or performance optimizations. While trace flags can be enabled programmatically using T-SQL commands such as DBCC TRACEON, this approach has an inherent limitation: the flags are session-based or global only until the SQL Server instance is restarted. After a service restart, these trace flags are no longer active unless they are configured as startup parameters.</p>



<p class="wp-block-paragraph">To ensure persistence across restarts, trace flags need to be set as startup parameters. Traditionally, this can be done manually through two main avenues: using the SQL Server Configuration Manager or editing the Windows Registry directly. However, both of these approaches require manual interaction and are not ideal for automated deployments, large-scale environments or automated deployments.</p>



<p class="wp-block-paragraph">Unfortunately, SQL Server doesn’t provide a built-in command-line tool or T-SQL syntax to set trace flags as startup parameters programmatically. This gap leaves many DBAs and system engineers either scripting complex registry edits themselves or relying on manual configurations.</p>



<p class="wp-block-paragraph">That’s exactly the problem I set out to solve. I developed a PowerShell function that allows you to set SQL Server trace flags as startup parameters in a programmatical way and I’m using this function already at one of my customers to set Trace flags within an automated deployment process. This function not only configures the registry settings required for each SQL Server instance on a server, but it also offers optional functionality to restart the SQL Server service—ensuring that your changes take effect immediately. Even better, it includes logic to detect which trace flags are already in place and avoids redundant updates.</p>



<p class="wp-block-paragraph">In this blog post, I’ll walk you through how the function works, how to use it in your own environment, and how it can fit into your broader infrastructure automation strategy. Whether you manage a single SQL Server or dozens across a large environment, this solution aims to simplify your workflow and reduce the risk of human error.</p>



<h2 class="wp-block-heading" id="h-let-s-take-a-look-at-how-to-set-the-trace-flag-programmatically-with-the-powershell-function"><strong>Let’s take a look at how to set the Trace flag programmatically with the PowerShell Function:</strong></h2>



<p class="wp-block-paragraph">I have the function stored in a ps1 file within my visual studio project directory.</p>



<figure class="wp-block-image size-full is-resized is-style-default wp-duotone-unset-4"><img loading="lazy" decoding="async" width="226" height="221" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-39.png" alt="" class="wp-image-38244" style="width:421px;height:auto" /></figure>



<p class="wp-block-paragraph">To execute the function and pass the appropriate input parameters to the function, I create a second .ps1 file in the same directory.</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="229" height="220" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-38.png" alt="" class="wp-image-38243" style="width:424px;height:auto" /></figure>



<p class="wp-block-paragraph">In this file I add the following values in variables:</p>



<p class="wp-block-paragraph"><strong>$Trace flags</strong> &#8211; The trace flags I would like to set for the particular instances</p>



<p class="wp-block-paragraph"><strong>$Restart</strong> &#8211; This defines if the SQL-Server Service should be restarted after setting the trace flags (Y = restart, N = no restart)</p>



<p class="wp-block-paragraph"><strong>$ServerName</strong> &#8211; This defines the Server on which the trace flags should be set as a startup parameter for every instance running on it</p>



<p class="wp-block-paragraph"><strong>$Cred &#8211;</strong>This is the credential which is used to access the remote Server</p>



<p class="wp-block-paragraph">Then I import the function as a module and execute the function with the values stored in the variables as input parameters.</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="602" height="205" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-40.png" alt="" class="wp-image-38245" style="width:840px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-40.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-40-300x102.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph">After saving the file I execute it from the terminal. You can see that the Trace flags have been successfully set.</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="602" height="206" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-41.png" alt="" class="wp-image-38246" style="width:840px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-41.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-41-300x103.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph">When we take a look in the registry on the particular server we can see, that the Trace flags have been added.</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="602" height="177" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-42.png" alt="" class="wp-image-38247" style="width:840px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-42.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-42-300x88.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph">We can see the same result when taking a look on the startup parameters from the SQL Server Configuration Manager.</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="328" height="401" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-43.png" alt="" class="wp-image-38248" style="width:388px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-43.png 328w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-43-245x300.png 245w" sizes="auto, (max-width: 328px) 100vw, 328px" /></figure>



<p class="wp-block-paragraph">Let’s take a look from the SQL Server Management Studio. With the DBCC TRACESTATUS() function. You can see, that no trace flags are currently active.</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="602" height="218" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-44.png" alt="" class="wp-image-38249" style="width:840px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-44.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-44-300x109.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph">This is because the trace flags are set as startup parameters and will become active on the next service start. </p>



<p class="wp-block-paragraph">After restarting the SQL Server service, we can see that the trace flags are now active. </p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="496" height="307" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-45.png" alt="" class="wp-image-38250" style="width:484px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-45.png 496w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-45-300x186.png 300w" sizes="auto, (max-width: 496px) 100vw, 496px" /></figure>



<p class="wp-block-paragraph">In a running production environment, you can also enable the trace flags using the <code>DBCC TRACEON()</code> command with a global scope (<code>-1</code>) to avoid service interruption. This allows the trace flags to take effect immediately without restarting the SQL Server instance. Since the script also adds them as startup parameters in the registry, the trace flags will persist after the next restart.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
DBCC TRACEON (2371, -1);
GO
DBCC TRACEON (3226, -1);
GO
</pre></div>


<p class="wp-block-paragraph">The function is as well “intelligent” enough to see which trace flags are already in place and only sets the trace flags which are missing.</p>



<p class="wp-block-paragraph">When I execute the function again with the same trace flags, you can see, that the functions tells you that the trace flags are already in place.</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="602" height="230" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-46.png" alt="" class="wp-image-38251" style="width:840px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-46.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-46-300x115.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph">The function can also trigger a restart of the SQL-Server service to ensure that the trace flags become active immediately. Let consider therefore, that we want to set additionally the trace flag 1211. I change as well the $Restart variable to ‘Y’ to trigger a service restart.</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="602" height="212" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-47.png" alt="" class="wp-image-38252" style="width:840px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-47.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-47-300x106.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph">After saving the file and executing it again from the terminal you can see that the trace flag 1211 has been set and that the instance has been restarted.</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="602" height="158" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-48.png" alt="" class="wp-image-38253" style="width:840px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-48.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-48-300x79.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph">As the function triggered a restart of the instance, you can now see from the SQL Server Management Studio that the trace flag has become already active.</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="452" height="321" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-49.png" alt="" class="wp-image-38254" style="width:523px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-49.png 452w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/04/image-49-300x213.png 300w" sizes="auto, (max-width: 452px) 100vw, 452px" /></figure>



<p class="wp-block-paragraph">I hope this post was interesting for you <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;" /> Let me know your thoughts in the comment section below.</p>



<p class="wp-block-paragraph">I’ve uploaded the PowerShell function to GitHub. You can access it under this link: <a href="https://github.com/HocineMechara/SetSQLServerTraceFlags.git">https://github.com/HocineMechara/SetSQLServerTrace flags.git</a></p>
<p>L’article <a href="https://www.dbi-services.com/blog/set-sql-server-trace-flags-automatically-and-smoothly/">Set SQL Server Trace Flags Automatically and Smoothly</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/set-sql-server-trace-flags-automatically-and-smoothly/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Automate your Deployments in Azure with Terraform!</title>
		<link>https://www.dbi-services.com/blog/automate-your-deployments-in-azure-with-terraform/</link>
					<comments>https://www.dbi-services.com/blog/automate-your-deployments-in-azure-with-terraform/#respond</comments>
		
		<dc:creator><![CDATA[Hocine Mechara]]></dc:creator>
		<pubDate>Thu, 16 Jan 2025 13:43:38 +0000</pubDate>
				<category><![CDATA[Azure]]></category>
		<category><![CDATA[Cloud]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Terraform]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=36604</guid>

					<description><![CDATA[<p>Terraform is a strong open-source declarative and platform agnostic infrastructure as code (IaC) tool developed by HashiCorp. It facilitates the deployment and whole management of infrastructure. In this hands on blog I will show you how you can use Terraform to automate your cloud deployments in Azure. Initial Setup: For this blog I’m using a [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/automate-your-deployments-in-azure-with-terraform/">Automate your Deployments in Azure with Terraform!</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">Terraform is a strong open-source declarative and platform agnostic infrastructure as code (IaC) tool developed by HashiCorp. It facilitates the deployment and whole management of infrastructure. In this hands on blog I will show you how you can use Terraform to automate your cloud deployments in Azure.</p>



<h2 class="wp-block-heading" id="h-initial-setup">Initial Setup:</h2>


<p>For this blog I’m using a ubuntu server as a automation server where I’m running Terraform. You can install Terraform on different operating systems. For instructions how to install Terraform check out this <a href="https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli">link</a> from HashiCorp.</p>
<p>Starting with the hands on part I’m creating a new dedicated directory for my new Terraform project:</p>


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



<p class="wp-block-paragraph">Within this new directory I’m creating the following files which will hold my configuration code:</p>



<ul class="wp-block-list">
<li>main.tf</li>



<li>providers.tf</li>



<li>variables.tf</li>



<li>outputs.tf</li>
</ul>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="909" height="221" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-21.png" alt="" class="wp-image-36606" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-21.png 909w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-21-300x73.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-21-768x187.png 768w" sizes="auto, (max-width: 909px) 100vw, 909px" /></figure>



<h2 class="wp-block-heading" id="h-installing-the-azure-cli">Installing the Azure CLI:</h2>



<p class="wp-block-paragraph">For the authentication with Azure I’m using the Azure CLI command line tool. You can Install the Azure CLI on Ubuntu with one command which curls a script, provided by Microsoft, from the internet and executes it on your system:</p>



<p class="wp-block-paragraph"><strong><em>curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash</em></strong></p>



<p class="wp-block-paragraph">To get more information’s about how to install the Azure CLI on your system, checkout this <a href="https://learn.microsoft.com/en-us/cli/azure/install-azure-cli">link</a> from Microsoft.</p>



<p class="wp-block-paragraph">As far as the installation is successfully done, you can verify it with the following command:</p>



<p class="wp-block-paragraph"><strong><em>az &#8211;version</em></strong></p>



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



<p class="wp-block-paragraph">Then use the following command for connecting to Azure:</p>



<p class="wp-block-paragraph"><strong><em>az login</em></strong></p>



<p class="wp-block-paragraph">This command will open a browser window where you can sign in to Azure:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="95" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-23.png" alt="" class="wp-image-36608" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-23.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-23-300x30.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-23-768x78.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<p class="wp-block-paragraph">After you successfully authenticated yourself to Azure, you can check your available subscriptions with the following command:</p>



<p class="wp-block-paragraph"><strong><em>az account list</em></strong></p>



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



<h2 class="wp-block-heading" id="h-initialize-terraform">Initialize Terraform:</h2>



<p class="wp-block-paragraph">As the Azure CLI is now installed on the system and we are successfully authenticated to Azure, we can now start with the configuration of Terraform and the required provider for interacting with the Azure cloud platform.</p>



<p class="wp-block-paragraph">Therefore I add the code block below into the providers.tf file which will tell terraform to install and initialize the azurerm provider with the specific version 4.10.0, which is the latest at the moment:</p>



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



<p class="wp-block-paragraph">To configure the azurerm provider, I add the provider code block below additionally into the providers.tf file.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="816" height="234" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-26.png" alt="" class="wp-image-36611" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-26.png 816w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-26-300x86.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-26-768x220.png 768w" sizes="auto, (max-width: 816px) 100vw, 816px" /></figure>



<p class="wp-block-paragraph">You can find your subscription ID in the output from the “az account list” command above.</p>



<p class="wp-block-paragraph">After inserting those code blocks into the providers.tf file, we can install the defined azurerm provider and initialize Terraform by running the below command in our project directory:</p>



<p class="wp-block-paragraph"><strong><em>terraform init</em></strong></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="595" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-27.png" alt="" class="wp-image-36612" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-27.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-27-300x190.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-27-768x486.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<h2 class="wp-block-heading" id="h-terraform-workspaces">Terraform Workspaces:</h2>



<p class="wp-block-paragraph">As Terraform is now successfully initialized and the required provider is installed, we can start with the development of our infrastructure code.</p>



<p class="wp-block-paragraph">But before doing so I would like to target the concept of workspaces in Terraform. Workspaces enables you to use the same configuration code for multiple environments through separate state files. You can imagine workspaces as a separated deployment environment and you terraform code as a independent plan or image of your infrastructure. As an example, imagine you added a new virtual machine to your terraform code and deployed it in production. If you now want to have the same virtual machine for test purposes, you just have to switch into your test workspace and run the terraform code again. You will have the exact same virtual machine within a few minutes!</p>



<p class="wp-block-paragraph">To check the workspaces you have in your Terraform project, use the following command:</p>



<p class="wp-block-paragraph"><strong><em>terraform workspace list</em></strong></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="78" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-28.png" alt="" class="wp-image-36613" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-28.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-28-300x25.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-28-768x64.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<p class="wp-block-paragraph">As you can see we just have the default workspace in our new Terraform project. I want to deploy my infrastructure in this hands on blog post for multiple environments, therefore I will create some new workspaces. Lets assume we have a development, test and production stage for our infrastructure. I will create therefore the workspaces accordingly with the commands below:</p>



<p class="wp-block-paragraph"><strong><em>terraform workspace new development</em></strong></p>



<p class="wp-block-paragraph"><strong><em>terraform workspace new test</em></strong></p>



<p class="wp-block-paragraph"><strong><em>terraform workspace new production</em></strong></p>



<p class="wp-block-paragraph">After executing these commands, we can now check again our available workspaces in our terraform project:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="140" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-29.png" alt="" class="wp-image-36614" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-29.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-29-300x45.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-29-768x114.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<p class="wp-block-paragraph">Note that terraform will let you know your current workspace through the “*” symbol behind the particular workspace. We want to deploy our infrastructure for development first. So I will switch back into the development workspace with the following command:</p>



<p class="wp-block-paragraph"><strong><em>terraform workspace select development</em></strong></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="58" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-30.png" alt="" class="wp-image-36615" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-30.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-30-300x19.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-30-768x47.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<h2 class="wp-block-heading" id="h-create-an-azure-resource-group">Create an Azure Resource Group:</h2>



<p class="wp-block-paragraph">As the workspaces are now successfully created, we can start with our configuration code.</p>



<p class="wp-block-paragraph">First of all I go into the variables.tf file and add the variable code block below to that file:</p>



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



<p class="wp-block-paragraph">I will use this “env” variable for the suffix or prefix of resource names, which I will deploy, to simple recognize to which environment these resources belong.</p>



<p class="wp-block-paragraph">Next I will create a resource group in Azure. Therefore I add the code block below to the main.tf file:</p>



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



<p class="wp-block-paragraph">As you can see I set the name of the resource group dynamically with the prefix “RG_” and the value for the current workspace in the variable “env”, which I’ve defined before in the variables.tf file. The variable “terraform.workspace” is a default variable which refers to the current workspace.</p>



<p class="wp-block-paragraph">To check which resource terraform would create in case we would apply the current configuration code, we can run the following command:</p>



<p class="wp-block-paragraph"><strong><em>terraform plan</em></strong></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="259" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-33.png" alt="" class="wp-image-36619" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-33.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-33-300x83.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-33-768x212.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<p class="wp-block-paragraph">We can see that terraform would create a new resource group with the name “RG_DEV”.</p>



<h2 class="wp-block-heading" id="h-create-a-virtual-network-and-subnets">Create a Virtual Network and Subnets:</h2>



<p class="wp-block-paragraph">Next I will create a virtual network. Therefore I add the variable code block below to the variables.tf file. This variable defines for each environment stage a own address space:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="903" height="421" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-34.png" alt="" class="wp-image-36620" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-34.png 903w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-34-300x140.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-34-768x358.png 768w" sizes="auto, (max-width: 903px) 100vw, 903px" /></figure>



<p class="wp-block-paragraph">I add now the code block below to the main.tf file to create a virtual network:</p>



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



<p class="wp-block-paragraph">As you can see I’m referencing here as well to the “env” variable for dynamically setting the suffix of the network name and as well to the new “cidr” variable to set the address space of the virtual network.</p>



<p class="wp-block-paragraph">Next I will create some subnets within the virtual network. I want to create 4 subnets in total:</p>



<ul class="wp-block-list">
<li>A front tier subnet</li>



<li>A middle tier subnet</li>



<li>A backend tier subnet</li>



<li>A bastion subnet for the administration</li>
</ul>



<p class="wp-block-paragraph">Therefore I add the variable below to my variables.tf file, which defines for each environment stage and subnet an address space:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="876" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-36.png" alt="" class="wp-image-36623" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-36.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-36-300x280.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-36-768x716.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<p class="wp-block-paragraph">Next I will add for each subnet a new resource block to the main.tf file:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="204" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-37.png" alt="" class="wp-image-36624" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-37.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-37-300x65.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-37-768x167.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="242" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-38.png" alt="" class="wp-image-36625" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-38.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-38-300x77.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-38-768x198.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="262" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-39.png" alt="" class="wp-image-36626" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-39.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-39-300x84.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-39-768x214.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="226" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-40.png" alt="" class="wp-image-36627" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-40.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-40-300x72.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-40-768x185.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<p class="wp-block-paragraph">Note that I enabled in the backend tier subnet the option “private_endpoint_network_policies”. This is a option which enforces the network security groups to take effect on the private endpoints in the particular subnet. Checkout this <a href="https://learn.microsoft.com/en-us/azure/private-link/disable-private-endpoint-network-policy?tabs=network-policy-portal">link</a> from Microsoft for more information’s about this option.</p>



<h2 class="wp-block-heading" id="h-create-an-azure-sql-database">Create an Azure SQL Database:</h2>



<p class="wp-block-paragraph">Next I will create an Azure SQL Server. Therefore I add the variable below to my variables.tf file. This variable is supposed to hold the admin password of the Azure SQL Server. I set the sensitivity option for this variable which will prevent the password to be exposed in the terminal output or in the Terraform logs:</p>



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



<p class="wp-block-paragraph">I did also not set any value in the configuration files, instead I will set the variable value as a environment variable before applying the configuration.</p>



<p class="wp-block-paragraph">Next I add the code block below to my main.tf file:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="312" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-42.png" alt="" class="wp-image-36630" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-42.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-42-300x100.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-42-768x255.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<p class="wp-block-paragraph">As you can see I referenced the “sqlserver_password” variable to set the password for the “sqladmin” user. I also disabled the public network access to prevent database access over the public endpoint of the server. I will create instead a private endpoint later on.</p>



<p class="wp-block-paragraph">Next I will create the Azure SQL Database. Therefore I add the variable below to my variables.tf file:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="821" height="806" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-43.png" alt="" class="wp-image-36631" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-43.png 821w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-43-300x295.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-43-768x754.png 768w" sizes="auto, (max-width: 821px) 100vw, 821px" /></figure>



<p class="wp-block-paragraph">The thought behind this variable is, that we have different requirements for the different stages. The general purpose SKU is sufficient for the non-productive databases but for the productive one we want the business critical service tier. As well as we want to have 30 days of point in time recovery for our productive data while 7 days is sufficient for non-productive and we want to store our productive database backups on geo-zone redundant storage while zone redundant storage is sufficient for the non-productive databases.</p>



<p class="wp-block-paragraph">Then I add the resource block below into my main.tf file:</p>



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



<p class="wp-block-paragraph">As you can see I’m referencing to my “database_settings” variable to set the configuration options dynamically.</p>



<h2 class="wp-block-heading" id="h-create-a-dns-zone-and-a-private-endpoint">Create a DNS Zone and a Private Endpoint:</h2>



<p class="wp-block-paragraph">For name resolution I will next create a private DNS zone. For that I add the resource block below to my main.tf file:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="870" height="219" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-45.png" alt="" class="wp-image-36633" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-45.png 870w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-45-300x76.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-45-768x193.png 768w" sizes="auto, (max-width: 870px) 100vw, 870px" /></figure>



<p class="wp-block-paragraph">To associate this private DNS zone now with my virtual network, I will next create a virtual network link. Therefore I add the resource block below to my main.tf file:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="201" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-46.png" alt="" class="wp-image-36634" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-46.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-46-300x64.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-46-768x164.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<p class="wp-block-paragraph">To be able to securely connect to my azure sql database I will now create a private endpoint in my backend subnet. Therefore I add the resource block below to my main.tf file:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="405" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-47.png" alt="" class="wp-image-36635" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-47.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-47-300x129.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-47-768x331.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<p class="wp-block-paragraph">With this configuration code, I create a private endpoint with the name of the Azure SQL Server and the suffix “-endpoint”. Through the option “subnet_id” I place this endpoint in the backend subnet with a private service connection to the Azure SQL Server. I also associate the endpoint to the private DNS zone, which I’ve created just before, for name resolution.</p>



<h2 class="wp-block-heading" id="h-create-an-azure-bastion">Create an Azure Bastion:</h2>



<p class="wp-block-paragraph">Lets now continue and create an azure bastion host for the administration of our environment. Therefore I first create a public IP address through adding the resource block below to my main.tf file:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="938" height="313" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-48.png" alt="" class="wp-image-36636" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-48.png 938w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-48-300x100.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-48-768x256.png 768w" sizes="auto, (max-width: 938px) 100vw, 938px" /></figure>



<p class="wp-block-paragraph">Next I create the bastion host itself. For that I add the code block below to my main.tf file:</p>



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



<h2 class="wp-block-heading" id="h-create-a-virtual-machine">Create a Virtual Machine:</h2>



<p class="wp-block-paragraph">Now I will add a virtual machine to my middle tier subnet. Therefore I need to create first a network interface for that virtual machine. The resource block below will create the needed network interface:</p>



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



<p class="wp-block-paragraph">As the virtual machine, which I intend to create, needs an admin password like the azure sql server, I will create an additional password variable. Therefore I add the code block below to my variables.tf file:</p>



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



<p class="wp-block-paragraph">To create the virtual machine itself, I add the resource block below to my main.tf file:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="765" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-52.png" alt="" class="wp-image-36640" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-52.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-52-300x244.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-52-768x625.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<h2 class="wp-block-heading" id="h-create-network-security-groups-and-rules">Create Network Security Groups and Rules:</h2>



<p class="wp-block-paragraph">Next I want to secure my subnets. Therefore I create for my front tier, middle tier and backend tier subnet a network security group by adding the resource blocks below to my main.tf file:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="302" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-53.png" alt="" class="wp-image-36641" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-53.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-53-300x96.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-53-768x247.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="263" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-54.png" alt="" class="wp-image-36642" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-54.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-54-300x84.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-54-768x215.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="262" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-55.png" alt="" class="wp-image-36643" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-55.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-55-300x84.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-55-768x214.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<p class="wp-block-paragraph">Next I create for each network security group particular rules.</p>



<p class="wp-block-paragraph">Starting with the front tier subnet I want to block all Inbound traffic except traffic over https. Therefore I add the two resource blocks below to my main.tf file:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="690" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-56.png" alt="" class="wp-image-36644" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-56.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-56-300x220.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-56-768x564.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<p class="wp-block-paragraph">Continuing with the middle tier subnet I want to block all inbound traffic but allow http traffic only from the front tier subnet and allow rdp traffic only from the bastion subnet. Therefore I add the three resource blocks below to my main.tf file:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="667" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-57.png" alt="" class="wp-image-36645" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-57.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-57-300x213.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-57-768x545.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="354" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-58.png" alt="" class="wp-image-36646" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-58.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-58-300x113.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-58-768x289.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<p class="wp-block-paragraph">Last but not least I want to block all Inbound traffic to my backend tier subnet except traffic to the sql-server port from the middle tier subnet. In addition I want to block explicitly the internet access from this subnet.</p>



<p class="wp-block-paragraph">You are questioning why I’m explicitly block internet access from this subnet while I haven’t any public IP address or NAT gateway for this subnet? That’s because Microsoft provides access to the internet through a default outbound IP address in case no explicit way is defined. That’s a feature which will be deprecated on the 30<sup>th</sup> September 2025. To get more information’s about this feature check out this <a href="https://learn.microsoft.com/en-us/azure/virtual-network/ip-services/default-outbound-access">link</a> from Microsoft.</p>



<p class="wp-block-paragraph">To create the rules for the backend tier subnet I add the three resource blocks below to my main.tf file:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="662" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-59.png" alt="" class="wp-image-36647" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-59.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-59-300x211.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-59-768x541.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="341" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-60.png" alt="" class="wp-image-36648" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-60.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-60-300x109.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-60-768x279.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<h2 class="wp-block-heading" id="h-define-output-variables">Define Output Variables:</h2>



<p class="wp-block-paragraph">I will stop with the creation of resources for this blog post and will show you finally how you can define outputs. For example let’s assume we want to have the name of the Azure SQL Server and the IP address of the virtual machine extracted after the deployment. Therefore I add the two output variables below to the outputs.tf file:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="364" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-61.png" alt="" class="wp-image-36649" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-61.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-61-300x116.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-61-768x297.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<p class="wp-block-paragraph">Outputs are especially useful when you need to pass up information’s from the deployment to a higher context. For example when you are working with modules in terraform and you want to pass information’s from a child module to a parent module. In our case the outputs will just be printed out to the command line after the deployment.</p>



<h2 class="wp-block-heading" id="h-apply-the-configuration-code">Apply the Configuration Code:</h2>



<p class="wp-block-paragraph">As I am now done with the definition of the configuration code for this blog post, I will plan and apply my configuration for each stage. Before doing so, I need to first set a value for my password variables. On Ubuntu this can be done with this command:</p>



<p class="wp-block-paragraph"><strong><em>export TF_VAR_sqlserver_password=&#8221;your password&#8221;</em></strong></p>



<p class="wp-block-paragraph"><strong><em>export TF_VAR_vm_password=&#8221;your password&#8221;</em></strong></p>



<p class="wp-block-paragraph">After I’ve set the variables, I run the terraform plan command and we can see that terraform would create 29 resources:</p>



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



<p class="wp-block-paragraph">This seems to be good for my so I run the terraform apply command to deploy my infrastructure:</p>



<p class="wp-block-paragraph"><strong><em>terraform apply</em></strong></p>



<p class="wp-block-paragraph">After some minutes of patience terraform applied the configuration code successfully:</p>



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



<h2 class="wp-block-heading" id="h-check-the-infrastructure-in-the-azure-portal">Check the Infrastructure in the Azure Portal:</h2>



<p class="wp-block-paragraph">When I’m signing in to the Azure portal I can see my development resource group with all the resources inside:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="429" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-64.png" alt="" class="wp-image-36653" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-64.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-64-300x137.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-64-768x351.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<p class="wp-block-paragraph">I want to have my test and production resources as well so I switch the terraform workspace to test and production and run in both workspaces again the terraform apply command.</p>



<p class="wp-block-paragraph">After some additional minutes of patience we can see in the Azure portal that we have now all resources for each environment stage:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="939" height="230" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-65.png" alt="" class="wp-image-36654" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-65.png 939w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-65-300x73.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-65-768x188.png 768w" sizes="auto, (max-width: 939px) 100vw, 939px" /></figure>



<p class="wp-block-paragraph">Lets now compare the settings from the productive database with the development database and we can see that the SKU for the productive one is business critical while the SKU for the non-productive one is general purpose:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="257" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-66.png" alt="" class="wp-image-36655" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-66.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-66-300x82.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-66-768x210.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="243" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-67.png" alt="" class="wp-image-36656" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-67.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-67-300x78.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-67-768x199.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<p class="wp-block-paragraph">The Backup storage has also been set according to the “database_settings” variable:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="453" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-68.png" alt="" class="wp-image-36657" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-68.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-68-300x145.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-68-768x370.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



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



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="474" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-71.png" alt="" class="wp-image-36660" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-71.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-71-300x151.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-71-768x387.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



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



<p class="wp-block-paragraph">We can see the same for the point in time recovery option:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="939" height="350" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-72.png" alt="" class="wp-image-36663" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-72.png 939w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-72-300x112.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-72-768x286.png 768w" sizes="auto, (max-width: 939px) 100vw, 939px" /></figure>



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



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="939" height="334" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-73.png" alt="" class="wp-image-36664" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-73.png 939w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-73-300x107.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-73-768x273.png 768w" sizes="auto, (max-width: 939px) 100vw, 939px" /></figure>



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



<p class="wp-block-paragraph">We can see that our subnets are also all in place with the corresponding address space and network security group associated:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="191" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-74.png" alt="" class="wp-image-36665" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-74.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-74-300x61.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-74-768x156.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="939" height="189" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-75.png" alt="" class="wp-image-36666" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-75.png 939w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-75-300x60.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-75-768x155.png 768w" sizes="auto, (max-width: 939px) 100vw, 939px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="939" height="197" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-76.png" alt="" class="wp-image-36667" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-76.png 939w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-76-300x63.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-76-768x161.png 768w" sizes="auto, (max-width: 939px) 100vw, 939px" /></figure>



<p class="wp-block-paragraph">Lets check the private endpoint of the Azure SQL Server. We can see that we have a private IP address within our backend subnet which is linked to the Azure SQL Server:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="940" height="102" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-77.png" alt="" class="wp-image-36668" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-77.png 940w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-77-300x33.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-77-768x83.png 768w" sizes="auto, (max-width: 940px) 100vw, 940px" /></figure>



<p class="wp-block-paragraph">Lets connect to the virtual machine and try a name resolution. You can see that we were able to successfully resolve the FQDN:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="879" height="269" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-78.png" alt="" class="wp-image-36669" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-78.png 879w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-78-300x92.png 300w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2025/01/image-78-768x235.png 768w" sizes="auto, (max-width: 879px) 100vw, 879px" /></figure>



<p class="wp-block-paragraph">After installing SQL-Server management studio on the virtual machine, we can also connect to the Azure SQL Server through the FQDN of the private endpoint:</p>



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



<h2 class="wp-block-heading" id="h-delete-the-deployed-resources">Delete the Deployed Resources:</h2>



<p class="wp-block-paragraph">For now preventing getting a high bill for something I didn’t use, I will now delete all resources which I’ve created with Terraform. This is very simple, and can be done through running the terraform destroy command in each workspace:</p>



<p class="wp-block-paragraph"><strong><em>terraform destroy</em></strong></p>



<p class="wp-block-paragraph">I hope you got some interesting examples and ideas about Terraform and Azure! Feel free to share your questions and feelings about Terraform and Azure with me in the comment section below.</p>



<p class="wp-block-paragraph"><strong>PS:</strong> I’ve uploaded the configuration code used in this blog post to a GitHub repository to make it easier for you to reproduce the steps and examples. You can find it <a href="https://github.com/HocineMechara/TerraformBlog01">here</a>.</p>



<p class="wp-block-paragraph"></p>
<p>L’article <a href="https://www.dbi-services.com/blog/automate-your-deployments-in-azure-with-terraform/">Automate your Deployments in Azure with Terraform!</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/automate-your-deployments-in-azure-with-terraform/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Understanding Head Blockers in SQL-Server</title>
		<link>https://www.dbi-services.com/blog/understanding-head-blockers-in-sql-server/</link>
					<comments>https://www.dbi-services.com/blog/understanding-head-blockers-in-sql-server/#comments</comments>
		
		<dc:creator><![CDATA[Hocine Mechara]]></dc:creator>
		<pubDate>Thu, 10 Oct 2024 15:13:15 +0000</pubDate>
				<category><![CDATA[Database Administration & Monitoring]]></category>
		<category><![CDATA[Database management]]></category>
		<category><![CDATA[SQL Server]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=35164</guid>

					<description><![CDATA[<p>A customer of mine had a recurring issue with one of his business critical applications where everything was frozen and stuck for the users. The issue was caused due to a head blocking transaction which increased blocking in the database exponentially. Depending on your technical background, you may know that locking is a very important [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/understanding-head-blockers-in-sql-server/">Understanding Head Blockers in SQL-Server</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">A customer of mine had a recurring issue with one of his business critical applications where everything was frozen and stuck for the users. The issue was caused due to a head blocking transaction which increased blocking in the database exponentially. Depending on your technical background, you may know that locking is a very important mechanism in relational database systems to ensure transactional isolation, thus fulfilling the “I” in the theoretical ACID concept. However, when locks cause extensive blocking, it can lead to performance issues.</p>



<h2 class="wp-block-heading" id="h-but-what-is-now-a-head-blocker"><strong>But what is now a head blocker?</strong></h2>



<p class="wp-block-paragraph">A head blocker is a transaction that is either very slow or never commits, holding locks on database resources. Other transactions that must access the resources locked by the head blocker are also holding locks while waiting for those resources.</p>



<p class="wp-block-paragraph">On the next level, there are more transactions that need access to resources locked by the transactions blocked by the head blocker, and they too are holding locks. This process continues on multiple levels, creating a &#8220;lock chain&#8221; where all the transactions are waiting for the first transaction (the head blocker) to commit or roll back.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="602" height="498" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-37.png" alt="" class="wp-image-35165" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-37.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-37-300x248.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>


<p>For the non-technical blog reader, let me give you an example from a daily life situation:</p>
<p>You are in the supermarket and you see a long queue behind the checkout. At the front of the queue you can see person A who is talking with the cashier about the weather, the next neighborhood event and about the cat of the neighbor. This person is the head blocker. Behind that person is person B  and person C waiting. This is the beginning of the lock chain and the checkout is the database resource on which the head blocker is holding a lock.</p>
<p>To better understand the complexity of the lock mechanism in relational database systems with this metaphor imagine that only one person is allowed to have a specific item in their shopping cart at any given time in the supermarket.</p>
<p>Person C is waiting for person A at the checkout and has a liter of milk in their shopping cart. Person D, who already has bread and butter in their shopping cart, but wants some milk too, must wait until person C can pay and leave the supermarket. Person E and F, who also already have some items in their shopping cart but still have butter or bread on their shopping list, must wait too. The whole process continues until everyone in the supermarket is waiting for person A to complete the story about the neighbor’s cat.</p>
<p>In the case of my customer, the IT-staff there terminated the head blocking transaction manually as far as they recognized it.</p>
<p>The major problem on this approach was, that the IT-staff remarked the problem through user feedback. So way too late.</p>
<p>Therefore I implemented a custom developed alert solution for the case when blocking is increasing within the applications database. This enabled the IT-staff to terminate the head blocking transaction before the application users were disturbed.</p>


<h2 class="wp-block-heading" id="h-how-to-determine-blocking-in-a-database"><strong>How to determine blocking in a database?</strong></h2>


<p>SQL-Server offers some very useful dynamic management views (DMV) to query information’s about blocking. For example the DMV  “<em>sys.dm_exec_requests”</em>. When you query this DMV and you are interested in blocking, the most interesting column is the “<em>blocking_session_id</em>” column.  This column shows you which session is currently blocking the particular request. If the request isn’t blocked, there will be a “<em>0</em>”.</p>


<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="406" height="158" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-40.png" alt="" class="wp-image-35168" style="width:397px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-40.png 406w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-40-300x117.png 300w" sizes="auto, (max-width: 406px) 100vw, 406px" /></figure>


<p>There are also other pragmatic ways to gather information about currently running processes and their blocking. One very user-friendly method that provides a wealth of useful information is the stored procedure sp_whoisactive, developed by Adam Mechanic. This stored procedure displays currently running processes in SQL Server in a user-friendly manner and can be managed through multiple input parameters. It also returns a blocking_session_id column and many other useful details, such as the SQL text, the number of reads and writes, CPU time, and more. The stored procedure is not available by default in SQL Server but can be downloaded and installed from GitHub. </p>
<p>Another valuable solution is the stored procedure sp_HumanEventsBlockViewer, created by Erik Darling. This procedure is used in conjunction with an extended event session, which collects data from the Blocked Process Report. The stored procedure reads from the extended event session and presents the information in a very useful format, including valuable details such as the SQL text, the isolation level of the transactions, the wait time, and more.</p>

<p>I used th DMV &#8220;<em>sys.dm_exec_requests&#8221;</em> in the stored procedure that I developed to monitor blocking in the customer’s database.</p>
<p>Please note that I’m just sharing some key snippets of the stored procedure and not the entire procedure, as sharing the whole one would go beyond the planed scope of this blog post.</p>
<p>The stored procedure queries first of all the DMV with the “<em>count</em>” aggregation function to get the total number of blocked requests:</p>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="602" height="205" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-42.png" alt="" class="wp-image-35170" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-42.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-42-300x102.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>


<p>After that the stored procedure checks if the amount of blocked requests exceeds the defined sensitivity threshold:</p>


<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="350" height="69" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-43.png" alt="" class="wp-image-35171" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-43.png 350w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-43-300x59.png 300w" sizes="auto, (max-width: 350px) 100vw, 350px" /></figure>



<p class="wp-block-paragraph">If this is true, the stored procedure starts with the collection of the information’s which we were interested in. The first information is the session that is holding the most locks in the database. Therefore I wrote the query below which is storing the particular session ID in a variable:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="602" height="114" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-44.png" alt="" class="wp-image-35172" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-44.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-44-300x57.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph">After that, the stored procedure additionally gathers the number of blocked requests by this session and stores it in a second variable:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="602" height="113" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-45.png" alt="" class="wp-image-35173" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-45.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-45-300x56.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph">After that I used the session ID from the session, which is blocking the most requests, as a starting point to loop the lock chain up through&nbsp; the DMV:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="602" height="180" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-46.png" alt="" class="wp-image-35174" style="width:840px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-46.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-46-300x90.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph">After that I formatted everything in HTML and sent the information’s through the database mail. This resulted in the mail below which warned the recipients about the exceeding of the blocking threshold and provided some valuable information’s:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="600" height="291" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-47.png" alt="" class="wp-image-35175" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-47.png 600w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/10/image-47-300x146.png 300w" sizes="auto, (max-width: 600px) 100vw, 600px" /></figure>



<h2 class="wp-block-heading" id="h-summary"><strong>Summary:</strong></h2>



<p class="wp-block-paragraph">This blog post presents an approach to monitor blocking within a SQL-Server database. Locking is an important mechanism in SQL-Server and other relational database systems to ensure transactional isolation and it is usual that locking leads to blocking in an acceptable extent. However, extensive blocking can slow down the performance, which is considered a problem. There are multiple of potential root causes of blocking problems, including:</p>



<ul class="wp-block-list">
<li>Poor query performance, leading to long running transactions</li>



<li>Lock escalation</li>



<li>High concurrency on particular database resources</li>



<li>Bad transaction design</li>



<li>High transaction isolation level</li>
</ul>



<p class="wp-block-paragraph">If you are facing similar problems or if you have any questions don’t hesitate to share it in the comment section!</p>
<p>L’article <a href="https://www.dbi-services.com/blog/understanding-head-blockers-in-sql-server/">Understanding Head Blockers in SQL-Server</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/understanding-head-blockers-in-sql-server/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Keep an eye on your SQL Agent jobs using T-SQL!</title>
		<link>https://www.dbi-services.com/blog/keep-an-eye-on-your-sql-agent-jobs-using-t-sql/</link>
					<comments>https://www.dbi-services.com/blog/keep-an-eye-on-your-sql-agent-jobs-using-t-sql/#respond</comments>
		
		<dc:creator><![CDATA[Hocine Mechara]]></dc:creator>
		<pubDate>Wed, 12 Jun 2024 12:50:36 +0000</pubDate>
				<category><![CDATA[Database Administration & Monitoring]]></category>
		<category><![CDATA[SQL Server]]></category>
		<guid isPermaLink="false">https://www.dbi-services.com/blog/?p=33617</guid>

					<description><![CDATA[<p>Introduction: A couple of weeks ago I had a customer who was interested in monitoring the duration of some of his SQL-Agent jobs. The jobs were running in a data warehouse environment and were executing some ETL processes for loading data. The goal was to get informed when one of these specific jobs were running [&#8230;]</p>
<p>L’article <a href="https://www.dbi-services.com/blog/keep-an-eye-on-your-sql-agent-jobs-using-t-sql/">Keep an eye on your SQL Agent jobs using T-SQL!</a> est apparu en premier sur <a href="https://www.dbi-services.com/blog">dbi Blog</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading" id="h-introduction">Introduction:</h2>



<p class="wp-block-paragraph">A couple of weeks ago I had a customer who was interested in monitoring the duration of some of his SQL-Agent jobs. The jobs were running in a data warehouse environment and were executing some ETL processes for loading data. The goal was to get informed when one of these specific jobs were running significantly longer than usual. Therefore I wrote a short stored procedure and a script which I would like to show you in this blog post!</p>



<p class="wp-block-paragraph">First of all, I would like to start with explaining the parameter’s of the stored procedure to get a better understanding of the logic:</p>



<h2 class="wp-block-heading" id="h-input-parameter-s">Input parameter&#8217;s:</h2>



<p class="wp-block-paragraph">We have the following input parameter’s for influencing the behavior of the stored procedure:</p>



<p class="wp-block-paragraph"><strong><u>@SensitivityPercentage</u></strong>: This “float” parameter defines the percentage increase of the last job runtime compared to the calculated average value, which must be exceeded to set the <strong>@ThresholdExceeded</strong> output variable as true. I set the default value for this Input parameter to “30%” or “0.3“ in decimal, but the value can be overwritten to a less or more aggressive behavior when executing the stored procedure.</p>



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



<p class="wp-block-paragraph"><strong><u>@SensivityTotalMinutes</u></strong>: This “integer” parameter defines the increase in minutes of the last job runtime compared to the calculated average value, which must be exceeded to set the <strong>@ThresholdExceeded</strong> output variable as true. The default value in the stored procedure is 20 minutes, but it can be changed here as well!</p>



<p class="wp-block-paragraph">Note: both values provided in the sensitivity variables must be exceeded to set the <strong>@ThresholdExceeded </strong>variable as true. I implemented this logic, for not getting notified when a job is increasing from 1 minute to 2 minutes – the increase would be 100% but as it’s only 1 minute I don’t care😉</p>



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



<p class="wp-block-paragraph"><strong><u>@granularity</u></strong>: This “integer” parameter defines how many of the latest entries of the job history should be used to calculate a average value for comparing the last run duration. I set this with a default value of 5, but here as well you can change it.</p>



<p class="wp-block-paragraph">Note: If in the job history are less than 5 entries, the stored procedure takes automatically the amount of entries which are available to calculate a average value.</p>



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



<p class="wp-block-paragraph"><strong><u>@JobId </u></strong>-&gt; This is quite obvious: The ID of the job which should be checked.</p>



<h2 class="wp-block-heading" id="h-output-parameter-s">Output parameter&#8217;s:</h2>



<p class="wp-block-paragraph">To get some interesting values out of the stored procedure, I defined the following output parameters:</p>



<p class="wp-block-paragraph"><strong><u>@AverageJobRunDuration</u></strong>: This is the average job run duration in minutes.</p>



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



<p class="wp-block-paragraph"><strong><u>@RunDurationLastExecution</u></strong>: This is the duration of the last execution in minutes.</p>



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



<p class="wp-block-paragraph"><strong><u>@Factor</u></strong>: This is the factor of the duration from the last execution compared to the calculated average value.</p>



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



<p class="wp-block-paragraph"><strong><u>@IncreasionPercentageString</u></strong>: This is the increase or decrease of the last duration compared to the average value in a percentage value.</p>



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



<p class="wp-block-paragraph"><strong><u>@IncreasionTotalMinutes</u></strong>: This is the increase or decrease of the last duration compared to the average value in minutes.</p>



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



<p class="wp-block-paragraph"><strong><u>@OutputMessage</u></strong>: This is a generated output message.</p>



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



<p class="wp-block-paragraph"><strong><u>@ThresholdExceeded</u></strong>: This defines if the threshold is exceeded based on the above sensitivity values.</p>



<h2 class="wp-block-heading" id="h-the-stored-procedure">The stored procedure:</h2>



<p class="wp-block-paragraph">See below the stored procedure:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
CREATE PROCEDURE &#x5B;dbo].&#x5B;dbi_jobruntimeinfo]
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--Parameters
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
(

--Input
@SensitivityPercentage float = 0.3,
@SensivityTotalMinutes int = 20,
@granularity int = 5,
@JobId uniqueidentifier,

--Output
@AverageJobRunDuration float OUTPUT,
@RunDurationLastExecution float OUTPUT,
@Factor float OUTPUT,
@IncreasionPercentageString NVARCHAR(50) OUTPUT,
@IncreasionTotalMinutes int OUTPUT,
@OutputMessage NVARCHAR(max) OUTPUT,
@ThresholdExceeded BIT OUTPUT

)

AS

Begin

   SET NOCOUNT ON;

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--Gather average runtime information
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
		declare @IncreasionPercentage float
       --@StepCount selects how many stps a specific Job has
       declare @StepCount Int
       Set @StepCount = (select count(*) from msdb.dbo.sysjobsteps where job_id = @JobId)

       --@RowCount dynamically sets how many rows of informations must be queried from job history --&gt; its calculated between granularity and the amount of job steps for a specific job
       declare @RowCount Int
       Set @RowCount = @granularity * @StepCount

	   declare @TotalAvailableRows int
	   set @TotalAvailableRows = (
	   select count(*)
	   From msdb.dbo.sysjobs j 
	   INNER JOIN msdb.dbo.sysjobhistory h 
	   ON j.job_id = h.job_id 
	   where j.enabled = 1  --Only Enabled Job
	   and j.job_id = @JobId
	   and h.step_id != 0)

	   if @TotalAvailableRows &lt;= @RowCount
		Begin 
			if @TotalAvailableRows &lt;= @StepCount
				Begin
				Declare @MessageReturn NVARCHAR(max)
				Set @MessageReturn= &#039;Not enough History Data for Job with ID: &#039; +cast(@JobId as nvarchar(50))+ &#039; There is only: &#039; + cast((@TotalAvailableRows/@StepCount) as nvarchar (1)) + &#039; Runtime entrie in the history.&#039;
				Print @MessageReturn
				set @AverageJobRunDuration = NULL
				set @RunDurationLastExecution = NULL
				set @Factor = NULL
				set @IncreasionPercentageString = NULL
				set @IncreasionTotalMinutes = NULL
				set @OutputMessage = @MessageReturn
				set @ThresholdExceeded = NULL
				Return
				End

			Set @RowCount = @TotalAvailableRows
			set @granularity = (@RowCount - @StepCount) / @StepCount
		End
			else
				Begin
					set @RowCount = @RowCount + @StepCount
				End
			print cast(@RowCount as NVARCHAR(max)) + &#039; Rowcount&#039;
			print cast(@granularity as nvarchar(max)) + &#039; Granularity&#039;

       --@SumRunDuration is for summing the total run Duration of all job executions defined in @granularity
       declare @SumRunDuration int

       Set @SumRunDuration = (Select SUM(RunDurationMinutes) from
       (select Top (@RowCount) j.name as &#039;JobName&#039;, h.step_id as &#039;StepID&#039;,
       msdb.dbo.agent_datetime(run_date, run_time) as &#039;RunDateTime&#039;,
       ((run_duration/10000*3600 + (run_duration/100)%100*60 + run_duration%100 + 31 ) / 60)
                     as &#039;RunDurationMinutes&#039;
       From msdb.dbo.sysjobs j 
       INNER JOIN msdb.dbo.sysjobhistory h 
       ON j.job_id = h.job_id 
       where j.enabled = 1  --Only Enabled Job
       and j.job_id = @JobId
       and h.step_id != 0
       Order by run_date desc, run_time desc) SubSelect)

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--Gather last runtime information
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

       declare @RowCountLastExecution int
       set @RowCountLastExecution = 1 * @StepCount

       set @RunDurationLastExecution = (Select SUM(RunDurationMinutes) from
       (select Top (@RowCountLastExecution) j.name as &#039;JobName&#039;, h.step_id as &#039;StepID&#039;,
       msdb.dbo.agent_datetime(run_date, run_time) as &#039;RunDateTime&#039;,
       ((run_duration/10000*3600 + (run_duration/100)%100*60 + run_duration%100 + 31 ) / 60)
                     as &#039;RunDurationMinutes&#039;
       From msdb.dbo.sysjobs j 
       INNER JOIN msdb.dbo.sysjobhistory h 
       ON j.job_id = h.job_id 
       where j.enabled = 1  --Only Enabled Job
       and j.job_id = @JobId
       and h.step_id != 0
       Order by run_date desc, run_time desc) SubSelect)

       --@AverageJobRunDuration calculates the avarage runtime for a single job execution. Calculateion is made through @SumRunDuration and the @granularity
       set @AverageJobRunDuration = (@SumRunDuration - @RunDurationLastExecution) / @granularity
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--Calculate Factor
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
       
       Begin try
              set @Factor = (1/@AverageJobRunDuration)*@RunDurationLastExecution
       End try
       Begin catch
              set @Factor = NULL
       End catch

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--Check if sensitivity threshold is exceeded
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

       if @Factor != 0 and @Factor !&lt; 0 and @Factor Is Not NULL
       Begin
              set @IncreasionPercentage = @Factor -1
              set @IncreasionTotalMinutes = @RunDurationLastExecution - @AverageJobRunDuration

				if @IncreasionPercentage &gt; 0
				Begin
					set @IncreasionPercentageString = &#039;+&#039; + convert(nvarchar(max), @IncreasionPercentage*100) + &#039;%&#039;
				End
					Else
					Begin
					set @IncreasionPercentageString = convert(nvarchar(max), @IncreasionPercentage*100) + &#039;%&#039;
					End

       End 
              Else 
              Begin
              set @IncreasionPercentage = NULL
			  set @IncreasionPercentageString = NULL
              set @IncreasionTotalMinutes = NULL
              End

       If @IncreasionPercentage Is Not Null and @IncreasionTotalMinutes is not null and @IncreasionPercentage &gt;= @SensitivityPercentage and @IncreasionTotalMinutes &gt;= @SensivityTotalMinutes
       Begin
              Set @OutputMessage = &#039;Job with ID: &#039; + cast(@JobId as nvarchar(200)) + &#039; has exceed the defined thresholds. &#039; + &#039;The duration has increased by factor: &#039; +  cast(@Factor as nvarchar(200)) + &#039; Which is in total &#039; + cast(@IncreasionTotalMinutes as nvarchar(200)) + &#039; minutes.&#039;
              Set @ThresholdExceeded = 1
       End
       Else
              Begin
              Set @OutputMessage = &#039;Job with ID: &#039; + cast(@JobId as nvarchar(200)) + &#039; has not exceeded the defined threshold&#039;
              Set @ThresholdExceeded = 0
              End

End
GO



</pre></div>


<h2 class="wp-block-heading" id="h-execution-script">Execution Script:</h2>



<p class="wp-block-paragraph">In case of my customer I implemented a daily running job which is executing the stored procedure through an execution script. In this case a daily schedule was appropriate, as the ETL jobs were also running on a daily base.</p>



<p class="wp-block-paragraph">In the script below, you can put every job name which should be monitored in the <strong>@Jobs </strong>variable and divide the names by semicolons. The <strong>STRING_SPLIT</strong> function will separate the string by semicolons and load the cursor with the job names. Note: that this functions is available from compatibility level 130 and above.</p>



<p class="wp-block-paragraph">After the cursor is loaded with each job, the script will execute the stored procedure for each of them. If the threshold is exceeded for any of the provided jobs, the execution script will use SQL-Server mail to inform. To get informed you must put in the appropriate mail profile and recipients mail addresses in the <strong>@profile_name</strong> and <strong>@recipients </strong>input parameter!</p>



<p class="wp-block-paragraph">See below the execution script:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
declare @Jobs NVARCHAR(max)
Set @Jobs = &#039;&#039;

Declare @Job_Id uniqueidentifier
Declare @JobName Nvarchar(max)

--Load Jobs i cursor
declare jobs_cursor cursor
       for select value from STRING_SPLIT(@Jobs, &#039;;&#039;)

open jobs_cursor
fetch next from jobs_cursor into @JobName

While @@FETCH_STATUS = 0
Begin

       --set JobId from Jobname
       set @Job_Id = (select j.job_id from msdb.dbo.sysjobs j where j.name = @JobName)

       --Exec SP
       declare @AverageJobRunDurationOut float
       declare @RunDurationLastExecutionOut int
       declare @FactorOut float
	   declare @IncreasionPercentageStringOut NVARCHAR(50)
       declare @IncreasionTotalMinutesOut int
       declare @OutputMessageOut NVARCHAR(max)
       declare @ThresholdExceededOut BIT

       exec dbo.dbi_jobruntimeinfo @SensivityTotalMinutes = 1,
       @JobId = @Job_Id,
       @AverageJobRunDuration = @AverageJobRunDurationOut Output,
       @RunDurationLastExecution = @RunDurationLastExecutionOut Output,
       @Factor = @FactorOut Output,
       @IncreasionTotalMinutes = @IncreasionTotalMinutesOut Output,
       @OutputMessage = @OutputMessageOut Output,
       @ThresholdExceeded = @ThresholdExceededOut Output,
	   @IncreasionPercentageString = @IncreasionPercentageStringOut Output;

       Select 
	   @JobName as &#039;Job&#039;,
	   @AverageJobRunDurationOut as &#039;AverageDuration&#039;,
	   @RunDurationLastExecutionOut as &#039;DurationLastExecution&#039;,
	   @IncreasionPercentageStringOut as &#039;IncreasionInPercent&#039;,
	   @IncreasionTotalMinutesOut as &#039;IncreasionMinutes&#039;,
       @ThresholdExceededOut as &#039;Threshold exceeded&#039;,
	   @FactorOut as &#039;Factor&#039;,
	   @OutputMessageOut as &#039;Message&#039;

	   

	   if @ThresholdExceededOut = 1
	   Begin

		declare @bodycontent nvarchar(max)
		set @bodycontent = &#039;Job Name = &#039;+@JobName+&#039;; Average duration in minutes = &#039;+cast(@AverageJobRunDurationOut as nvarchar(max))+&#039;; Duration of last execution in minutes = &#039;
		+cast(@RunDurationLastExecutionOut as nvarchar(max))+&#039;; Slow down in percent = &#039;+
		@IncreasionPercentageStringOut+&#039;; Slow down in minutes = &#039;+cast(@IncreasionTotalMinutesOut as nvarchar(max))
		
		declare @subjectcontent nvarchar(100)
		set @subjectcontent = &#039;SQL Agent Job: &#039;+@JobName+&#039; has slowed down&#039;

		exec msdb.dbo.sp_send_dbmail
			@profile_name = &#039;&#039;,
			@recipients  = &#039;&#039;,
			@subject = @subjectcontent,
			@body = @bodycontent
       End

       fetch next from jobs_cursor into @JobName

End
CLOSE jobs_cursor;
DEALLOCATE jobs_cursor;
</pre></div>


<h2 class="wp-block-heading" id="h-let-s-see-how-it-works"><strong><u>Let’s see how it works!</u></strong></h2>



<p class="wp-block-paragraph">For testing I have two jobs as shown below:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="401" height="237" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-31.png" alt="" class="wp-image-33619" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-31.png 401w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-31-300x177.png 300w" sizes="auto, (max-width: 401px) 100vw, 401px" /></figure>



<p class="wp-block-paragraph">The only thing which these jobs are actually doing is waiting:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="276" height="294" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-32.png" alt="" class="wp-image-33620" /></figure>



<p class="wp-block-paragraph">The job <strong>Test_dbi_jobruntimeinfo</strong> has one step and the job <strong>Test_dbi_jobruntimeinfo_2</strong> has two steps:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="498" height="203" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-33.png" alt="" class="wp-image-33621" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-33.png 498w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-33-300x122.png 300w" sizes="auto, (max-width: 498px) 100vw, 498px" /></figure>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="499" height="187" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-34.png" alt="" class="wp-image-33622" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-34.png 499w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-34-300x112.png 300w" sizes="auto, (max-width: 499px) 100vw, 499px" /></figure>



<p class="wp-block-paragraph">For demonstration purpose I made sure, that the history for both jobs is empty:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="432" height="276" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-35.png" alt="" class="wp-image-33623" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-35.png 432w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-35-300x192.png 300w" sizes="auto, (max-width: 432px) 100vw, 432px" /></figure>



<p class="wp-block-paragraph">Let’s start and see how the stored procedure is going to behave when no data is in the history. Therefore I’m putting the name of both jobs in the <strong>@Jobs</strong> variable of my execution script:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="541" height="110" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-36.png" alt="" class="wp-image-33624" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-36.png 541w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-36-300x61.png 300w" sizes="auto, (max-width: 541px) 100vw, 541px" /></figure>



<p class="wp-block-paragraph">In this demonstration I’m also selecting the output values from the stored procedure for showing the returned values:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="447" height="174" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-37.png" alt="" class="wp-image-33625" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-37.png 447w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-37-300x117.png 300w" sizes="auto, (max-width: 447px) 100vw, 447px" /></figure>



<p class="wp-block-paragraph">I’m now executing the script and these are the values which are returned:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="602" height="142" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-53.png" alt="" class="wp-image-33642" style="width:834px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-53.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-53-300x71.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph">We can see that every value is “NULL” and the message says, that there is no history data available for the jobs. The stored procedure is even telling us the amount of available history data which is 0.</p>



<p class="wp-block-paragraph">Let’s execute the jobs to generate a first entry in the history. Note: that each job step is running 35 second which would be rounded by the stored procedure to 1 minute. So the stored procedure will consider the job <strong>Test_dbi_jobruntimeinfo </strong>with one step as running one minute while the job <strong>Test_dbi_jobruntimeinfo_2 </strong>would be considered by the stored procedure as running two minutes.<br><p class="MsoNormal"><span lang="EN-US">The jobs are executed for the first time:</span></p></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="602" height="156" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-57.png" alt="" class="wp-image-33648" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-57.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-57-300x78.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph">Now let’s execute again the script and see if we get any values:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="602" height="135" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-52.png" alt="" class="wp-image-33641" style="width:836px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-52.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-52-300x67.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph">We can see, that we still didn’t get any values. As we have only one entry in the history we obviously can’t compare the last run duration with the previous. But the message is now telling us, that we have “only 1” entry in the history.</p>



<p class="wp-block-paragraph">I’m now executing both jobs again and after that the script and we can see from the script the following data:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="602" height="143" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-50.png" alt="" class="wp-image-33639" style="width:837px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-50.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-50-300x71.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph">We can see, that we have for the first time real data. As mentioned previously: In case that there is not enough history data to calculate the average according to the <strong>@granularity</strong> input parameter (which is 5 per default), the stored procedure is just taking the amount of history data which is available. So in this case were we have 2x entries in the history, the stored procedure is just taking the first duration as the average and compares the second execution to it.</p>



<p class="wp-block-paragraph">I’m now executing the jobs again but now I’m changing the waiting duration within the job steps. For the first job <strong>Test_dbi_jobruntimeinfo</strong>, with only one step, I’m changing the wait duration to 1 minute and 35 seconds which will be considered by the stored procedure as 2 minutes. So we can expect for this job an increase of 100% returned by the stored procedure. For the other job <strong>Test_dbi_jobruntimeinfo_2</strong> I’m doing the same but only for one step. So we can expect there an increase of 50% returned by the stored procedure:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="280" height="267" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-41.png" alt="" class="wp-image-33629" style="width:367px;height:auto" /></figure>



<p class="wp-block-paragraph">After executing the jobs with the changed duration and executing the script, we got the following values back from the stored procedure:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="602" height="150" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-49.png" alt="" class="wp-image-33638" style="width:840px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-49.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-49-300x75.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph">We can see, that we have the increase exactly as expected in the output. But the threshold value is still at “0 = false”, as we didn’t reached the default sensitivity value in minutes of 20 minutes.</p>



<p class="wp-block-paragraph">I’m changing now the waiting duration back to 35 seconds and execute the jobs and the script again. We can expect now a decrease, as the previous increase will be considered when the stored procedure is calculating the average value. After doing so, the following values are returned as expected:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="602" height="142" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-48.png" alt="" class="wp-image-33637" style="width:840px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-48.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-48-300x71.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph">The stored procedure is telling us, that the decrease was 0 minutes because its working with rounded values. But for the percentage value we can see a small decrease.</p>



<p class="wp-block-paragraph">I’m now changing the wait time for the job <strong>Test_dbi_jobruntimeinfo_2</strong> back to 1 minute and 35 seconds but this time for both steps and execute this job again. We can see that we have now again an increase for this job:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="602" height="163" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-47.png" alt="" class="wp-image-33636" style="width:852px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-47.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-47-300x81.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph"><p class="MsoNormal"><span lang="EN-US">In my execution script, I send a mail over the SQL-Server Mail in case when the threshold is exceeded to inform interested people:</span></p></p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="602" height="145" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-44.png" alt="" class="wp-image-33632" style="width:1033px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-44.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-44-300x72.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph">To demonstrate the generated information mail, I overwrite the default sensitivity value in minutes, which is 20 minutes, as I’m too impatient to wait 20 minutes 😉:</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="602" height="166" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-45.png" alt="" class="wp-image-33633" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-45.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-45-300x83.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph">After executing the script again, we can see, that the threshold value for the job <strong>Test_dbi_jobruntimeinfo_2 </strong>is now set to “1 = true”:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="602" height="111" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-54.png" alt="" class="wp-image-33643" style="width:837px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-54.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-54-300x55.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<p class="wp-block-paragraph">And when I check the interface of the test SMTP Server I can see the following mail:</p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="602" height="161" src="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-55.png" alt="" class="wp-image-33644" style="width:827px;height:auto" srcset="https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-55.png 602w, https://www.dbi-services.com/blog/wp-content/uploads/sites/2/2024/06/image-55-300x80.png 300w" sizes="auto, (max-width: 602px) 100vw, 602px" /></figure>



<h2 class="wp-block-heading" id="h-summary">Summary:</h2>



<p class="wp-block-paragraph">For some SQL Agent jobs it can be interesting or necessary to keep an eye on it. Feel free to use the stored procedure and script from this blog or get inspired by them when you are facing a similar situation! </p>



<p class="wp-block-paragraph"><br></p>
<p>L’article <a href="https://www.dbi-services.com/blog/keep-an-eye-on-your-sql-agent-jobs-using-t-sql/">Keep an eye on your SQL Agent jobs using T-SQL!</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/keep-an-eye-on-your-sql-agent-jobs-using-t-sql/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-07-16 05:40:26 by W3 Total Cache
-->