<?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>David Barbarin &#187; Architecture</title>
	<atom:link href="https://blog.developpez.com/mikedavem/pcategory/sql-server-2005/architecture/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.developpez.com/mikedavem</link>
	<description>MVP DataPlatform - MCM SQL Server</description>
	<lastBuildDate>Thu, 09 Sep 2021 21:19:50 +0000</lastBuildDate>
	<language>fr-FR</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.42</generator>
	<item>
		<title>AAD user creation on behalf AAD Service Principal with Azure SQL DB</title>
		<link>https://blog.developpez.com/mikedavem/p13197/sql-azure/aad-user-creation-on-behalf-aad-service-principal-with-azure-sql-db</link>
		<comments>https://blog.developpez.com/mikedavem/p13197/sql-azure/aad-user-creation-on-behalf-aad-service-principal-with-azure-sql-db#comments</comments>
		<pubDate>Sun, 02 Aug 2020 22:28:06 +0000</pubDate>
		<dc:creator><![CDATA[mikedavem]]></dc:creator>
				<category><![CDATA[DevOps]]></category>
		<category><![CDATA[PowerShell]]></category>
		<category><![CDATA[SQL Azure]]></category>
		<category><![CDATA[Authentication]]></category>
		<category><![CDATA[Azure Automation]]></category>
		<category><![CDATA[Azure SQL Database]]></category>
		<category><![CDATA[Azure SQL DB]]></category>
		<category><![CDATA[Powershell]]></category>
		<category><![CDATA[Runbook]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Service Principal]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[System managed identity]]></category>

		<guid isPermaLink="false">http://blog.developpez.com/mikedavem/?p=1643</guid>
		<description><![CDATA[An interesting improvement was announced by the SQL AAD team on Monday 27th July 2020 and concerns the support for Azure AD user creation on behalf of Azure AD Applications for Azure SQL as mentioned to this Microsoft blog post. &#8230; <a href="https://blog.developpez.com/mikedavem/p13197/sql-azure/aad-user-creation-on-behalf-aad-service-principal-with-azure-sql-db">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>An interesting improvement was announced by the SQL AAD team on Monday 27th July 2020 and concerns the support for Azure AD user creation on behalf of Azure AD Applications for Azure SQL as mentioned to this <a href="https://techcommunity.microsoft.com/t5/azure-sql-database/support-for-azure-ad-user-creation-on-behalf-of-azure-ad/ba-p/1491121" rel="noopener" target="_blank">Microsoft blog post</a>. </p>
<p><span id="more-1643"></span></p>
<p>In my company, this is something we were looking for a while with our database refresh process in Azure. Before talking this new feature, let me share a brief history of different considerations we had for this DB refresh process over the time with different approaches we went through. First let’s precise DB Refresh includes usually at least two steps: restoring backup / copying database – you have both ways in Azure SQL Database – and realigning security context with specific users regarding your targeted environment (ACC / INT …).  But the latter is not as trivial as you may expect if you opted to use either a SQL Login / User or a Service Principal to carry out this operation in your process. Indeed, in both cases creating an Azure AD User or Group is not supported, and if you try you will face this error message:</p>
<blockquote><p>‘’ is not a valid login or you do not have permission. </p></blockquote>
<p>All the stuff (either Azure automation runbook and PowerShell modules on-prem) done so far and described afterwards meets the same following process:</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/08/164-1-DB-Refresh-process-e1596406580306.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/08/164-1-DB-Refresh-process-e1596406580306.jpg" alt="164 - 1 - DB Refresh process" width="800" height="566" class="alignnone size-full wp-image-1645" /></a></p>
<p>First, we used Invoke-SQCMD in Azure Automation runbook with T-SQL query to create a copy of a source database to the target server. T-SQL is mandatory in this case as per <a href="https://docs.microsoft.com/en-us/azure/azure-sql/database/database-copy?tabs=azure-powershell" rel="noopener" target="_blank">documented</a> in the Microsoft BOL because PROD and ACC or INT servers are not on the same subscription. Here a simplified sample of code:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">...<br />
$CopyDBCMD = @{<br />
&nbsp; &nbsp; 'Database' = 'master'<br />
&nbsp; &nbsp; 'ServerInstance' = $TargetServerName<br />
&nbsp; &nbsp; 'Username' = $SQLUser<br />
&nbsp; &nbsp; 'Password' = $SQLPWD<br />
&nbsp; &nbsp; 'Query' = 'CREATE DATABASE '+ '[' + $DatabaseName + '] ' + 'AS COPY OF ' + '[' + $SourceServerName + '].[' + $DatabaseName + ']'<br />
} <br />
<br />
Invoke-Sqlcmd @CopyDBCMD <br />
...</div></div>
<p>But as you likely know, Invoke-SQLCMD doesn’t support AAD authentication and because SQL Login authentication was the only option here, it led us dealing with an annoying issue about the security configuration step with AAD users or groups as you may imagine. </p>
<p>Then, because we based authentication mainly on trust architecture and our security rules require using it including apps with managed identities or service principals, we wanted also to introduce this concept to our database refresh process. Fortunately, service principals are supported with <a href="https://techcommunity.microsoft.com/t5/azure-sql-database/token-based-authentication-support-for-azure-sql-db-using-azure/ba-p/386091" rel="noopener" target="_blank">Azure SQL DBs since v12</a> with access token for authentication by ADALSQL. The corresponding DLL is required on your server or if you use it from Azure Automation like us, we added the ADAL.PS module but be aware it is now deprecated, and I advise you to strongly invest in moving to MSAL. Here a sample we used:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;height:450px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">...<br />
$response = Get-ADALToken `<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -ClientId $clientId `<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -ClientSecret $clientSecret `<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -Resource $resourceUri `<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -Authority $authorityUri `<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -TenantId $tenantName<br />
<br />
...<br />
<br />
$connectionString = &quot;Server=tcp:$SqlInstanceFQDN,1433;Initial Catalog=master;Persist Security Info=False;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;&quot;<br />
# Create the connection object<br />
$connection = New-Object System.Data.SqlClient.SqlConnection($connectionString)<br />
# Set AAD generated token to SQL connection token<br />
$connection.AccessToken = $response.AccessToken<br />
<br />
Try {<br />
&nbsp; &nbsp; $connection.Open()<br />
&nbsp; &nbsp; ...<br />
} <br />
...</div></div>
<p>But again, even if the copy or restore steps are well managed, we still got stuck with security reconfiguration, because service principals were not supported for creating AAD users or groups so far &#8230;</p>
<p>In the meantime, we found out a temporary and interesting solution based on <a href="https://dbatools.io/" rel="noopener" target="_blank">dbatools framework</a> and the <a href="https://docs.dbatools.io/#Invoke-DbaQuery" rel="noopener" target="_blank">Invoke-dbaquery command</a> which supports AAD authentication (Login + Password). As we may not rely on service principal in this case, using a dedicated AAD account was an acceptable tradeoff to manage all the database refresh process steps. But going through this way comes with some disadvantages because running Invoke-dbaquery in a full Azure automation mode is not possible with missing ADALsql.dll. Workaround may be to use hybrid-worker, but we didn’t want to add complexity to our current architecture only for this special case. Instead we decided to move the logic of the Azure automation runbook into on-prem PowerShell framework which already include logic for DB refresh for on-prem SQL Server instances. </p>
<p>Here a simplified sample of code we are using:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;height:450px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">...<br />
Try {<br />
&nbsp; &nbsp; # Connect to get access to Key Vault info<br />
&nbsp; &nbsp; Connect-AzAccount | Out-Null<br />
<br />
&nbsp; &nbsp; [String]$user = (Get-AzKeyVaultSecret -VaultName $KeyvaultName -Name &quot;AZSQL-SQLBCKUSER&quot;).SecretValueText<br />
&nbsp; &nbsp; [System.Security.SecureString]$pwd = &nbsp;ConvertTo-SecureString (Get-AzKeyVaultSecret -VaultName $KeyvaultName -Name &quot;AZSQL-SQLBCKPWD&quot;).SecretValueText -AsPlainText -Force<br />
&nbsp; &nbsp; [String]$SourceServerName = (Get-AzKeyVaultSecret -VaultName $KeyvaultName -Name &quot;AZSQL-NAME&quot;).SecretValueText<br />
&nbsp; &nbsp; [String]$TargetServerName = (Get-AzKeyVaultSecret -VaultName $KeyvaultName -Name &quot;AZSQL-TARGETNAME&quot;).SecretValueText + '.database.windows.net'<br />
<br />
&nbsp; &nbsp; # DB Restore will be performed in the context of dedicated AAD account <br />
&nbsp; &nbsp; $pscredential = New-Object -TypeName System.Management.Automation.PSCredential($user, $pwd)<br />
<br />
&nbsp; &nbsp; Write-Host &quot;Restoring DB:$DatabaseName from Source Server: $SourceServerName to Target Server: $TargetServerName&quot;<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; $Query = &quot;CREATE DATABASE [$DatabaseName] AS COPY OF [$SourceServerName].[$DatabaseName]&quot;<br />
&nbsp; &nbsp; Invoke-DbaQuery `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -SqlInstance $TargetServerName `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -Database master `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -SqlCredential $pscredential `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -Query $Query `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -EnableException <br />
<br />
&nbsp; &nbsp; # Wait for DB online and ready ... <br />
&nbsp; &nbsp; # Code should be implemented for this check <br />
<br />
<br />
&nbsp; &nbsp; Write-Output &quot;Applying security configuration to DB: $DatabaseName on Server:$TargetServerName&quot;<br />
<br />
&nbsp; &nbsp; $Query = &quot;<br />
&nbsp; &nbsp; &nbsp; &nbsp; DROP USER [az_sql_ro];CREATE USER [az_sql_ro] FROM EXTERNAL PROVIDER;<br />
&nbsp; &nbsp; &quot;<br />
&nbsp; &nbsp; Invoke-DbaQuery `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -SqlInstance $TargetServerName `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -Database $DatabaseName `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -SqlCredential $pscredential `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -Query $Query `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -EnableException<br />
<br />
}<br />
Catch {<br />
&nbsp; &nbsp; Write-Host &quot;Error encountered: $($_.Exception.Message)&quot;<br />
} <br />
...</div></div>
<p>Referring to the PowerShell code above, in the second step, we create an AAG group [az_sql_ro] on behalf of the AAD dedicated account with the CLAUSE FROM EXTERNAL PROVIDER. </p>
<p>Finally, with the latest news published by the SQL AAD team, we will likely consider using back service principal instead of dedicated Windows AAD account. <a href="https://techcommunity.microsoft.com/t5/azure-sql-database/support-for-azure-ad-user-creation-on-behalf-of-azure-ad/ba-p/1491121" rel="noopener" target="_blank">This Microsoft blog post</a> explains in details how it works and what you have to setup to make it work correctly. I don’t want to duplicate what is already explained so I will apply the new stuff to my context. </p>
<p>Referring to the above blog post, you need first to setup a server identity for your Azure SQL Server as below:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">Set-AzSqlServer `<br />
&nbsp; &nbsp; -ResourceGroupName sandox-rg `<br />
&nbsp; &nbsp; -ServerName a-s-sql02 `<br />
&nbsp; &nbsp; -AssignIdentity<br />
<br />
# Check server identity<br />
Get-AzSqlServer `<br />
&nbsp; &nbsp; -ResourceGroupName sandox-rg `<br />
&nbsp; &nbsp; -ServerName a-s-sql02 | `<br />
&nbsp; &nbsp; Select-Object ServerName, Identity</div></div>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">ServerName Identity &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
---------- -------- &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
a-s-sql02 &nbsp;Microsoft.Azure.Management.Sql.Models.ResourceIdentity</div></div>
<p>Let&rsquo;s have a look at the server identity</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap"># Get identity details<br />
$identity = Get-AzSqlServer `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -ResourceGroupName sandox-rg `<br />
&nbsp; &nbsp; &nbsp; &nbsp; -ServerName a-s-sql02<br />
<br />
$identity.identity</div></div>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">PrincipalId &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Type &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; TenantId &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
----------- &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;---- &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; -------- &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<br />
7f0d16f7-b172-4c97-94d3-34f0f7ed93cf SystemAssigned 2fcd19a7-ab24-4aef-802b-6851ef5d1ed5</div></div>
<p>In fact, assigning a server identity means creating a system assigned managed identity in the Azure AD tenant that&rsquo;s trusted by the subscription of the instance. To keep things simple, let’s say that System Managed Identity in Azure is like to Managed Account or Group Managed Account on-prem. Those identities are self-managed by the system. Then you need to grant this identity the Azure AD &laquo;&nbsp;Directory Readers &laquo;&nbsp;permission to get rights for creating AAD Users or Groups on behalf of this identity. A PowerShell script is provided by Microsoft <a href="https://docs.microsoft.com/en-us/azure/azure-sql/database/authentication-aad-service-principal-tutorial" rel="noopener" target="_blank">here</a> a sample of code I applied in my context for testing:</p>
<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:650px;height:450px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">...<br />
Try {<br />
&nbsp; &nbsp; $DatabaseName = &quot;test-DBA&quot; &nbsp; <br />
&nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; # Connect to get access to Key Vault info<br />
&nbsp; &nbsp; Connect-AzAccount | Out-Null<br />
<br />
&nbsp; &nbsp; [String]$user = (Get-AzKeyVaultSecret -VaultName $KeyvaultName -Name &quot;AZSQL-SQLBCKAPPID&quot;).SecretValueText<br />
&nbsp; &nbsp; [System.Security.SecureString]$pwd = &nbsp;ConvertTo-SecureString (Get-AzKeyVaultSecret -VaultName $KeyvaultName -Name &quot;AZSQL-SQLBCKAPPSECRET&quot;).SecretValueText -AsPlainText -Force<br />
&nbsp; &nbsp; [String]$SourceServerName = (Get-AzKeyVaultSecret -VaultName $KeyvaultName -Name &quot;AZSQL-NAME&quot;).SecretValueText<br />
&nbsp; &nbsp; [String]$TargetServerName = (Get-AzKeyVaultSecret -VaultName $KeyvaultName -Name &quot;AZSQL-TARGETNAME&quot;).SecretValueText + '.database.windows.net'<br />
<br />
&nbsp; &nbsp; # DB Restore will be performed in the context of dedicated AAD account <br />
&nbsp; &nbsp; $pscredential = New-Object -TypeName System.Management.Automation.PSCredential($user, $pwd)<br />
<br />
&nbsp; &nbsp; $adalPath &nbsp;= &quot;${env:ProgramFiles}\WindowsPowerShell\Modules\Az.Profile.7.0\PreloadAssemblies&quot;<br />
&nbsp; &nbsp; # To install the latest AzureRM.profile version execute &nbsp;-Install-Module -Name AzureRM.profile<br />
&nbsp; &nbsp; $adal &nbsp; &nbsp; &nbsp;= &quot;$adalPath\Microsoft.IdentityModel.Clients.ActiveDirectory.dll&quot;<br />
&nbsp; &nbsp; $adalforms = &quot;$adalPath\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll&quot;<br />
&nbsp; &nbsp; [System.Reflection.Assembly]::LoadFrom($adal) | Out-Null<br />
&nbsp; &nbsp; $resourceAppIdURI = 'https://database.windows.net/'<br />
<br />
&nbsp; &nbsp; # Set Authority to Azure AD Tenant<br />
&nbsp; &nbsp; $authority = 'https://login.windows.net/' + $tenantId<br />
<br />
&nbsp; &nbsp; $ClientCred = [Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential]::new($clientId, $clientSecret)<br />
&nbsp; &nbsp; $authContext = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext]::new($authority)<br />
&nbsp; &nbsp; $authResult = $authContext.AcquireTokenAsync($resourceAppIdURI,$ClientCred)<br />
&nbsp; &nbsp; $Tok = $authResult.Result.CreateAuthorizationHeader()<br />
&nbsp; &nbsp; $Tok=$Tok.Replace(&quot;Bearer &quot;,&quot;&quot;)<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; Write-host &quot;Token generated is ...&quot;<br />
&nbsp; &nbsp; $Tok<br />
&nbsp; &nbsp; Write-host &nbsp;&quot;&quot;<br />
<br />
&nbsp; &nbsp; Write-Host &quot;Create SQL connectionstring&quot;<br />
&nbsp; &nbsp; $conn = New-Object System.Data.SqlClient.SQLConnection <br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; $conn.ConnectionString = &quot;Data Source=$TargetServerName;Initial Catalog=master;Connect Timeout=30&quot;<br />
&nbsp; &nbsp; $conn.AccessToken = $Tok<br />
<br />
&nbsp; &nbsp; Write-host &quot;Connect to database and execute SQL script&quot;<br />
&nbsp; &nbsp; $conn.Open() <br />
<br />
&nbsp; &nbsp; Write-Host &quot;Check connected user ...&quot;<br />
&nbsp; &nbsp; $Query = &quot;SELECT USER_NAME() AS [user_name];&quot;<br />
&nbsp; &nbsp; $command = New-Object -TypeName System.Data.SqlClient.SqlCommand($Query, $conn)<br />
&nbsp; &nbsp; $Command.ExecuteScalar()<br />
&nbsp; &nbsp; $conn.Close()<br />
<br />
&nbsp; &nbsp; Write-Host &quot;Restoring DB:$DatabaseName from Source Server: $SourceServerName to Target Server: $TargetServerName&quot;<br />
<br />
&nbsp; &nbsp; $conn.ConnectionString = &quot;Data Source=$TargetServerName;Initial Catalog=master;Connect Timeout=30&quot;<br />
&nbsp; &nbsp; $conn.AccessToken = $Tok<br />
&nbsp; &nbsp; $conn.Open()<br />
&nbsp; &nbsp; $Query = &quot;DROP DATABASE IF EXISTS [$DatabaseName]; CREATE DATABASE [$DatabaseName] AS COPY OF [$SourceServerName].[$DatabaseName]&quot;<br />
&nbsp; &nbsp; $command = New-Object -TypeName System.Data.SqlClient.SqlCommand($Query, $conn)<br />
&nbsp; &nbsp; $command.CommandTimeout = 1200<br />
&nbsp; &nbsp; $command.ExecuteNonQuery()<br />
&nbsp; &nbsp; $conn.Close()<br />
<br />
&nbsp; &nbsp; # Wait for DB online and ready ... <br />
&nbsp; &nbsp; # Code should be implemented for this check <br />
<br />
&nbsp; &nbsp; <br />
&nbsp; &nbsp; Write-Output &quot;Applying security configuration to DB: $DatabaseName on Server:$TargetServerName&quot;<br />
<br />
&nbsp; &nbsp; $conn.ConnectionString = &quot;Data Source=$TargetServerName;Initial Catalog=$DatabaseName;Connect Timeout=30&quot;<br />
&nbsp; &nbsp; $conn.AccessToken = $Tok<br />
&nbsp; &nbsp; $conn.Open() <br />
&nbsp; &nbsp; $Query = 'CREATE USER [az_sql_ro] FROM EXTERNAL PROVIDER;'<br />
&nbsp; &nbsp; $command = New-Object -TypeName System.Data.SqlClient.SqlCommand($Query, $conn) &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; $command.ExecuteNonQuery()<br />
&nbsp; &nbsp; $conn.Close()<br />
<br />
}<br />
Catch {<br />
&nbsp; &nbsp; Write-Output &quot;Error encountered: $($_.Exception.Message)&quot;<br />
} <br />
...</div></div>
<p>Using service principal required few changes in my case. I now get credentials of the service principal (ClientId and Secret) from Azure Key Vault instead of the AAD dedicated account used in previous example. I also changed the way to connect to SQL Server by relying on ADALSQL to get the access token instead of using dbatools commands. Indeed, as far as I know, dbatools doesn’t support this authentication way (yet?). </p>
<p>The authentication process becomes as follows:</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/08/164-3-new-auth-process-e1596407082747.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/08/164-3-new-auth-process-e1596407082747.jpg" alt="164 - 3 - new auth process" width="800" height="610" class="alignnone size-full wp-image-1647" /></a></p>
<p>My first test seems to be relevant:</p>
<p><a href="http://blog.developpez.com/mikedavem/files/2020/08/164-4-test-with-SP-e1596407153885.jpg"><img src="http://blog.developpez.com/mikedavem/files/2020/08/164-4-test-with-SP-e1596407153885.jpg" alt="164 - 4 - test with SP" width="800" height="301" class="alignnone size-full wp-image-1648" /></a></p>
<p>This improvement looks promise and may cover broader scenarios as the one I described in this blog post. This feature is in preview at the moment of this write-up and I hope to see it coming soon in GA as well as a potential support of preferred PowerShell framework DBAtools <img src="https://blog.developpez.com/mikedavem/wp-includes/images/smilies/icon_smile.gif" alt=":)" class="wp-smiley" /></p>
<p>See you!</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Quand l’économie d’énergie dans le bios ne fait pas bon ménage avec les performances de SQL Server</title>
		<link>https://blog.developpez.com/mikedavem/p10634/sql-server-2012/quand_larsquo_economie_darsquo_energie_d</link>
		<comments>https://blog.developpez.com/mikedavem/p10634/sql-server-2012/quand_larsquo_economie_darsquo_energie_d#comments</comments>
		<pubDate>Sun, 08 Jan 2012 18:40:07 +0000</pubDate>
		<dc:creator><![CDATA[mikedavem]]></dc:creator>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[SQL Server 2012]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Il y a quelques temps j&#8217;avais écrit un billet sur les power plans de Windows et l&#8217;impact que cela pouvait avoir sur SQL Server. Il y a quelques temps j&#8217;ai pu l&#8217;observer chez un de mes clients qui souffraient de &#8230; <a href="https://blog.developpez.com/mikedavem/p10634/sql-server-2012/quand_larsquo_economie_darsquo_energie_d">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p>Il y a quelques temps j&rsquo;avais écrit un <a href="http://blog.developpez.com/mikedavem/p9955/sql-server-2008-r2/gestion-de-larsquo-alimentation-et-impac/">billet</a> sur les power plans de Windows et l&rsquo;impact que cela pouvait avoir sur SQL Server. Il y a quelques temps j&rsquo;ai pu l&rsquo;observer chez un de mes clients qui souffraient de problèmes de performances de requêtes après avoir acheté un nouveau serveur. Voici ce que nous avons pu voir en comparant deux configurations serveurs différentes. </p>
<p> <span id="more-43"></span>
<p>Je précise que seule la configuration des processeurs nous intéresse ici car les performances des requêtes ne dépendaient pas d&rsquo;un manque de mémoire (l&rsquo;ensemble de la base tenait en mémoire avec 130Go de mémoire totale et 10Go de données) ou des performances disques.  Les plans d&rsquo;exécutions utilisés après analyse étaient strictement les mêmes. Cependant une différence au niveau du temps CPU a été relevé (après avoir utilisé les options de statistiques SET STATISTICS IO et SET STATISTICS TIME) Une requête de référence ayant été prise pour comparaison voici les résultats de temps d&rsquo;exécution sur chaque serveur (en désactivant le parallélisme) :</p>
<table border="1" cellspacing="0" cellpadding="2" width="627">
<tbody>
<tr>
<td valign="top" width="133">Serveurs</td>
<td valign="top" width="295">Processeurs</td>
<td valign="top" width="197">Temps CPU requête</td>
</tr>
<tr>
<td valign="top" width="133">Serveur 1</td>
<td valign="top" width="295">Processeurs Intel Xeon X7460 @2.66GHz</td>
<td valign="top" width="197">12344 ms</td>
</tr>
<tr>
<td valign="top" width="133">Serveur 2</td>
<td valign="top" width="295">Processeurs Intel Xeon X7560 @2.27Ghz</td>
<td valign="top" width="197">21892 ms</td>
</tr>
</tbody>
</table>
<p>En regardant sur le site du constructeur on peut vite remarquer qu&rsquo;à priori les processeurs (Intel Xeon X7560) devraient donner des résultats au moins similaires aux processeurs (Intel X7460) voir plus (en utilisant l&rsquo;outil de comparaison du site).</p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Quandlconomiednergiedanslebiosnefaitpasb_9623/image_2.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Quandlconomiednergiedanslebiosnefaitpasb_9623/image_thumb.png" width="433" height="401" /></a> </p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Quandlconomiednergiedanslebiosnefaitpasb_9623/image_4.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Quandlconomiednergiedanslebiosnefaitpasb_9623/image_thumb_1.png" width="430" height="316" /></a> </p>
</p>
<p>Le temps CPU multiplié pratiquement par deux m&rsquo;a donc incité à utiliser l&rsquo;outil CPU-Z pour voir les caractéristiques réelles des processeurs sur chaque serveur.</p>
<p> </p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Quandlconomiednergiedanslebiosnefaitpasb_9623/icon_arrow_2.gif"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="icon_arrow" border="0" alt="icon_arrow" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Quandlconomiednergiedanslebiosnefaitpasb_9623/icon_arrow_thumb.gif" width="15" height="15" /></a> Sur le serveur 1 : <u>Intel Xeon X7460 @ 2.66Ghz</u></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Quandlconomiednergiedanslebiosnefaitpasb_9623/image_6.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Quandlconomiednergiedanslebiosnefaitpasb_9623/image_thumb_2.png" width="419" height="409" /></a> </p>
<p> </p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Quandlconomiednergiedanslebiosnefaitpasb_9623/icon_arrow%5B4%5D.gif"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="icon_arrow[4]" border="0" alt="icon_arrow[4]" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Quandlconomiednergiedanslebiosnefaitpasb_9623/icon_arrow%5B4%5D_thumb.gif" width="15" height="15" /></a> Sur le serveur 2 : <u>Intel Xeon X7560 @ 2.27Ghz</u></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Quandlconomiednergiedanslebiosnefaitpasb_9623/image_8.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Quandlconomiednergiedanslebiosnefaitpasb_9623/image_thumb_3.png" width="415" height="399" /></a> </p>
</p>
<p>On remarque rapidement que la vitesse réelle des processeurs n&rsquo;est pas celle annoncée sur les spécifications du constructeur pour le serveur 2. Le ratio est donc de 2.27Ghz / 1.064Ghz soit environ 2. Curieusement on observe le même ratio entre la vitesse des requêtes et la vitesse des processeurs. La modification des power plans pour les performances n&rsquo;avait visiblement rien changé. Après avoir investigué dans le BIOS on a pu voir que les options d&rsquo;économies étaient activés. Après avoir réglé ce souci nous nous sommes retrouvés sur des temps d&rsquo;exécutions plus bas pour le serveur 2 (de l&rsquo;ordre de 10 000 ms). Enjoy !!</p>
<p>David BARBARIN (Mikedavem)    <br />MVP SQL Server</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Locked pages in memory, Instant File Initialization et SID de service SQL</title>
		<link>https://blog.developpez.com/mikedavem/p10514/sql-server-2012/locked_pages_in_memory_instant_file_init</link>
		<comments>https://blog.developpez.com/mikedavem/p10514/sql-server-2012/locked_pages_in_memory_instant_file_init#comments</comments>
		<pubDate>Thu, 17 Nov 2011 21:52:29 +0000</pubDate>
		<dc:creator><![CDATA[mikedavem]]></dc:creator>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[SQL Server 2012]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Locked pages in memory et Instant File Initialization sont deux options de performance utilisés fréquemment avec SQL Server. Pour rappel la première option d&#8217;empêcher la pagination du buffer pool de SQL Server par le système d&#8217;exploitation et la deuxième permet &#8230; <a href="https://blog.developpez.com/mikedavem/p10514/sql-server-2012/locked_pages_in_memory_instant_file_init">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p><font size="2">Locked pages in memory et Instant File Initialization sont deux options de performance utilisés fréquemment avec SQL Server. Pour rappel la première option d&rsquo;empêcher la pagination du buffer pool de SQL Server par le système d&rsquo;exploitation et la deuxième permet de réduire considérablement les temps d&rsquo;allocation de fichier en supprimant la phase de remplissage de zéro des fichiers de bases de données. Ceci n&rsquo;est valable uniquement que pour les fichiers de données SQL Server. Pour bénéficier de ces deux options de performances  il faut octroyer au compte de service SQL Server les privilèges nécessaires. Cependant dans beaucoup de littérature et très certainement par habitude, c&rsquo;est le compte de domaine utilisé par le service SQL qui bénéficie de ces privilèges dans la plupart des cas. Mais qu&rsquo;en est-il avec l&rsquo;apparition des SID de service ?</font></p>
<p><span id="more-42"></span><br />
<font size="2"></font>
<p><font size="2">Avant la version 2008 de SQL Server on avait gère le choix entre pouvoir octroyer ces droits soit au compte de domaine ou au groupe prédéfini créé pendant l&rsquo;installation d&rsquo;une installation SQL Server de la forme <em>SQLServerMSSQLUser$[servername]$MSSQLServer</em> pour une instance par défaut. Dans le premier cas lorsque l&rsquo;on change de compte utilisateur il faudra penser à paramétrer le nouveau compte utilisé avec les privilèges nécessaires. Dans l&rsquo;autre cas cette gymnastique n&rsquo;est plus nécessaire mais par définition l&rsquo;ensemble des membres potentiels du groupe concerné pourront bénéficier de ce privilège. Cela peut être bien entendu problématique. </font></p>
<p><font size="2">Depuis SQL Server 2008 (et Windows 2008) on a vu l&rsquo;apparition des SID de services qui permettent de créer une identité propre de service. L&rsquo;avantage bien entendu est qu&rsquo;il est possible d&rsquo;utiliser directement cet identifiant pour isoler les ressources utilisées par une instance SQL. Une autre utilisation moins répandue est d&rsquo;octroyer les privilèges cités plus hauts directement à l&rsquo;identité du service SQL d&rsquo;une instance donnée. </font></p>
<p><font size="2">Pour savoir si cela fonctionne nous avons selon l&rsquo;option de performance des outils à disposition. Pour le verrouillage de page en mémoire il suffit d&rsquo;utiliser la commande DBCC MEMORYSTATUS alors que pour <em>Instant File Initialization</em> on pourra utiliser les traces flag 3004 et 3605. L&rsquo;activation de ces traces flag permettent de pouvoir lire les informations d&rsquo;initialisation de fichier directement dans le journal d&rsquo;erreur SQL.</font></p>
<p> 
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Lockedpagesinmemoryinstantfileinitializa_11968/icon_arrow_2.gif"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="icon_arrow" border="0" alt="icon_arrow" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Lockedpagesinmemoryinstantfileinitializa_11968/icon_arrow_thumb.gif" width="15" height="15" /></font></a><font size="2"> Test 1 : compte de domaine utilisateur classique sans privilèges</font></p>
<p><font size="2">Pour les pages verrouillés en mémoire :</font></p>
<p><font size="2"> </font><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Lockedpagesinmemoryinstantfileinitializa_11968/image_2.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Lockedpagesinmemoryinstantfileinitializa_11968/image_thumb.png" width="237" height="172" /></font></a><font size="2"> </font></p>
<p><font size="2"></font></p>
<p><font size="2">Pour Instant File Initialization :</font></p>
<blockquote><p><strong><font size="2">USE [master]         <br />GO </font></strong></p>
<p><strong><font size="2">CREATE DATABASE [TEST] ON  PRIMARY          <br />( NAME = N&rsquo;TEST&rsquo;, FILENAME = N&rsquo;C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\TEST.mdf&rsquo; , SIZE = 2048MB , MAXSIZE = UNLIMITED)          <br /> LOG ON           <br />( NAME = N&rsquo;TEST_log&rsquo;, FILENAME = N&rsquo;C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\TEST_log.LDF&rsquo; , SIZE = 100MB , MAXSIZE = 2048GB)          <br />GO</font></strong></p>
</blockquote>
<p><font size="2">L&rsquo;idée ici est de créer une base de données et de visualiser ensuite les événements générés dans le journal d&rsquo;erreur SQL. On peut voir avec l&rsquo;activation des traces flag 3004 et 3605 dans les options de démarrage de démarrage du service SQL (-T3004; -T3605) les informations relatives à l&rsquo;initialisation des fichiers et de leur remplissage par des 0 (Zeroing &#8230;). On constate que les 2 types de fichiers sont concernés dans ce premier cas.</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Lockedpagesinmemoryinstantfileinitializa_11968/image_4.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Lockedpagesinmemoryinstantfileinitializa_11968/image_thumb_1.png" width="906" height="151" /></font></a><font size="2"> </font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p> 
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Lockedpagesinmemoryinstantfileinitializa_11968/icon_arrow_2.gif"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="icon_arrow" border="0" alt="icon_arrow" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Lockedpagesinmemoryinstantfileinitializa_11968/icon_arrow_thumb.gif" width="15" height="15" /></font></a><font size="2"> Test 2 : paramétrage du SID de service SQL avec les privilèges nécessaires pour l&rsquo;activation de nos 2 options de performances. Pour la prise en compte des deux options il faudra redémarrer l&rsquo;instance SQL Server.</font></p>
<p><font size="2"><u>Pour le verrouillage de pages en mémoire</u> :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Lockedpagesinmemoryinstantfileinitializa_11968/image_6.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Lockedpagesinmemoryinstantfileinitializa_11968/image_thumb_2.png" width="577" height="303" /></font></a><font size="2"> </font></p>
<p><font size="2">On vérifie à nouveau avec DBCC MEMORYSTATUS :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Lockedpagesinmemoryinstantfileinitializa_11968/image_12.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Lockedpagesinmemoryinstantfileinitializa_11968/image_thumb_5.png" width="240" height="187" /></font></a><font size="2"> </font></p>
<p><font size="2"></font></p>
<p><font size="2"><u>Pour Instant File Initialization</u> :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Lockedpagesinmemoryinstantfileinitializa_11968/image_10.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Lockedpagesinmemoryinstantfileinitializa_11968/image_thumb_4.png" width="575" height="299" /></font></a><font size="2"> </font></p>
<p><font size="2">On recrée ensuite notre base de données avec le même script que précédemment :</font></p>
<blockquote><p><strong><font size="2">USE [master]         <br />GO </font></strong></p>
<p><strong><font size="2">DROP DATABASE [TEST]         <br />GO </font></strong></p>
<p><strong><font size="2">CREATE DATABASE [TEST] ON  PRIMARY          <br />( NAME = N&rsquo;TEST&rsquo;, FILENAME = N&rsquo;C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\TEST.mdf&rsquo; , SIZE = 2048MB , MAXSIZE = UNLIMITED)          <br /> LOG ON           <br />( NAME = N&rsquo;TEST_log&rsquo;, FILENAME = N&rsquo;C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\TEST_log.LDF&rsquo; , SIZE = 100MB , MAXSIZE = 2048GB)          <br />GO</font></strong></p>
</blockquote>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2">Vous remarquerez au passage que le temps de création de la base a nettement diminué (du moins c&rsquo;est le cas chez moi). On vérifie à nouveau dans le journal d&rsquo;erreur SQL :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Lockedpagesinmemoryinstantfileinitializa_11968/image_14.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Lockedpagesinmemoryinstantfileinitializa_11968/image_thumb_6.png" width="838" height="108" /></font></a><font size="2"> </font></p>
<p><font size="2">On constate cette fois que seul le fichier journal est concerné (ce qui est normal comme expliqué plus haut).</font></p>
<p><font size="2">CQFD !!!</font></p>
<p><font size="2">David BARBARIN (Mikedavem)      <br />MVP SQL Server</font></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>SQL Cluster : Comment paramétrer le nom des 2 nœuds en majuscule !</title>
		<link>https://blog.developpez.com/mikedavem/p10307/sql-server-2005/architecture/sql_cluster_comment_parametrer_le_nom_de</link>
		<comments>https://blog.developpez.com/mikedavem/p10307/sql-server-2005/architecture/sql_cluster_comment_parametrer_le_nom_de#comments</comments>
		<pubDate>Wed, 21 Sep 2011 21:41:27 +0000</pubDate>
		<dc:creator><![CDATA[mikedavem]]></dc:creator>
				<category><![CDATA[Architecture]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Ce post s&#8217;adresse surtout aux maniaques du travail bien fait . Lorsque l&#8217;on configure un cluster Windows il arrive bien souvent que le nom d&#8217;un des 2 noeuds soit en minuscule. Bien que cela n&#8217;ait pas d&#8217;incidence (enfin il y &#8230; <a href="https://blog.developpez.com/mikedavem/p10307/sql-server-2005/architecture/sql_cluster_comment_parametrer_le_nom_de">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p><font size="2">Ce post s&rsquo;adresse surtout aux maniaques du travail bien fait <img src="https://blog.developpez.com/mikedavem/wp-includes/images/smilies/icon_smile.gif" alt=":-)" class="wp-smiley" /> . Lorsque l&rsquo;on configure un cluster Windows il arrive bien souvent que le nom d&rsquo;un des 2 noeuds soit en minuscule. Bien que cela n&rsquo;ait pas d&rsquo;incidence (enfin il y avait bien quelques cas où cela posait problème comme par exemple l&rsquo;installation du service pack 3 de SQL Server 2005 en cluster) on peut vouloir avoir nos deux noms en majuscule dans la console de gestion du cluster.</font></p>
<p><span id="more-41"></span></p>
<p><font size="2">Plaçons le décor :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLClusterCommentparamtrerlenomdes2nudse_139B9/image_2.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLClusterCommentparamtrerlenomdes2nudse_139B9/image_thumb.png" width="219" height="183" /></font></a><font size="2"> </font></p>
<p><font size="2">Le nom du deuxième noeud est effectivement en minuscule. Pourtant après avoir ajouter plusieurs fois le nom du deuxième noeud en majuscule rien ne change !!</font></p>
<p><font size="2">Avant :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLClusterCommentparamtrerlenomdes2nudse_139B9/image_4.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLClusterCommentparamtrerlenomdes2nudse_139B9/image_thumb_1.png" width="494" height="210" /></font></a><font size="2"> </font></p>
<p><font size="2"></font></p>
<p><font size="2">Après :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLClusterCommentparamtrerlenomdes2nudse_139B9/image_6.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLClusterCommentparamtrerlenomdes2nudse_139B9/image_thumb_2.png" width="494" height="202" /></font></a><font size="2"> </font></p>
<p><font size="2">Après avoir vérifié tout ce qu&rsquo;il était possible de vérifier (entrée DNS, objet d&rsquo;ordinateur, attributs de l&rsquo;objet d&rsquo;ordinateur etc &#8230;) le constat était le suivant : tous les noms associés au nom du deuxième noeud était bien en majuscule. Alors comment faire pour entrer le nom de ce dernier en majuscule ? </font></p>
<p><font size="2">Il faut tout simplement entrer le nom par l&rsquo;utilitaire en ligne de commande <em>cluster.exe</em> de la façon suivante :</font></p>
<blockquote><p><font size="2">> <strong>cluster.exe /cluster : [clustername] /addnode /node: [nodename]</strong></font></p>
</blockquote>
<p><font size="2"></font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLClusterCommentparamtrerlenomdes2nudse_139B9/image_8.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLClusterCommentparamtrerlenomdes2nudse_139B9/image_thumb_3.png" width="589" height="214" /></font></a><font size="2"> </font></p>
<p><font size="2">Le résultat est le suivant :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLClusterCommentparamtrerlenomdes2nudse_139B9/image_10.png"><font size="2"><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLClusterCommentparamtrerlenomdes2nudse_139B9/image_10.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLClusterCommentparamtrerlenomdes2nudse_139B9/image_thumb_4.png" width="200" height="161" /></a></font></a><font size="2"> </font></p>
<p><font size="2">Et voilà le travail !!</font></p>
<p><font size="2">David BARBARIN (Mikedavem)      <br />MVP SQL Server</font></p>
<p><font size="2"> </font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SQL Server agent : Propriétaires de travaux, comptes de service et proxies</title>
		<link>https://blog.developpez.com/mikedavem/p10290/sql-server-2005/architecture/sql_server_agent_proprietaire_de_travaux</link>
		<comments>https://blog.developpez.com/mikedavem/p10290/sql-server-2005/architecture/sql_server_agent_proprietaire_de_travaux#comments</comments>
		<pubDate>Thu, 15 Sep 2011 19:56:45 +0000</pubDate>
		<dc:creator><![CDATA[mikedavem]]></dc:creator>
				<category><![CDATA[Architecture]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[J&#8217;ai déjà eu plusieurs fois à répondre à la question suivante : Sous quel contexte utilisateur s&#8217;exécute un travail SQL Server ? Est ce qu&#8217;il utilise le compte de service du compte de l&#8217;agent ou bien le contexte de son &#8230; <a href="https://blog.developpez.com/mikedavem/p10290/sql-server-2005/architecture/sql_server_agent_proprietaire_de_travaux">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p><font size="2">J&rsquo;ai déjà eu plusieurs fois à répondre à la question suivante : Sous quel contexte utilisateur s&rsquo;exécute un travail SQL Server ? Est ce qu&rsquo;il utilise le compte de service du compte de l&rsquo;agent ou bien le contexte de son propriétaire ? </font></p>
<p><span id="more-40"></span></p>
<p><font size="2">La réponse est : cela dépend du contexte  (cela aurait été trop facile sinon :D). </font></p>
<p><font size="2">Prenons un exemple simple pour bien comprendre :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeragentPropritairedetravauxcompte_11C34/icon_arrow_2.gif"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="icon_arrow" border="0" alt="icon_arrow" src="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeragentPropritairedetravauxcompte_11C34/icon_arrow_thumb.gif" width="15" height="15" /></font></a><font size="2"> Un job SQL Server avec comme propriétaire un compte de connexion <em>test_login</em> sans privilèges particuliers. Le job en question est constitué de 2 étapes :</font></p>
<ul>
<li><font size="2">une étape TSQL avec la requête SELECT SYSTEM_USER</font></li>
<li><font size="2">une étape de type <em>cmdExec</em> qui exécute un <em>&laquo;&nbsp;dir E:\*.*&nbsp;&raquo;</em></font></li>
</ul>
<p><font size="2">On exécute le job et on peut faire le constat suivant :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeragentPropritairedetravauxcompte_11C34/image_2.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeragentPropritairedetravauxcompte_11C34/image_thumb.png" width="995" height="54" /></font></a><font size="2"> </font></p>
<p><font size="2">Le job s&rsquo;exécute dans le contexte du propriétaire du job (<em>Executed as user:test_login</em>). Cependant la 2ème étape est en erreur mais cela reste logique. En effet pour exécuter une commande de type <em>cmdExec</em> il faut soit faire partir du rôle <em>sysadmin</em> soit utiliser un compte de proxy qui est autorisé à exécuter une telle commande.</font></p>
<p> </p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeragentPropritairedetravauxcompte_11C34/icon_arrow_2.gif"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="icon_arrow" border="0" alt="icon_arrow" src="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeragentPropritairedetravauxcompte_11C34/icon_arrow_thumb.gif" width="15" height="15" /></font></a><font size="2"> Nous allons donc dans ce 2ème test ajouter le compte de connexion <em>test_user</em> au rôle de serveur <em>sysadmin</em> et exécuter à nouveau notre job. Voici le résultat :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeragentPropritairedetravauxcompte_11C34/image_4.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeragentPropritairedetravauxcompte_11C34/image_thumb_1.png" width="1009" height="63" /></font></a><font size="2"> </font></p>
<p><font size="2">Cette fois-ci le job s&rsquo;est exécuté sans problème mais le plus intéressant est le contexte qui a été utilisé ici. L&rsquo;utilisateur concerné est en réalité le compte de service de l&rsquo;agent SQL Server (TESTLLAB2\sql2k8svcaccount). Comme vous le savez sans doute ce compte de service est <em>sysadmin</em> sur l&rsquo;instance SQL Server. Donc quand un compte de connexion est <em>sysadmin</em> c&rsquo;est le compte de service de l&rsquo;agent SQL qui est utilisé.</font></p>
<p><font size="2"></font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeragentPropritairedetravauxcompte_11C34/icon_arrow_2.gif"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="icon_arrow" border="0" alt="icon_arrow" src="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeragentPropritairedetravauxcompte_11C34/icon_arrow_thumb.gif" width="15" height="15" /></font></a><font size="2"> Bien entendu on ne peut pas élever les privilèges d&rsquo;un compte de connexion uniquement pour qu&rsquo;il puisse exécuter une commande de type <em>cmdExec</em>. Une bonne pratique est d&rsquo;utiliser un compte de proxy associé à un credential qui sera autorisé à initier des ordres de type <em>cmdExec</em>. C&rsquo;est ce que nous allons faire ici. Après exécution du job voici le résultat :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeragentPropritairedetravauxcompte_11C34/image_6.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeragentPropritairedetravauxcompte_11C34/image_thumb_2.png" width="1024" height="73" /></font></a><font size="2"> </font></p>
<p><font size="2">On peut remarquer que le contexte utilisé lors de la 1ère étape est bien celui attendu : <em>test_login</em> qui est, pour rappel, le propriétaire de notre job. Comme nous avons paramétré un compte de proxy (<em>TESTLLAB2\proxycmdexec</em>)pour l&rsquo;exécution de la 2ème étape c&rsquo;est le contexte de ce dernier qui est utilisé. </font></p>
<p><font size="2">Voilà pour la démonstration !!</font></p>
<p><font size="2">David BARBARIN (Mikedavem)      <br />MVP SQL Server</font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2"> </font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SQL Server et Service SID</title>
		<link>https://blog.developpez.com/mikedavem/p10214/sql-server-2012/sql_server_et_service_sid</link>
		<comments>https://blog.developpez.com/mikedavem/p10214/sql-server-2012/sql_server_et_service_sid#comments</comments>
		<pubDate>Mon, 22 Aug 2011 14:30:54 +0000</pubDate>
		<dc:creator><![CDATA[mikedavem]]></dc:creator>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[SQL Server 2012]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Le modèle de sécurité de SQL Server a évolué au cours des différentes versions. Chaque version de SQL Server a amené son lot de nouveautés en même temps que les versions de Windows sur lequel il repose. Avec la venue &#8230; <a href="https://blog.developpez.com/mikedavem/p10214/sql-server-2012/sql_server_et_service_sid">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p><font size="2">Le modèle de sécurité de SQL Server a évolué au cours des différentes versions. Chaque version de SQL Server a amené son lot de nouveautés en même temps que les versions de Windows sur lequel il repose. Avec la venue de Windows Vista, Seven ou Server 2008 il est apparu la notion de SID pour les services Windows. Depuis SQL Server 2008 il est possible d&rsquo;utiliser cette fonctionnalité offerte par les nouvelles versions du système d&rsquo;exploitation. Mais qu&rsquo;est ce que un SID exactement ? A quoi cela peut bien servir ? Quels sont les avantages ? </font></p>
<p><span id="more-39"></span><br />
<font size="2"></font>
<p><font size="2">Faisons tout d&rsquo;abord un bon en arrière avec les version 2000 et 2005 de SQL Server. Avec SQL Server 2000 le paramétrage d&rsquo;un compte de service pour SQL Server donnait lieu à la création d&rsquo;un login SQL correspondant appartenant au rôle de serveur <em>sysadmin</em>. Les droits nécessaires sont octroyés comme <em>Log On As A Service</em>, <em>Replace a Process-Level Token</em> etc&#8230; mais ce n&rsquo;est pas le cas des ACL des fichiers et dossiers. SQL Server 2005 apporte une réponse ce problème en introduisant des groupes prédéfinis créés lors de l&rsquo;instance (<em>SQLServer2005MSSQLUser$InstanceName</em> pour le moteur SQL Server par exemple). Ces groupes permettent une administration beaucoup plus simple des comptes de services. Les ACL sont configurées directement pour les groupes concernés. Le compte de service concerné n&rsquo;est plus qu&rsquo;à être ajouté en tant que membre du groupe Windows adéquate. </font></p>
<p><font size="2">Mais dans les deux cas, c&rsquo;est le contexte de sécurité du compte ou du groupe Windows qui est utilisé, ce qui ne permet pas une isolation complète des ressources dédiés à une instance SQL Server. En effet imaginez qu&rsquo;une instance SQL Server utilise un compte de service A et que plusieurs autres applications utilisent ce même compte de service A. Cela signifierait que ces dernières pourraient donc avoir accès aux ressources de notre instance SQL Server. Prenons un 2ème exemple où deux instances SQL Server cohabitent sur le même serveur. Si ces deux instances utilisent le même compte de service cela signifie qu&rsquo;elles ont accès mutuellement à leurs ressources. Dans un contexte de sécurisation ce n&rsquo;est pas vraiment ce que l&rsquo;on veut. </font></p>
<p><font size="2">Dans le schéma qui suit les flèches rouges représentent les accès potentiels non désirés. On voit ici que l&rsquo;utilisation d&rsquo;un même compte de domaine en tant que compte de service par différentes applications comme SQL Server ou des applications métiers peut vite engendrer des problèmes d&rsquo;accès aux ressources. Chaque application peut avoir accès aux ressources des autres. Hors tout le monde le sait, beaucoup d&rsquo;attaques se produisent par le biais des services Windows.</font></p>
<p><font size="2"></font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeretServiceSID_E9D0/image_2.png"><font size="2"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeretServiceSID_E9D0/image_thumb.png" width="301" height="415" /></font></a><font size="2"> </font></p>
<p><font size="2">Comment régler ce problème ? Au final, nous voulons que seul le service d&rsquo;une application ait accès à ses ressources. Nous ne voulons pas utiliser de contexte utilisateur mais un contexte &laquo;&nbsp;de service&nbsp;&raquo;. C&rsquo;est ce que propose Microsoft à partir des versions Windows Vista, Seven ou Server 2008. Chaque service possède son propre (et unique) identifiant appelé SID. C&rsquo;est cet identifiant qui sera directement utilisé pour paramétrer les différentes ACL des ressources concernées. L&rsquo;avantage est ici est certain : on n&rsquo;utilise plus l&rsquo;identifiant du compte utilisateur mais l&rsquo;identifiant du service pour avoir accès aux ressources. L&rsquo;utilisation d&rsquo;un compte utilisateur pour deux instances SQL Server ne posera plus de problème dans ce cas car au final c&rsquo;est le SID des différents services SQL Server qui seront utilisés pour avoir accès aux ressources nécessaires. Il en va de même pour les services concernant le moteur SQL et l&rsquo;agent SQL. </font></p>
<p><font size="2">Sous SQL Server cela se traduit par l&rsquo;appariation de deux comptes de connexion (pour une instance par défaut) : </font></p>
<ul>
<li><font size="2">NT SERVICE\MSSQLSERVER pour le moteur SQL</font> </li>
<li><font size="2">NT SERVICE\SQLSERVERAGENT pour l&rsquo;agent SQL Server</font> </li>
</ul>
<p><font size="2">Ces 2 comptes font parti du rôle de serveur <em>sysadmin</em>.</font></p>
<p><font size="2"></font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeretServiceSID_E9D0/image_8.png"><font size="2"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeretServiceSID_E9D0/image_thumb_3.png" width="229" height="125" /></font></a><font size="2"> </font></p>
<p><font size="2">On retrouve notre SID dans les ACL (A noter aussi que l&rsquo;on peut retrouver le groupe Windows <em>SQLServer2005MSSQLUser</em> créé par SQL Server lors de l&rsquo;installation. Le SID fait automatiquement parti de ce groupe) :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeretServiceSID_E9D0/image_12.png"><font size="2"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeretServiceSID_E9D0/image_thumb_5.png" width="275" height="155" /></font></a><font size="2"> </font></p>
<p><font size="2"></font></p>
<p><font size="2">Pour une instance nommée :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeretServiceSID_E9D0/image_10.png"><font size="2"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/SQLServeretServiceSID_E9D0/image_thumb_4.png" width="250" height="103" /></font></a><font size="2"> </font></p>
<p><font size="2"></font></p>
<p><font size="2">A noter que :</font></p>
<ul>
<li><font size="2">Il n&rsquo;existe plus de compte de connexion associé aux groupes Windows du type <i>SQLServerMSSQLUser$&#8230;</i> alors que dans la version 2005 ceux-ci étaient présents et faisaient parti du rôle de serveur sysadmin</font> </li>
<li><font size="2">Tous les services qui concernent de près ou de loin SQL Server ne peuvent pas utiliser de SID. Par exemple avec SQL Server 2008 et 2008 R2 le service SQL Writer n&rsquo;utilise pas de SID. Avec SQL Server Denali (CTP3) il semblerait que ceci ne soit plus vrai. On peut facilement le vérifier avec la commande suivante :</font> </li>
</ul>
<blockquote><p><font size="2">       <strong>sc qsidtype [servicename]</strong></font></p>
</blockquote>
<p><font size="2">       La sortie de cette commande peut donner les résultats suivants :</font></p>
<ul>
<li><font size="2">None (0x0) &#8211; the service will not have a per-service SID.  This is the default configuration for a service </font></li>
<li><font size="2">Unrestricted (0x1) &#8211; the service has a per-service SID </font></li>
<li><font size="2">Restricted (0x3) &#8211; the service has a per-service SID <u>and</u> a <em>write-restricted token</em>. </font></li>
</ul>
<p><font size="2"></font></p>
<p><font size="2">L&rsquo;utilisation des SID pour les services SQL permettent une délimitation et une isolation beaucoup plus marquée des ressources propres à chaque instance SQL Server. Encore un grand pas en avant dans le domaine de la sécurité ! </font></p>
<p><font size="2">David BARBARIN (Mikedavem)      <br />MVP SQL Server</font></p>
<p><font size="2"> </font></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fragmentation des indexes et fragments : quesako ?</title>
		<link>https://blog.developpez.com/mikedavem/p10152/sql-server-2005/architecture/fragmentation_des_indexes_et_fragments_q</link>
		<comments>https://blog.developpez.com/mikedavem/p10152/sql-server-2005/architecture/fragmentation_des_indexes_et_fragments_q#comments</comments>
		<pubDate>Mon, 25 Jul 2011 20:56:50 +0000</pubDate>
		<dc:creator><![CDATA[mikedavem]]></dc:creator>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Pour ceux qui utilisent la DMV sys.dm_db_index_physical_stats depuis la version 2005 de SQL Server ont certainement vu une colonne nommée fragment_count. La documentation Microsoft nous donne la description suivante : Nombre de fragments dans le niveau feuille d&#8217;une unité d&#8217;allocation &#8230; <a href="https://blog.developpez.com/mikedavem/p10152/sql-server-2005/architecture/fragmentation_des_indexes_et_fragments_q">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p><font size="2">Pour ceux qui utilisent la DMV <em>sys.dm_db_index_physical_stats</em> depuis la version 2005 de SQL Server ont certainement vu une colonne nommée fragment_count. La documentation Microsoft nous donne la description suivante : <em>Nombre de fragments dans le niveau feuille d&rsquo;une unité d&rsquo;allocation IN_ROW_DATA. </em>J&rsquo;ai déjà eu pas mal de questions à ce sujet car même avec la description fournie nous pouvons avoir du mal à visualiser ce que cette colonne représente exactement et quelle peut être la relation avec la fragmentation des indexes.</font></p>
<p><span id="more-13"></span></p>
<p><font size="2">Tout d&rsquo;abord une définition :  un fragment est en réalité une séquence de pages physique contigüe. Un index possède au minimum un fragment et il peut y avoir autant de fragments que de pages dans le pire des scénarios. Pourquoi le pire des scénarios ? Nous le découvrirons un peu plus tard mais pour le moment nous prendrons l&rsquo;exemple suivant :</font></p>
<blockquote><p><strong><font size="2">CREATE TABLE T          <br />(           <br />id INT IDENTITY(1,1),           <br />texte VARCHAR(100)           <br />); </font></strong></p>
<p><strong><font size="2">&#8211; Index cluster          <br />CREATE UNIQUE CLUSTERED INDEX PK_id           <br />ON T           <br />(           <br />id           <br />); </font></strong></p>
<p><strong><font size="2">INSERT T (texte) VALUES (REPLICATE(&lsquo;T&rsquo;, 100))          <br />GO 100</font></strong></p>
</blockquote>
<p><font size="2">Nous avons donc une table relativement simple avec un index cluster. On remplit la table avec 100 lignes de données ce qui représente environ 10Ko à la louche &#8230; autant dire que notre index ne sera composé que de quelques pages. Vérifions le :</font></p>
<blockquote><p><strong><font size="2">SELECT          <br />OBJECT_NAME(object_id) AS [object_name],           <br />index_id,           <br />avg_fragmentation_in_percent,           <br />avg_page_space_used_in_percent,           <br />avg_fragment_size_in_pages AS page_per_fragment,           <br />fragment_count,           <br />page_count,           <br />record_count           <br />FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID(&lsquo;T&rsquo;), NULL, NULL, &lsquo;DETAILED&rsquo;);</font></strong></p>
</blockquote>
<p><font size="2">&#8230; qui donne </font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Fragmentationdesindexesettablesfaiblevol_91CC/image_2.png"><font size="2"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Fragmentationdesindexesettablesfaiblevol_91CC/image_thumb.png" width="772" height="51" /></font></a><font size="2"> </font></p>
<p><font size="2">Effectivement nous avons trois pages dont une page racine et deux pages de niveau feuille qui composent l&rsquo;index cluster (index_id = 1). J&rsquo;ai également ajouté les colonnes <em>fragment_count</em> et <em>avg_fragment_size_in_pages</em>. On constate que pour le niveau feuille il existe deux fragments pour deux pages et que la fragmentation de l&rsquo;index est de 50%. Si deux fragments existent cela signifie que les pages composants le niveau feuille ne se suivent pas physiquement. On peut le vérifier avec la commande DBCC IND. Ici nous nous intéresserons uniquement qu&rsquo;au niveau feuille de l&rsquo;index.</font></p>
<blockquote><p><strong><font size="2">CREATE TABLE T_DBCC_IND          <br />(           <br />ID INT IDENTITY(1,1) PRIMARY KEY,           <br />PageFID TINYINT,           <br />PagePID INT,           <br />IAMFID TINYINT,           <br />IAMPID INT,           <br />ObjectID INT,           <br />IndexID TINYINT,           <br />PartitionNumber TINYINT,           <br />PartitionID BIGINT,           <br />iam_chain_type VARCHAR(30),           <br />PageType TINYINT,           <br />IndexLevel TINYINT,           <br />NextPageFID TINYINT,           <br />NextPagePID INT,           <br />PrevPageFID TINYINT,           <br />PrevPagePID INT           <br />);           <br />INSERT INTO T_DBCC_IND           <br />EXEC (&lsquo;DBCC IND (test, T, 1)&rsquo;);  </font></strong></p>
<p><strong><font size="2">SELECT *          <br />FROM T_DBCC_IND           <br />WHERE IndexLevel IS NOT NULL;</font></strong></p>
</blockquote>
<p><font size="2">&#8230; qui donne le plan d&rsquo;allocation de pages suivant :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Fragmentationdesindexesettablesfaiblevol_91CC/image_8.png"><font size="2"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Fragmentationdesindexesettablesfaiblevol_91CC/image_thumb_3.png" width="968" height="77" /></font></a><font size="2"> </font></p>
<p><font size="2">On constate effectivement que les numéros de pages ne se suivent pas. On peut le représenter schématiquement de la manière suivante :</font></p>
<p><font size="2"> </font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Fragmentationdesindexesettablesfaiblevol_91CC/image_10.png"><font size="2"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Fragmentationdesindexesettablesfaiblevol_91CC/image_thumb_4.png" width="438" height="290" /></font></a><font size="2"> </font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2">Quelle est le lien avec la fragmentation ? Définissons d&rsquo;abord ce qu&rsquo;est la fragmentation. La documentation en ligne donne l&rsquo;explication suivante concernant la fragmentation logique : </font></p>
<blockquote><p><font size="2"><em>Pourcentage de pages hors service dans les pages de feuilles d&rsquo;un index.</em> <em>Une page non ordonnée est une page pour laquelle la page physique suivante allouée à l&rsquo;index n&rsquo;est pas la page désignée par le pointeur de page suivante dans la page feuille actuelle</em>. </font></p>
</blockquote>
<p><font size="2">Autant dire que cette explication reste plutôt abscons &#8230; Je préfère la définition suivante : </font></p>
<blockquote><p><font size="2">La fragmentation logique représente le nombre de pages dont la page suivante annoncée dans la page IAM (donc le plan d&rsquo;allocation) ne se trouve pas dans la même portion de fragment.</font></p>
</blockquote>
<p><font size="2">Nous sommes bien dans ce cas car la page 201 ne se trouve pas dans le même fragment que la page 206. Pour aller de la page 201 à 206 nous devons passer sur un fragment différent pour un lire un total de deux pages. Nous pouvons en déduire la formule suivante :</font></p>
<blockquote><p><strong><font size="2">Fragmentation = (Nb de fragments &#8211; 1) / Nb de pages </font></strong></p>
</blockquote>
<p><font size="2">Dans notre cas nous aurons :</font></p>
<blockquote><p><font size="2">Fragmentation = (2 &#8211; 1) / 2 = 50%</font></p>
</blockquote>
<p><font size="2">On peut vérifier notre raisonnement en ajoutant un jeu de données supplémentaire :</font></p>
<blockquote><p><strong><font size="2">INSERT T (texte) VALUES (REPLICATE(&lsquo;T&rsquo;, 100))          <br />GO 1000</font></strong></p>
</blockquote>
<p><font size="2">En utilisant la DMV sys.dm_db_index_physical_stats nous pouvons constater que le nombre de pages à bien évidement augmenté. </font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Fragmentationdesindexesettablesfaiblevol_91CC/image_12.png"><font size="2"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Fragmentationdesindexesettablesfaiblevol_91CC/image_thumb_5.png" width="848" height="60" /></font></a><font size="2"> </font></p>
<p><font size="2">Le nombre de fragments est maintenant égale à trois avec 16 pages pour le niveau feuille. En utilisant la formule ci-dessus la fragmentation est de (3 &#8211; 1) / 16 soit 12.5% .. On peut vérifier par la commande DBCC IND le nombre de fragments :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Fragmentationdesindexesettablesfaiblevol_91CC/image_14.png"><font size="2"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Fragmentationdesindexesettablesfaiblevol_91CC/image_thumb_6.png" width="844" height="296" /></font></a><font size="2"> </font></p>
<p><font size="2">On retrouve trois fragments composés de la manière suivante :</font></p>
<ul>
<li><font size="2">Fragment 1 : page 201 </font></li>
<li><font size="2">Fragment 2 : {page 206 à page 211} </font></li>
<li><font size="2">Fragment 3 : {page 336 à page 344} </font></li>
</ul>
<p><font size="2">Donc pour aller de la page 201 (niveau feuille) à la page 344 il faudra passer par 2 fragments :</font></p>
<p><font size="2"></font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Fragmentationdesindexesettablesfaiblevol_91CC/image_16.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Fragmentationdesindexesettablesfaiblevol_91CC/image_thumb_1.png" width="717" height="278" /></font></a><font size="2"> </font></p>
<p><font size="2">Ajoutons encore quelques données à notre table T.</font></p>
<blockquote><p><strong><font size="2">INSERT T (texte) VALUES (REPLICATE(&lsquo;T&rsquo;, 100))         <br />GO 1000</font></strong></p>
</blockquote>
<p><font size="2">L&rsquo;état de notre index cluster est le suivant :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Fragmentationdesindexesettablesfaiblevol_91CC/image_24.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Fragmentationdesindexesettablesfaiblevol_91CC/image_thumb_9.png" width="842" height="56" /></a> </p>
<p><font size="2">Le nombre de fragments n&rsquo;a pas augmenté. Le nombre de pages est passé de 16 à 31 soit 15 pages supplémentaires. La fragmentation a diminué puisque le ratio fragments / pages a également diminué. Le plan d&rsquo;allocation de pages est devenu le suivant :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Fragmentationdesindexesettablesfaiblevol_91CC/image_30.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Fragmentationdesindexesettablesfaiblevol_91CC/image_thumb_12.png" width="830" height="145" /></a> </p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Fragmentationdesindexesettablesfaiblevol_91CC/image_28.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Fragmentationdesindexesettablesfaiblevol_91CC/image_thumb_11.png" width="832" height="410" /></a> </p>
<p><font size="2">Aller un dernier petit calcul de fragmentation :</font></p>
<blockquote>
<p><font size="2">Fragmentation = (3 – 1) / 32 soit 6.25%</font></p>
</blockquote>
<p><font size="2"> Vous aurez donc compris que plus le ratio fragment / pages est petit mieux c&rsquo;est. Ce ratio évolue lorsque la table (et donc l&rsquo;index) est mis à jour. Mais ceci fera certainement l&rsquo;objet d&rsquo;un autre billet <img src="https://blog.developpez.com/mikedavem/wp-includes/images/smilies/icon_smile.gif" alt=":-)" class="wp-smiley" /></font></p>
<p><font size="2">David BARBARIN (Mikedavem)      <br />MVP SQL Server</font></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Connaître le gain de compression d’une table avec SQL Server</title>
		<link>https://blog.developpez.com/mikedavem/p10142/sql-server-2005/architecture/connaitre_le_gain_de_compression_darsquo</link>
		<comments>https://blog.developpez.com/mikedavem/p10142/sql-server-2005/architecture/connaitre_le_gain_de_compression_darsquo#comments</comments>
		<pubDate>Tue, 19 Jul 2011 20:36:33 +0000</pubDate>
		<dc:creator><![CDATA[mikedavem]]></dc:creator>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[SQL]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Vous avez identifié une table candidate à la compression ? mais vous voulez savoir quelle sera la meilleure méthode de compression ROW ou PAGE. SQL Server met à disposition une procédure stockée sp_estimate_data_compression_savings. Cependant  l&#8217;exécution de cette dernière permet seulement &#8230; <a href="https://blog.developpez.com/mikedavem/p10142/sql-server-2005/architecture/connaitre_le_gain_de_compression_darsquo">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p><font size="2">Vous avez identifié une table candidate à la compression ? mais vous voulez savoir quelle sera la meilleure méthode de compression ROW ou PAGE. SQL Server met à disposition une procédure stockée <em>sp_estimate_data_compression_savings</em>. Cependant  l&rsquo;exécution de cette dernière permet seulement de savoir le taux de compression pour l&rsquo;une ou pour l&rsquo;autre méthode pour une seule partition d&rsquo;une table à la fois. Le script suivant permet de connaître pour une table donnée quelle est la meilleure méthode de compression à utiliser pour l&rsquo;ensemble des partitions d&rsquo;une table sachant qu&rsquo;une table non partitionnée possède une seule partition.</font></p>
<p><span id="more-38"></span></p>
<p><font size="2">Petite précision : il se peut que le résultat du script affiche un gain négatif. Ceci est parfaitement normal car la compression peut être contre bénéfique dans certains cas. En effet le nouveau format de page utilisé dans ce cas requiert des octets de gestion supplémentaire. Si le gain de compression est nulle alors on perd tout l&rsquo;intérêt de la compression et on se retrouve avec un nombre de pages supérieure qu&rsquo;à l&rsquo;état initial.</font></p>
<blockquote><p><strong><font size="2">DECLARE @object_id INT;         <br />SET @object_id = OBJECT_ID(&lsquo;monschema.maTable&rsquo;); </font></strong></p>
<p><strong><font size="2">DECLARE @i INT = 1;         <br />DECLARE @max INT; </font></strong></p>
<p><strong><font size="2">IF EXISTS (SELECT * FROM tempdb.sys.objects WHERE [object_id] = OBJECT_ID(&lsquo;tempdb..#_estimate_data_compression_savings&rsquo;))         <br /> DROP TABLE  #_estimate_data_compression_savings; </font></strong></p>
<p><strong><font size="2">&#8211; Work table for compression         <br />CREATE TABLE #_estimate_data_compression_savings          <br />(          <br /> id INT IDENTITY(1,1),          <br /> [object_name] SYSNAME,          <br /> [schema_name] SYSNAME,          <br /> index_id INT,          <br /> partition_number INT,           <br /> partition_compression VARCHAR(50) NULL,          <br /> partition_value VARCHAR(50) NULL,          <br /> [size_with_current_compression_setting(KB)] INT,          <br /> [size_with_requested_compression_setting(KB)] INT,          <br /> [sample_size_with_current_compression_setting(KB)] INT,          <br /> [sample_size_with_requested_compression_setting(KB)] INT,          <br /> [data_compression] VARCHAR(10) NULL          <br />); </font></strong></p>
<p><strong><font size="2">&#8211; Table for the concerned object          <br />DECLARE @table_compression TABLE           <br />(          <br /> id INT IDENTITY(1,1),          <br /> [schema_name] SYSNAME,          <br /> [object_name] SYSNAME,          <br /> index_id INT,          <br /> partition_number INT,          <br /> partition_compression VARCHAR(50),          <br /> partition_value SQL_VARIANT           <br />); </font></strong></p>
<p><strong><font size="2">INSERT @table_compression ([schema_name], [object_name], index_id, partition_number, partition_compression, partition_value)         <br />SELECT           <br /> s.name,          <br /> o.name,          <br /> p.index_id,          <br /> p.partition_number,          <br /> p.data_compression_desc,          <br /> pv.value          <br />FROM sys.partitions AS p          <br />INNER JOIN sys.objects AS o          <br /> ON p.[object_id] = o.[object_id]          <br />INNER JOIN sys.schemas AS s          <br /> ON s.[schema_id] = o.[schema_id]          <br />LEFT JOIN sys.partition_range_values AS pv          <br /> ON pv.boundary_id = p.partition_number          <br />WHERE o.[object_id] = @object_id          <br /> AND index_id &lt; 2; &#8212; Table heap or with clustered index          <br />SELECT @max = MAX(id)          <br />FROM @table_compression; </font></strong></p>
<p><strong><font size="2">DECLARE @schema_name SYSNAME;         <br />DECLARE @object_name SYSNAME;          <br />DECLARE @index_id INT;          <br />DECLARE @partition_number INT;          <br />DECLARE @partition_compression VARCHAR(50)          <br />DECLARE @partition_value SQL_VARIANT; </font></strong></p>
<p><strong><font size="2">WHILE @i &lt;= @max         <br />BEGIN </font></strong></p>
<p><strong><font size="2"> SELECT          <br />  @schema_name = [schema_name],          <br />  @object_name = [object_name],          <br />  @index_id = index_id,          <br />  @partition_number = partition_number,          <br />  @partition_compression = partition_compression,          <br />  @partition_value = partition_value          <br /> FROM @table_compression          <br /> WHERE id = @i; </font></strong></p>
<p><strong><font size="2"> INSERT #_estimate_data_compression_savings ([object_name], [schema_name], index_id, partition_number, [size_with_current_compression_setting(KB)],          <br />                                             [size_with_requested_compression_setting(KB)], [sample_size_with_current_compression_setting(KB)],          <br />                                             [sample_size_with_requested_compression_setting(KB)])          <br /> EXEC sp_estimate_data_compression_savings @schema_name, @object_name, @index_id, @partition_number, &lsquo;PAGE';          <br /> UPDATE #_estimate_data_compression_savings           <br /> SET [data_compression] = &lsquo;PAGE&rsquo;,          <br />  partition_value = CAST(@partition_value AS VARCHAR(50)),          <br />  partition_compression = @partition_compression          <br /> WHERE id = SCOPE_IDENTITY()          <br /> INSERT #_estimate_data_compression_savings ([object_name], [schema_name], index_id, partition_number, [size_with_current_compression_setting(KB)],           <br />                                             [size_with_requested_compression_setting(KB)], [sample_size_with_current_compression_setting(KB)],          <br />                                             [sample_size_with_requested_compression_setting(KB)])          <br /> EXEC sp_estimate_data_compression_savings @schema_name, @object_name, @index_id, @partition_number, &lsquo;ROW';          <br /> UPDATE #_estimate_data_compression_savings           <br /> SET [data_compression] = &lsquo;ROW&rsquo;,          <br />  partition_value = CAST(@partition_value AS VARCHAR(50)),          <br />  partition_compression = @partition_compression          <br /> WHERE id = SCOPE_IDENTITY()          <br /> SET @i += @i;          <br />END </font></strong></p>
<p><strong><font size="2">&#8211; Show result         <br />;WITH estimate_data_compression_with_page          <br />AS          <br />(          <br /> SELECT           <br />  [schema_name],          <br />  [object_name],          <br />  index_id,          <br />  partition_number,          <br />  partition_value,          <br />  partition_compression,          <br />  [data_compression],          <br />  CASE [size_with_current_compression_setting(KB)]          <br />   WHEN 0 THEN 0          <br />   ELSE (1 &#8211; CAST([size_with_requested_compression_setting(KB)] * 1.           <br />            / [size_with_current_compression_setting(KB)] AS DECIMAL(5,2))) * 100           <br />  END AS ratio_size_compressed,          <br />  [size_with_current_compression_setting(KB)],          <br />  [size_with_requested_compression_setting(KB)],          <br />  CASE [sample_size_with_current_compression_setting(KB)]          <br />   WHEN 0 THEN 0          <br />   ELSE (1 &#8211; CAST([sample_size_with_requested_compression_setting(KB)] * 1.          <br />            / [sample_size_with_current_compression_setting(KB)] AS DECIMAL(5,2))) * 100           <br />  END AS ratio_sample_compressed,          <br />  [sample_size_with_current_compression_setting(KB)],          <br />  [sample_size_with_requested_compression_setting(KB)]          <br /> FROM #_estimate_data_compression_savings          <br /> WHERE [data_compression] = &lsquo;PAGE&rsquo;          <br />),          <br />estimate_data_compression_with_row          <br />AS          <br />(          <br /> SELECT           <br />  [schema_name],          <br />  [object_name],          <br />  index_id,          <br />  partition_number,          <br />  [data_compression],          <br />  CASE [size_with_current_compression_setting(KB)]          <br />   WHEN 0 THEN 0          <br />   ELSE (1 &#8211; CAST([size_with_requested_compression_setting(KB)] * 1.           <br />            / [size_with_current_compression_setting(KB)] AS DECIMAL(5,2))) * 100           <br />  END AS ratio_size_compressed,          <br />  [size_with_current_compression_setting(KB)],          <br />  [size_with_requested_compression_setting(KB)],          <br />  CASE [sample_size_with_current_compression_setting(KB)]          <br />   WHEN 0 THEN 0          <br />   ELSE (1 &#8211; CAST([sample_size_with_requested_compression_setting(KB)] * 1.          <br />            / [sample_size_with_current_compression_setting(KB)] AS DECIMAL(5,2))) * 100           <br />  END AS ratio_sample_compressed,          <br />  [sample_size_with_current_compression_setting(KB)],          <br />  [sample_size_with_requested_compression_setting(KB)]          <br /> FROM #_estimate_data_compression_savings          <br /> WHERE [data_compression] = &lsquo;ROW&rsquo;          <br />)          <br />SELECT           <br /> P.[schema_name],          <br /> P.[object_name],          <br /> P.index_id,          <br /> P.partition_number,          <br /> P.partition_compression,          <br /> P.partition_value,          <br /> CASE WHEN P.ratio_size_compressed &lt; R.ratio_size_compressed THEN &lsquo;ROW : &lsquo; + CAST(R.ratio_size_compressed AS VARCHAR(8)) + &lsquo; %&rsquo;          <br />      WHEN P.ratio_size_compressed &gt; R.ratio_size_compressed THEN &lsquo;PAGE : &lsquo; + CAST(P.ratio_size_compressed AS VARCHAR(8)) + &lsquo; %&rsquo;          <br />      ELSE &lsquo;ROW OR PAGE : &lsquo; + CAST(P.ratio_size_compressed AS VARCHAR(8)) + &lsquo; %&rsquo;          <br /> END AS preferred_method_compression,          <br /> CASE WHEN P.ratio_size_compressed &lt; R.ratio_size_compressed THEN R.[size_with_current_compression_setting(KB)]          <br />      WHEN P.ratio_size_compressed &gt; R.ratio_size_compressed THEN P.[size_with_current_compression_setting(KB)]          <br />      ELSE P.[size_with_current_compression_setting(KB)]          <br /> END AS [size_with_current_compression_setting(KB)],          <br /> CASE WHEN P.ratio_size_compressed &lt; R.ratio_size_compressed THEN R.[size_with_requested_compression_setting(KB)]          <br />      WHEN P.ratio_size_compressed &gt; R.ratio_size_compressed THEN P.[size_with_requested_compression_setting(KB)]          <br />      ELSE P.[size_with_requested_compression_setting(KB)]          <br /> END AS [size_with_current_compression_setting(KB)],          <br /> CASE WHEN P.ratio_size_compressed &lt; R.ratio_size_compressed THEN R.[sample_size_with_current_compression_setting(KB)]          <br />      WHEN P.ratio_size_compressed &gt; R.ratio_size_compressed THEN P.[sample_size_with_current_compression_setting(KB)]          <br />      ELSE P.[sample_size_with_current_compression_setting(KB)]          <br /> END AS [sample_size_with_requested_compression_setting(KB)],          <br /> CASE WHEN P.ratio_size_compressed  &lt; R.ratio_size_compressed THEN R.[sample_size_with_requested_compression_setting(KB)]          <br />      WHEN P.ratio_size_compressed &gt; R.ratio_size_compressed THEN P.[sample_size_with_requested_compression_setting(KB)]          <br />      ELSE P.[sample_size_with_requested_compression_setting(KB)]          <br /> END AS [sample_size_with_requested_compression_setting(KB)]          <br />FROM estimate_data_compression_with_page AS P          <br />INNER JOIN estimate_data_compression_with_row AS R          <br /> ON P.[schema_name] = R.[schema_name]          <br />  AND P.[object_name] = R.[object_name]          <br />   AND P.index_id = R.index_id          <br />    AND P.partition_number = R.partition_number;</font></strong></p>
</blockquote>
<p><font size="2">Bonne compression !!</font></p>
<p><font size="2">David BARBARIN (Mikedavem)      <br />MVP SQL Server</font></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Déplacer les données et les index d’une base de manière équitable dans plusieurs fichiers</title>
		<link>https://blog.developpez.com/mikedavem/p10021/sql-server-2005/architecture/deplacer_les_donnees_et_les_index_darsqu</link>
		<comments>https://blog.developpez.com/mikedavem/p10021/sql-server-2005/architecture/deplacer_les_donnees_et_les_index_darsqu#comments</comments>
		<pubDate>Tue, 31 May 2011 21:18:04 +0000</pubDate>
		<dc:creator><![CDATA[mikedavem]]></dc:creator>
				<category><![CDATA[Architecture]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Suite à une discussion sur le forum je me permets une billet sur comment déplacer les données et index d&#8217;une base de données de façon équitable dans plusieurs fichiers d&#8217;un même groupe de fichiers. Cette opération peut s&#8217;avérer utile dans &#8230; <a href="https://blog.developpez.com/mikedavem/p10021/sql-server-2005/architecture/deplacer_les_donnees_et_les_index_darsqu">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p><font size="2">Suite à une </font><a href="http://www.developpez.net/forums/d1087561/bases-donnees/ms-sql-server/administration/sql-2008r2-p-datafiles/"><font size="2">discussion</font></a><font size="2"> sur le forum je me permets une billet sur comment déplacer les données et index d&rsquo;une base de données de façon équitable dans plusieurs fichiers d&rsquo;un même groupe de fichiers. Cette opération peut s&rsquo;avérer utile dans bien des cas. Un exemple simple est l&rsquo;ajout d&rsquo;un axe physique disque sur lequel on veut ajouter un fichier de données et répartir équitablement celles-ci pour bénéficier par la suite de l&rsquo;algorithme de réparation de SQL Server.</font></p>
<p> <span id="more-37"></span>
<p><font size="2">La question est comment réaliser cette opération ?</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/icon_arrow_2.gif"><font size="2"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="icon_arrow" border="0" alt="icon_arrow" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/icon_arrow_thumb.gif" width="15" height="15" /></font></a><font size="2"> La première solution à laquelle on peut penser en premier est d&rsquo;ajouter un ou plusieurs fichiers dans groupe de fichiers et de reconstruire les index. On peut supposer qu&rsquo;à ce moment le moteur va répartir les données de manière équitable sur tous les fichiers du groupe. Vérifions le &#8230; </font></p>
<p><font size="2">Pour cela nous prendrons une base de données avec 2 groupes de fichiers :</font></p>
<ul>
<li><font size="2">Le groupe PRIMARY avec un seul fichiers de données et qui accueillera les tables </font></li>
<li><font size="2">Le groupe INDEX avec un seul fichiers de données et qui accueillera les index non cluster </font></li>
</ul>
<blockquote><p><strong><font size="2">CREATE DATABASE [TEST_MOVE_DATA] ON  PRIMARY          <br />( NAME = N&rsquo;TEST_MOVE_DATA&rsquo;,           <br />  FILENAME = N&rsquo;C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\TEST_MOVE_DATA.mdf&rsquo; ,           <br />  SIZE = 1048576KB ,           <br />  FILEGROWTH = 1024KB ),           <br />FILEGROUP [INDEX]           <br />( NAME = N&rsquo;TEST_MOVE_DATA_index&rsquo;,           <br />  FILENAME = N&rsquo;C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\TEST_MOVE_DATA_index.ndf&rsquo; ,           <br />  SIZE = 1048576KB ,           <br />  FILEGROWTH = 1024KB )           <br />LOG ON           <br />( NAME = N&rsquo;TEST_MOVE_DATA_log&rsquo;,           <br />  FILENAME = N&rsquo;C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\TEST_MOVE_DATA_log.ldf&rsquo; ,           <br />  SIZE = 20160KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)           <br />GO </font></strong></p>
<p><strong><font size="2">ALTER DATABASE [TEST_MOVE_DATA] SET RECOVERY SIMPLE;          <br />GO</font></strong></p>
</blockquote>
<p><font size="2">Nous créerons ensuite une table ayant les caractéristiques suivantes &#8230;</font></p>
<blockquote><p><strong><font size="2">USE TEST_MOVE_DATA          <br />GO </font></strong></p>
<p><strong><font size="2">CREATE TABLE test          <br />(           <br />col1 INT IDENTITY(1,1) PRIMARY KEY,           <br />col2 CHAR(200),           <br />col3 VARCHAR(200)           <br />);</font></strong></p>
</blockquote>
<p><font size="2">&#8230; avec le jeu de données suivant </font></p>
<blockquote><p><strong><font size="2">INSERT test (col2, col3) VALUES (REPLICATE(&lsquo;T&rsquo;, 200), REPLICATE(&lsquo;X&rsquo;, 200));          <br />GO 2000000</font></strong></p>
</blockquote>
<p><font size="2">&#8230; et un index cluster qui résidera sur le groupe de fichiers [INDEX]</font></p>
<blockquote><p><strong><font size="2">CREATE NONCLUSTERED INDEX IDX_TEST          <br />ON test           <br />(           <br />col2, col3           <br />) ON [INDEX];</font></strong></p>
</blockquote>
<p><font size="2"> </font><font size="2">Commençons par ajouter un deuxième fichier au groupe de fichiers [PRIMARY] ayant la même taille que le fichier existant :</font></p>
<blockquote><p><strong><font size="2">ALTER DATABASE TEST_MOVE_DATA          <br />ADD FILE ( NAME = N&rsquo;TEST_MOVE_DATA_2&prime;,           <br />           FILENAME = N&rsquo;C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\TEST_MOVE_DATA_2.ndf&rsquo; ,           <br />           SIZE = 1048576KB ,           <br />           MAXSIZE = UNLIMITED)           <br />TO FILEGROUP [PRIMARY];</font></strong></p>
</blockquote>
<p><font size="2"> </font><font size="2">Le script suivant permet de voir l&rsquo;espace occupé par les données et l&rsquo;espace libre restant pour chaque fichier de données de la base concernée :</font></p>
<blockquote><p><strong><font size="2">SELECT          <br />    mf.file_id,           <br />    mf.type_desc,           <br />    mf.name AS [filename],           <br />    fg.name AS groupename,           <br />    mf.physical_name,           <br />    mf.size * 8 AS size_KB,           <br />    FILEPROPERTY(mf.name, &lsquo;spaceused&rsquo;) * 8 AS free_KB,           <br />    (mf.size &#8211; FILEPROPERTY(mf.name, &lsquo;spaceused&rsquo;)) * 8  AS used_KB           <br />FROM sys.master_files AS mf           <br />INNER JOIN sys.filegroups AS fg           <br />ON mf.data_space_id = fg.data_space_id           <br />WHERE mf.database_id = DB_ID(&lsquo;TEST_MOVE_DATA&rsquo;)           <br />AND mf.type_desc = &lsquo;ROWS&rsquo;           <br />ORDER BY fg.name, mf.name; </font></strong></p>
</blockquote>
<p><font size="2">Le résultat est le suivant :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/image_2.png"><font size="2"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/image_thumb.png" width="840" height="70" /></font></a><font size="2"> </font></p>
<p><font size="2">Le nouveau fichier TEST_MOVE_DATA_2 n&rsquo;est pour le moment pas utilisé. Reconstruisons maintenant l&rsquo;index cluster.</font></p>
<blockquote><p><strong><font size="2">EXEC sp_helpindex &lsquo;test';          <br />GO           <br />&#8211; Index name : PK__test__357D0D3E7F60ED59 </font></strong></p>
<p><strong><font size="2">ALTER INDEX PK__test__357D0D3E7F60ED59          <br />ON test REBUILD;</font></strong></p>
</blockquote>
<p><font size="2">Vérifions à nouveau l&rsquo;espace utilisé dans les deux fichiers du groupe PRIMARY :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/image_4.png"><font size="2"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/image_thumb_1.png" width="845" height="74" /></font></a><font size="2"> </font></p>
<p><font size="2">Le premier fichier du groupe PRIMARY possède 33% des données alors que le deuxième en possède 77%. La reconstruction d&rsquo;index ne permet pas dans ce premier cas de répartir les données uniformément dans les 2 fichiers. Il faut savoir que le premier fichier du groupe PRIMARY contient les données des tables systèmes et celles-ci ne peuvent pas être déplacés. En ajoutant deux autres fichiers au groupe PRIMARY et en reconstruisant l&rsquo;index on peut remarquer que les données sont répartis uniformément sur ces derniers.</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/image_12.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/image_thumb_5.png" width="852" height="75" /></font></a><font size="2">  </font></p>
<p><font size="2"></font></p>
<p><font size="2">Essayons la même chose avec le groupe INDEX et reconstruisons l&rsquo;index non cluster :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/image_14.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/image_thumb_6.png" width="859" height="54" /></font></a><font size="2">  </font></p>
<p><font size="2">Les données sont relativement bien réparties sur l&rsquo;ensemble des fichiers</font></p>
<p><font size="2"></font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/icon_arrow%5B4%5D.gif"><font size="2"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="icon_arrow[4]" border="0" alt="icon_arrow[4]" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/icon_arrow%5B4%5D_thumb.gif" width="15" height="15" /></font></a><font size="2"> Une deuxième solution qui a été également évoquée consiste à ajouter deux fichiers supplémentaires dans un même groupe et de vider les données du premier fichier en espérant que celles-ci soient répartis équitablement dans ces derniers. Seulement il y a deux inconvénients majeurs dans cette méthode : si le groupe de fichiers concerné est le groupe PRIMARY une erreur sera générée si l&rsquo;on essaie de vider le premier fichier. En effet les données des objets systèmes ne peuvent pas être déplacés. De plus une opération de ce type risque d&rsquo;augmenter la fragmentation des index dans le cas où elle ne concerne pas le premier fichier du groupe PRIMARY . </font></p>
<p><font size="2">Nous pouvons le vérifier. Récupérons d&rsquo;abord l&rsquo;état de fragmentation de l&rsquo;index non cluster avant cette opération.</font></p>
<blockquote><p><strong><font size="2">SELECT          <br />    DB_NAME(database_id) AS database_name,          <br />    index_id,          <br />    index_type_desc,          <br />    avg_fragmentation_in_percent          <br />FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID(&lsquo;test&rsquo;), NULL, NULL, DEFAULT);</font></strong></p>
</blockquote>
<p><font size="2">qui donne le résultat suivant :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/image_6.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/image_thumb_2.png" width="497" height="58" /></font></a><font size="2"> </font></p>
<p><font size="2">Ajoutons ensuite deux fichiers au groupe INDEX :</font></p>
<blockquote><p><strong><font size="2">ALTER DATABASE TEST_MOVE_DATA         <br />ADD FILE ( NAME = N&rsquo;TEST_MOVE_DATA_index_2&prime;,           <br />           FILENAME = N&rsquo;C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\TEST_MOVE_DATA_index_2.ndf&rsquo; ,           <br />           SIZE = 1048576KB ,           <br />           MAXSIZE = UNLIMITED)          <br />TO FILEGROUP [INDEX] </font></strong></p>
<p><strong><font size="2">ALTER DATABASE TEST_MOVE_DATA         <br />ADD FILE ( NAME = N&rsquo;TEST_MOVE_DATA_index_3&prime;,           <br />           FILENAME = N&rsquo;C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\TEST_MOVE_DATA_index_3.ndf&rsquo; ,           <br />           SIZE = 1048576KB ,           <br />           MAXSIZE = UNLIMITED)          <br />TO FILEGROUP [INDEX]</font></strong></p>
</blockquote>
<p><font size="2">et vidons le 1er fichier à l&rsquo;aide de la commande SHRINK et de l&rsquo;option EMPTYFILE</font></p>
<p><strong><font size="2">DBCC SHRINKFILE(TEST_MOVE_DATA_2, EMPTYFILE);</font></strong></p>
<p><font size="2">On peut constater que la répartition n&rsquo;est pas uniforme &#8230;</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/image_8.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/image_thumb_3.png" width="837" height="91" /></font></a><font size="2"> </font></p>
<p><font size="2">&#8230; et que la fragmentation est importante dans le cas présent </font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/image_10.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/image_thumb_4.png" width="513" height="58" /></font></a><font size="2"> </font></p>
<p><font size="2"></font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/icon_arrow_2.gif"><font size="2"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="icon_arrow" border="0" alt="icon_arrow" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/icon_arrow_thumb.gif" width="15" height="15" /></font></a><font size="2"> Une autre solution consiste à créer un groupe de fichiers à part et de transvaser les données d&rsquo;un groupe </font></p>
<blockquote><p><strong><font size="2">ALTER DATABASE TEST_MOVE_DATA         <br />ADD FILEGROUP DATA2 </font></strong></p>
<p><strong><font size="2">ALTER DATABASE TEST_MOVE_DATA         <br />ADD FILE ( NAME = N&rsquo;TEST_MOVE_DATA2&prime;,           <br />           FILENAME = N&rsquo;C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\TEST_MOVE_DATA2.ndf&rsquo; ,           <br />           SIZE = 1048576KB ,           <br />           MAXSIZE = UNLIMITED)          <br />TO FILEGROUP [DATA2] </font></strong></p>
<p><strong><font size="2">ALTER DATABASE TEST_MOVE_DATA         <br />ADD FILE ( NAME = N&rsquo;TEST_MOVE_DATA3&prime;,           <br />           FILENAME = N&rsquo;C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\TEST_MOVE_DATA3.ndf&rsquo; ,           <br />           SIZE = 1048576KB ,           <br />           MAXSIZE = UNLIMITED)          <br />TO FILEGROUP [DATA2] </font></strong></p>
<p><strong><font size="2">ALTER DATABASE TEST_MOVE_DATA         <br />ADD FILEGROUP INDEX2 </font></strong></p>
<p><strong><font size="2">ALTER DATABASE TEST_MOVE_DATA         <br />ADD FILE ( NAME = N&rsquo;TEST_MOVE_INDEX2&prime;,           <br />           FILENAME = N&rsquo;C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\TEST_MOVE_INDEX2.ndf&rsquo; ,           <br />           SIZE = 1048576KB ,           <br />           MAXSIZE = UNLIMITED)          <br />TO FILEGROUP [INDEX2] </font></strong></p>
<p><strong><font size="2">ALTER DATABASE TEST_MOVE_DATA         <br />ADD FILE ( NAME = N&rsquo;TEST_MOVE_INDEX3&prime;,           <br />           FILENAME = N&rsquo;C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\TEST_MOVE_INDEX3.ndf&rsquo; ,           <br />           SIZE = 1048576KB ,           <br />           MAXSIZE = UNLIMITED)          <br />TO FILEGROUP [INDEX2]</font></strong></p>
</blockquote>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2">L&rsquo;opération consiste à reconstruire les index dans les nouveaux groupes de fichiers. Pour cela il faut utiliser cette fois-ci l&rsquo;instruction CREATE INDEX et l&rsquo;option DROP_EXISTING. C&rsquo;est la seule instruction qui permet de cibler un groupe de fichiers.</font></p>
<blockquote><p><strong><font size="2">CREATE UNIQUE CLUSTERED INDEX PK__test__357D0D3E7F60ED59         <br />ON test          <br />(          <br /> col1          <br />)          <br />WITH (DROP_EXISTING = ON)          <br />ON [DATA2];</font></strong></p>
<p><strong><font size="2">CREATE NONCLUSTERED INDEX IDX_TEST         <br />ON test          <br />(          <br /> col2, col3          <br />)          <br />WITH (DROP_EXISTING = ON)          <br />ON [INDEX2];</font></strong></p>
</blockquote>
<p><font size="2">Voyons comment les données ont été réparties dans les nouveaux groupes de fichiers :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/image_16.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/image_thumb_7.png" width="812" height="88" /></font></a><font size="2"> </font></p>
<p><font size="2"></font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/icon_arrow_2.gif"><font size="2"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="icon_arrow" border="0" alt="icon_arrow" src="http://blog.developpez.com/media/269/WindowsLiveWriter/Dplacerlesdonnesetlesindexdunebasedemani_134CC/icon_arrow_thumb.gif" width="15" height="15" /></font></a><font size="2"> Enfin il reste la méthode de créer une base de données à côté et d&rsquo;y transférer les données &#8230; sur ce coup je vous laisse tenter l&rsquo;aventure <img src="https://blog.developpez.com/mikedavem/wp-includes/images/smilies/icon_smile.gif" alt=":-)" class="wp-smiley" /></font></p>
<p><font size="2">Résumons : </font></p>
<ul>
<li><font size="2">Peu importe la méthode utilisée, pour répartir les données de façon équitable il faut avant tout les créer avec la même taille pour que l&rsquo;algorithme de répartition du moteur puisse opérer de manière optimale. </font></li>
<li><font size="2">Les données du premier fichier du groupe PRIMARY ne peuvent être que partiellement déplacées du fait de l&rsquo;existence des objets systèmes</font></li>
<li><font size="2">Le déplacement des données via l&rsquo;instruction SHRINKFILE est à proscrire dans ce cas car elle ne préserve pas l&rsquo;état de fragmentation des index d&rsquo;une part et ne répartit uniformément les données d&rsquo;autre part</font></li>
</ul>
<p><font size="2">Bon déplacement de données !!</font></p>
<p><font size="2">David BARBARIN (Mikedavem)      <br />Elève ingénieur CNAM Lyon</font></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Configuration de SQL Browser avec un compte de domaine dans une délégation Kerberos avec SSAS en instance nommée</title>
		<link>https://blog.developpez.com/mikedavem/p10019/sql-server-2005/architecture/configuration_de_sql_browser_avec_un_com</link>
		<comments>https://blog.developpez.com/mikedavem/p10019/sql-server-2005/architecture/configuration_de_sql_browser_avec_un_com#comments</comments>
		<pubDate>Sun, 29 May 2011 20:45:14 +0000</pubDate>
		<dc:creator><![CDATA[mikedavem]]></dc:creator>
				<category><![CDATA[Architecture]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Lorsque l&#8217;on effectue une installation de SQL Server par défaut, on se retrouve bien souvent avec le compte prédéfini NT AUTORITHY\LOCALSERVICE comme compte de service pour le service SQLBrowser et cela convient bien dans la plupart des scénarios. Cependant je &#8230; <a href="https://blog.developpez.com/mikedavem/p10019/sql-server-2005/architecture/configuration_de_sql_browser_avec_un_com">Lire la suite <span class="meta-nav">&#8594;</span></a>]]></description>
				<content:encoded><![CDATA[<p><font size="2">Lorsque l&rsquo;on effectue une installation de SQL Server par défaut, on se retrouve bien souvent avec le compte prédéfini NT AUTORITHY\LOCALSERVICE comme compte de service pour le service SQLBrowser et cela convient bien dans la plupart des scénarios. Cependant je suis tombé dans un cas où un compte de domaine était nécessaire pour ce service pour pouvoir paramétrer correctement la délégation Kerberos avec une instance nommée SSAS. On retrouve dans la </font><a href="http://msdn.microsoft.com/en-us/library/ms143504.aspx"><font size="2">documentation</font></a><font size="2"> en ligne Microsoft les informations de sécurité nécessaires au bon fonctionnement du service SQLBrowser dans ce contexte sauf que &#8230;</font></p>
<p><span id="more-36"></span></p>
<p><font size="2">&#8230; ça ne fonctionne pas. Nous nous retrouvons avec le message d&rsquo;erreur suivant en voulant se connecter à l&rsquo;instance nommée SSAS :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/ConfigurationdeSQLBrowseravecuncompteded_10FEB/image_2.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/ConfigurationdeSQLBrowseravecuncompteded_10FEB/image_thumb.png" width="476" height="151" /></font></a><font size="2"> </font></p>
<p><font size="2">En lançant SQLBrowser manuellement via une fenêtre de commande avec l&rsquo;option -c dans le contexte du compte de service Windows on peut voir que l&rsquo;erreur suivante :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/ConfigurationdeSQLBrowseravecuncompteded_10FEB/image_4.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/ConfigurationdeSQLBrowseravecuncompteded_10FEB/image_thumb_1.png" width="508" height="190" /></font></a><font size="2"> </font></p>
<p><font size="2">On voit ici que SQLBrowser ne sera pas capable de rediriger les requêtes à l&rsquo;instance nommée SSAS car le service redirecteur n&rsquo;a pu démarrer correctement. En cherchant dans les KB Microsoft je n&rsquo;ai pas eu grand chose sur cette erreur. Aux grands maux les grands remèdes je prend l&rsquo;initiative d&rsquo;utiliser l&rsquo;utilitaire sysinternals </font><a href="http://technet.microsoft.com/en-us/sysinternals/bb896645"><font size="2"><strong><em>procmon</em></strong></font></a><font size="2"> de Microsoft (d&rsquo;ailleurs un de mes collègues a eu l&rsquo;idée bien avant moi <img src="https://blog.developpez.com/mikedavem/wp-includes/images/smilies/icon_smile.gif" alt=":-)" class="wp-smiley" /> ) et celui-ci m&rsquo;a permis de voir que SQLBrowser tentait d&rsquo;accéder en écriture sur le dossier C:\Program Files\Microsoft SQL Server\90\Shared\ASCONFIG et que ce type d&rsquo;accès était donc refusé.</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/ConfigurationdeSQLBrowseravecuncompteded_10FEB/image_6.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/ConfigurationdeSQLBrowseravecuncompteded_10FEB/image_thumb_2.png" width="924" height="143" /></font></a><font size="2"> </font></p>
<p><font size="2">Paramétrons les accès demandés sur ce dossier. Pour cela plusieurs façons de faire :</font></p>
<p><font size="2">- Ajouter le compte de service utilisé par SQL Browser avec un accès en <strong>lecture / écriture / modification</strong>  sur le dossier ASCONFIG</font><font size="2">Ajouter le compte de service utilisé par SQL Browser dans le groupe &#8211; &#8211; SQLServer2005SQLBrowser$User$instanceName et paramétrer pour ce dernier les droits nécessaires sur le dossier ASCONFIG</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/ConfigurationdeSQLBrowseravecuncompteded_10FEB/image_10.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/ConfigurationdeSQLBrowseravecuncompteded_10FEB/image_thumb_4.png" width="318" height="387" /></font></a><font size="2"> </font></p>
<p><font size="2">Tentons un redémarrage du service SQLBrowser via une fenêtre de commande avec l&rsquo;option -c toujours dans le contexte du compte de domaine &#8230;</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/ConfigurationdeSQLBrowseravecuncompteded_10FEB/image_12.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/ConfigurationdeSQLBrowseravecuncompteded_10FEB/image_thumb_5.png" width="634" height="251" /></font></a><font size="2"> </font></p>
<p><font size="2">&#8230; Ok plus aucun message d&rsquo;erreur &#8230; le service SQLBrowser semble pouvoir rediriger les requêtes vers l&rsquo;instance nommée SSAS. Nous sommes sur la bonne voie !!! Nous pouvons redémarrer le service SQLBrowser normalement via la console de gestion de configuration SQL server et voir si la connexion à l&rsquo;instance nommée SSAS fonctionne :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/ConfigurationdeSQLBrowseravecuncompteded_10FEB/image_14.png"><font size="2"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/ConfigurationdeSQLBrowseravecuncompteded_10FEB/image_thumb_6.png" width="338" height="125" /></font></a><font size="2"> </font></p>
<p><font size="2">Tout fonctionne correctement à présent. Tout cela pour dire qu&rsquo;il existe une coquille dans la documentation Microsoft. En effet si l&rsquo;on s&rsquo;y réfère voici les droits à appliquer aux différents dossiers pour le compte de service SQLBrowser :</font></p>
<p><a href="http://blog.developpez.com/media/269/WindowsLiveWriter/ConfigurationdeSQLBrowseravecuncompteded_10FEB/image_18.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://blog.developpez.com/media/269/WindowsLiveWriter/ConfigurationdeSQLBrowseravecuncompteded_10FEB/image_thumb_8.png" width="691" height="95" /></a> </p>
<p><font size="2">Le service SQL Browser doit également posséder le <strong><u>droit de modification</u></strong> sur ce dossier.</font></p>
<p><font size="2">Bon paramétrage !!</font></p>
<p><font size="2">David BARBARIN (Mikedavem)      <br />MVP SQL Server</font></p>
<p><font size="2"> </font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
<p><font size="2"></font></p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
