<?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>SQL &#38; SQL Server sans tabou</title>
	<atom:link href="https://blog.developpez.com/mssql-sans-tabou/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.developpez.com/mssql-sans-tabou</link>
	<description></description>
	<lastBuildDate>Tue, 26 Jun 2012 15:46:35 +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>Quelques choses à savoir sur les espaces en fin de chaîne</title>
		<link>https://blog.developpez.com/mssql-sans-tabou/p11117/bon-a-savoir/quelques_choses_a_savoir_sur_les_espaces</link>
		<comments>https://blog.developpez.com/mssql-sans-tabou/p11117/bon-a-savoir/quelques_choses_a_savoir_sur_les_espaces#comments</comments>
		<pubDate>Tue, 26 Jun 2012 15:42:58 +0000</pubDate>
		<dc:creator><![CDATA[Sergejack]]></dc:creator>
				<category><![CDATA[Bon à savoir]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Lorsque l&#8217;on manipule des chaîne de caractères se terminant par un ou une séries d&#8217;espace, il est bon d&#8217;avoir un certain nombre de considérations à l&#8217;esprit. &#8216;abc&#8217; = &#8216;abc &#8216; se vérifie Lorsque vous comparez deux chaînes de caractère de longueurs différentes via l&#8217;opérateur de comparaison &#171;&#160;=&#160;&#187;, SQL Server va &#171;&#160;au préalable&#160;&#187; (et selon la norme ANSI SQL92) forcer les chaînes a avoir des tailles identiques en rallonger la plus courte par des espaces terminaux [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>
Lorsque l&rsquo;on manipule des chaîne de caractères se terminant par un ou une séries d&rsquo;espace, il est bon d&rsquo;avoir un certain nombre de considérations à l&rsquo;esprit.
</p>
<p><span id="more-6"></span></p>
<h2>&lsquo;abc&rsquo; = &lsquo;abc &lsquo; se vérifie</h2>
<p>
Lorsque vous comparez deux chaînes de caractère de longueurs différentes via l&rsquo;opérateur de comparaison &laquo;&nbsp;=&nbsp;&raquo;, SQL Server va &laquo;&nbsp;au préalable&nbsp;&raquo; (et selon la norme ANSI SQL92) forcer les chaînes a avoir des tailles identiques en rallonger la plus courte par des espaces terminaux (PADDING)
</p>
<p>
Le résultat en est par exemple que &lsquo;abc&rsquo; = &lsquo;abc &lsquo; se vérifie.<br />
<br />
<a href="http://support.microsoft.com/kb/316626/en-us">infos complémentaires</a>
</p>
<h2>&lsquo;abc&rsquo; LIKE &lsquo;abc &lsquo; ne se vérifie pas</h2>
<p>
Comme la nature du LIKE ne se limite pas au test d&rsquo;égalité, celui n&rsquo;est pas, comme l&rsquo;opérateur &laquo;&nbsp;=&nbsp;&raquo;, soumis à la norme évoquée plus haut.
</p>
<p>
Le résultat en est par exemple que &lsquo;abc&rsquo; LIKE &lsquo;abc &lsquo; ne se vérifie pas.
</p>
<h2><a href="http://msdn.microsoft.com/en-us/library/ms190329.aspx">LEN</a>(&lsquo;abc &lsquo;) renvoi 3 et non 4</h2>
<p>
Par définition la fonction LEN qui renvoi la taille d&rsquo;une chaîne de caractère passée en paramètre ne tient pas compte des espaces en fin de cette chaîne
</p>
<p>
Par conséquent, LEN(&lsquo;abc &lsquo;) renvoi 3 et non 4.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Assigner des variables et renvoyer un résultat en une seule  opération</title>
		<link>https://blog.developpez.com/mssql-sans-tabou/p11074/le-cote-obscure/variable_n_return</link>
		<comments>https://blog.developpez.com/mssql-sans-tabou/p11074/le-cote-obscure/variable_n_return#comments</comments>
		<pubDate>Wed, 06 Jun 2012 10:49:55 +0000</pubDate>
		<dc:creator><![CDATA[Sergejack]]></dc:creator>
				<category><![CDATA[Le côté obscure]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Assigner des variables et renvoyer un résultat en une seule opération. Il vous arrive parfois de faire deux opérations SELECT équivalentes, la première servant à assigner une ou plusieurs valeurs à des variables (ou à une table) et la seconde à renvoyer un résultat ? Voici une technique (SQL Server 2008 et +) pour faire ces deux choses en un seul query. Situation : Il n&#8217;est pas rare, lors d&#8217;une opération SELECT, de souhaiter pouvoir [&#8230;]]]></description>
				<content:encoded><![CDATA[<h1>Assigner des variables et renvoyer un résultat en une seule  opération.</h1>
<p>Il vous arrive parfois de faire deux opérations SELECT  équivalentes, la première servant à assigner une ou plusieurs valeurs à des variables (ou à une table) et la seconde à renvoyer un résultat ?<br />
Voici une technique (SQL Server 2008 et +) pour faire ces deux choses en un  seul query.</p>
<p><span id="more-3"></span></p>
<h2> Situation :</h2>
<p>Il n&rsquo;est pas rare, lors d&rsquo;une opération SELECT, de souhaiter pouvoir en exploiter le résultat pour en conserver certaines valeurs (dans des  variables ou une table) et simultanément en renvoyer certaines valeurs (autres et/ou identiques).</p>
<p>Par exemple, si nous avons un query dont le résultat contient  un nombre important d&rsquo;éléments identiques provenant d&rsquo;une seule (voire quelques)  table seulement et afin de réduire la quantité de donnée renvoyée, il devient  souhaitable de ne pas directement inclure ces éléments (sinon une clé pour les  identifiés) dans notre query mais plutôt dans une nouvelle étape, un second  query (ce principe est analogue au principe de la normalisation).</p>
<p align="center"> <em><img src="http://blog.developpez.com/media/Normalisation.png" width="813" height="294" alt="Normalisation" /><br />
Moins de données mais plus de complexité.</em></p>
<p>
En rédigeant le second query et afin de n&rsquo;y récupérer que  les éléments nécessaire, il peut devenir nécessaire d&rsquo;y inclure un nombre  conséquent de jointures, filtres, … qui étaient déjà présents dans le premier  query. Nous amènerons donc probablement le second query à refaire tout un  travail (ou partie de travail) qui vient pourtant d&rsquo;être fait.</p>
<h2> Solution :</h2>
<p> La solution à ce problème est (encore) l&rsquo;utilisation de  MERGE et de la clause OUTPUT. L&rsquo;idée est simple, MERGE va insérer dans une variable  table les données à réutiliser dans une seconde étapes (les clés de certaines  lignes par exemple) tout en renvoyant via sa clause OUTPUT la première partie  du résultat dont nous avons besoin.</p>
<p align="center"> <em><img src="http://blog.developpez.com/media/NormMerge.png" width="813" height="520" alt="Merge" /><br /> <br />
La solution</em></p>
<h2>
  Un exemple solutionné :</h2>
<p> Prenons un exemple qui par soucis de lisibilité sera volontairement  trop simple que pour ne pas être trivial. Il n&rsquo;y sera par exemple pas question  d&rsquo;exploiter des clés et des jointures. Je laisse à votre esprit créatif le soin  d&rsquo;extrapoler cet exemple et sa solution à des scénarios plus complexes et pertinents.</p>
<p> Dans cet exemple, nous allons d&rsquo;abord renvoyer un ensemble  de prénoms, métiers et âge tout en conservant ces prénoms (dans une table  variable) pour ensuite renvoyer les prénoms ainsi conservés.</p>
<p> Note : Ces instructions ci-dessous n&rsquo;ont besoin d&rsquo;aucune  table pour fonctionner, vous pouvez donc les exécuter directement.</p>
<pre>

<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;height:300px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">DECLARE @tmp TABLE ( <br />
&nbsp; FirstName NVARCHAR(100) <br />
) <br />
&nbsp;<br />
MERGE INTO @tmp <br />
USING ( <br />
&nbsp; VALUES <br />
&nbsp; ('Bob', 'Camioneur', 42) <br />
&nbsp; , ('Roger', 'Sénateur', 62) <br />
&nbsp; , ('Françoise', 'Alterophile', 18) <br />
) AS Src (FirstName, Job, Age) <br />
ON ( 1 = 2 ) <br />
WHEN NOT MATCHED THEN <br />
&nbsp; INSERT ( FirstName ) <br />
&nbsp; VALUES ( Src.FirstName ) <br />
OUTPUT <br />
&nbsp; Src.* <br />
; <br />
&nbsp;<br />
SELECT FirstName <br />
FROM @tmp</div></div>

</pre>
<p> Je ne pense pas présumer de vos  capacités en concluant que vous aurez maintenant compris la solution général.</p>
<h2> Conslusion :</h2>
<p> Assigner des variables et renvoyer un résultat en une seule  opération n&rsquo;a rien de sorcier, il faut juste avoir à l&rsquo;esprit que SELECT n&rsquo;est  pas la seule opération qui peut renvoyer un résultat et que MERGE qui non  seulement le permet aussi, est justement un outil servant à conserver (insert/update)  des données. </p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Quand les fonctions tables surpassent les vues et CTE.</title>
		<link>https://blog.developpez.com/mssql-sans-tabou/p11057/le-cote-obscure/function_vs_cte</link>
		<comments>https://blog.developpez.com/mssql-sans-tabou/p11057/le-cote-obscure/function_vs_cte#comments</comments>
		<pubDate>Wed, 30 May 2012 16:19:00 +0000</pubDate>
		<dc:creator><![CDATA[Sergejack]]></dc:creator>
				<category><![CDATA[Le côté obscure]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Quand les fonctions tables surpassent les vues et CTE. SQL Server établit ses stratégies pour optimiser les requêtes en fonction d&#8217;une intelligence artificielle qui manque parfois d&#8217;efficacité. Ce sujet aura pour but de démontrer que l&#8217;emploie d&#8217;une fonction table remplace parfois avantageusement celle d&#8217;une vue ou une CTE. On aime les vues et les CTE : En effet, les vues et les CTE sont pratiques et faciles à manipuler (puisqu&#8217;elles se manipulent comme des tables). [&#8230;]]]></description>
				<content:encoded><![CDATA[<h1>Quand les fonctions tables surpassent les vues et CTE.</h1>
<p> SQL Server établit ses stratégies pour optimiser les  requêtes en fonction d&rsquo;une intelligence artificielle qui manque parfois  d&rsquo;efficacité. Ce sujet aura pour but de démontrer que l&rsquo;emploie d&rsquo;une fonction table  remplace parfois avantageusement celle d&rsquo;une vue ou une CTE.</p>
<p><span id="more-2"></span></p>
<h2> On aime les vues et les CTE :</h2>
<p> En effet, les vues et les CTE sont pratiques et faciles à  manipuler (puisqu&rsquo;elles se manipulent comme des tables). On aurait tort de s&rsquo;en  priver et il ne faudrait pas conclure à la lecture de ce sujet qu&rsquo;elles souffriraient  d&rsquo;un handicap dont la manifestation serait autre que très rare.</p>
<h2> Le bât blesse :</h2>
<p> Le problème qui peut apparaître lorsque l&rsquo;on utilise une ou  plusieurs vues et/ou CTE en conjonction à d&rsquo;autres ensembles de résultat est  l&rsquo;incapacité qu&rsquo;a SQL Server à « raisonner » avec abstraction afin de  déterminer des optimisations clés desquelles profiteraient ces vues et/ou CTE.</p>
<p> Exemple :</p>
<p> En considérant NUMBERS une table d&rsquo;un million d&rsquo;entier « n » distincts pour lesquels existe un index, imaginez le  query suivant :</p>
<pre>

<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">; WITH CTE AS ( <br />
&nbsp; SELECT <br />
&nbsp; &nbsp; n <br />
&nbsp; &nbsp; , ROW_NUMBER() OVER (ORDER BY n) AS Row <br />
&nbsp; FROM VIRTUAL_NUMBERS <br />
) <br />
SELECT <br />
&nbsp; C1.n <br />
&nbsp; , C2.n <br />
FROM CTE AS C1 <br />
INNER JOIN CTE AS C2 ON ( <br />
&nbsp; C2.Row = C1.Row + 1 <br />
) <br />
WHERE C1.n = 500</div></div>

</pre>
<p> Ce query qui renverra au plus une seule ligne, mettra un  temps plus que significatif à s&rsquo;exécuter. La cause en est que SQL Server auquel  nous soumettons une jointure filtrée en fonction du numéro de ligne et malgré  que nous ne récupérions pas ces numéros (4 et 5, 300 et 301, … ?) va déterminer  la valeur exacte de ces numéros (de 1 à 1.000.000 et c&rsquo;est tout un labeur). Si SQL Server était  capable d&rsquo;une abstraction suffisante, il ferait la corrélation entre le filtre  sur n (n = 500) et les numéros de ligne dont l&rsquo;ordre dépendants de n.</p>
<h2> Il est temps d&rsquo;agir !</h2>
<p> Le query précédent peut être réécrit comme suit :</p>
<pre>

<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">SELECT <br />
&nbsp; C1.n <br />
&nbsp; , C2.n <br />
FROM NUMBERS AS C1 <br />
CROSS APPLY ( <br />
&nbsp; SELECT TOP(1) <br />
&nbsp; &nbsp; C2.n <br />
&nbsp; FROM NUMBERS AS C2 <br />
&nbsp; WHERE C2.n &gt; 500 -- ou C1.n lui-même forcément égal à 500 <br />
&nbsp; ORDER BY C2.n ASC <br />
) AS C2(n) <br />
WHERE C1.n = 500</div></div>

</pre>
<p> Ici, le critère de filtrage est directement ré exploite dans  le sous-query et ainsi la performance générale de la requête sera optimale.</p>
<p> Pour peu que ce sous-query ait une certaine propension à se  retrouver parmi un nombre significatif de requêtes, on peut souhaiter en éviter  la redondance et en définir le comportement une seule et unique fois. Cela peut  justement être fait par une fonction table (impossible en CTE ou vue).</p>
<p> La définition d&rsquo;une telle fonction dans notre cas  serait :</p>
<pre>

<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">CREATE FUNCTION fNEXT(@pN INT) <br />
RETURNS TABLE <br />
AS <br />
RETURN ( <br />
&nbsp; SELECT TOP(1) <br />
&nbsp; &nbsp; n <br />
&nbsp; FROM NUMBERS <br />
&nbsp; WHERE n &gt; @pN <br />
&nbsp; ORDER BY n ASC <br />
)</div></div>

</pre>
<p>Et notre query deviendrait :</p>
<pre>

<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">SELECT <br />
&nbsp; C1.n <br />
&nbsp; , C2.n <br />
FROM NUMBERS AS C1 <br />
CROSS APPLY dbo.fNEXT(500) AS C2 <br />
WHERE C1.n = 500</div></div>

</pre>
<h2>Row by agonizing row ?</h2>
<p> Note : Si vous ingorez  ce dont il s&rsquo;agit, il vous faudra songer à vous renseigner (car ce serait bon  pour vous).</p>
<p> RBAR ! Cet anagrame,  quasi onomatopé signifiant « Y a mal ! », pourrait venir à la bouche  des néophytes qui ont une expérience suffisament longue pour avoir oublier en  être (des néophytes). Il est fréquent d&rsquo;entendre que la lecture de tables dans  une fonction provoque systématiquement le phénomène RBAR. Cela est une grosse  erreur. SQL Server est bien plus malin que ça*.</p>
<p> Sachez donc que la fonction  et son usage tels que décrits offrent des performances excéllentes et ne  provoquent pas le RBAR. D&rsquo;ailleurs je laisse à votre curiosité le query suivant  qui vous permettra de vous faire une opinion bien tranchée (mais pas trop,  restez critique).</p>
<pre>

<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">SELECT <br />
&nbsp; C1.n <br />
&nbsp; , C2.n <br />
FROM NUMBERS AS C1 <br />
INNER JOIN NUMBERS AS C2 ON C2.n = C1.n + 1 <br />
WHERE C1.n BETWEEN 10000 AND 20000</div></div>

</pre>
<p>* Si vous êtes dubitatifs, comparez les excécution des deux queries suivants et faites vous une idée :</p>
<pre>

<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">SELECT &nbsp;<br />
&nbsp; N.N <br />
&nbsp; , X.N <br />
FROM [Demo].[dbo].[NUMBERS] AS N <br />
CROSS APPLY ( <br />
&nbsp; SELECT N2.N <br />
&nbsp; FROM [Demo].[dbo].[NUMBERS] AS N2 <br />
&nbsp; WHERE N2.N = N.N - 1 <br />
) AS X <br />
WHERE N.N BETWEEN 1 AND 500</div></div>

</pre>
<pre>

<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">SELECT &nbsp;<br />
&nbsp; N.N <br />
&nbsp; , X.N <br />
FROM [Demo].[dbo].[NUMBERS] AS N <br />
INNER JOIN [Demo].[dbo].[NUMBERS] AS X ON ( <br />
&nbsp; X.N = N.N - 1 <br />
) <br />
WHERE N.N BETWEEN 1 AND 500</div></div>

</pre>
<h2>Conclusions :</h2>
<p> Il n&rsquo;y a parfois pas d&rsquo;autre  alternative pour faire profiter une requête (ou sous requête) d&rsquo;un ou plusieurs  critères de filtrage qu&rsquo;en employant des fonctions (ou des sous-requêtes)  jointes par l&rsquo;opérateur APPLY. Cette approche n&rsquo;étant pas en elle-même RBAR,  les performances peuvent s&rsquo;en retrouvées considérablement améliorée.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Le triangle parfait entre FROM, OUTPUT et MERGE</title>
		<link>https://blog.developpez.com/mssql-sans-tabou/p11041/bon-a-savoir/merge_output</link>
		<comments>https://blog.developpez.com/mssql-sans-tabou/p11041/bon-a-savoir/merge_output#comments</comments>
		<pubDate>Tue, 22 May 2012 15:42:48 +0000</pubDate>
		<dc:creator><![CDATA[Sergejack]]></dc:creator>
				<category><![CDATA[Bon à savoir]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Le triangle parfait entre FROM, OUTPUT et MERGE Le but de ce sujet est de vous montrer un intérêt résidant en MERGE et qui est d&#8217;offrir une possibilité cruellement absente avant son apparition en SQL Server : à l&#8217;insertion, récupérer dans la clause OUTPUT des valeurs provenant de la clause FROM (plutôt que provenant seulement des tables deleted et inserted). Note : MERGE est présent en SQL Server depuis la version 2008. Clause FROM, j&#8217;aime [&#8230;]]]></description>
				<content:encoded><![CDATA[<h1>Le triangle parfait entre FROM, OUTPUT et MERGE</h1>
<p><span id="more-4"></span></p>
<p> Le but de ce sujet est de vous montrer un intérêt résidant  en MERGE et qui est d&rsquo;offrir une possibilité cruellement absente avant son apparition  en SQL Server : à l&rsquo;insertion, récupérer dans la clause OUTPUT des valeurs  provenant de la clause FROM (plutôt que provenant seulement des tables deleted  et inserted).</p>
<p>
  Note :<br />
  MERGE est présent en SQL Server depuis la version 2008.</p>
<h2> Clause FROM, j&rsquo;aime :</h2>
<p> S&rsquo;il y a certaines raisons à l&rsquo;appréciation que je porte à  SQL Server (que je connais depuis sa version 2005), l&rsquo;existence de la clause  FROM pour UPDATE (entre autres) est une des plus significatives.<br />
  Il n&rsquo;y a aucun sarcasme tandis que je vous dis que Microsoft a fait, avec cette  clause, le choix de s&rsquo;écarter de la norme SQL avec une perspicacité exemplaire  (mais ceci n&rsquo;est pas le sujet traité). Cette clause qui est aussi présente pour  les DELETE est malheureusement absente des opérations INSERT.<br />
  À priori, cette absence ne semble pas porter à grande  conséquence puisqu&rsquo;il reste néanmoins possible de faire des insertions sur base  de résultats obtenus par select. (insert into … select … from …). Mais cela  serait oublier un peu vite la clause OUTPUT qui, si elle n&rsquo;en continue pas  moins d&rsquo;exister pour les opérations d&rsquo;insertion, n&rsquo;a alors pas accès à autre  chose qu&rsquo;aux table inserted et deleted.
</p>
<h2>
  INSERT FROM, y a OUTPUT qui te cherche :</h2>
<p> Ainsi donc, il n&rsquo;existe pas de clause FROM à l&rsquo;opération INSERT.<br />
  Alors imaginons un scénario où cela va nous manquer :<br />
  Nous avons deux tables temporaires, l&rsquo;une faisant référence à l&rsquo;autre (il y a  donc emploie de clés) et nous souhaitons copier le contenu de ces deux tables  temporaires vers des tables de même structures mais dont les identifiants  devront être adaptés (parce qu&rsquo;il s&rsquo;agit d&rsquo;identifiant numériques auto  incrémentés par exemple).<br />
  Nous allons donc devoir commencer par faire la copie des données de la table « parente/influente »  (celle qui est référée par l&rsquo;autre), récupérer les identifiants des lignes insérées  dans la table de destination et ensuite faire la copie des données de la table « fille/dépendante »  en exploitant les identifiants précédemment récupérés.
  </p>
<p>Facile à dire et bien que faisable, le faire est une belle  paire de manche (et autant vous dire, qu&rsquo;en tout cas de figure, il y aura des performances  tristement gâchées).</p>
<p align="center"> <em><img src="http://blog.developpez.com/media/InsertFrom.png" width="605" height="276" alt="Insert from" /><br />
  Recréer les relations n&rsquo;est pas une mince affaire.</em></p>
<h2>
  MERGE à la rescousse :</h2>
<p> MERGE qui a une syntaxe particulière offre non seulement une  approche compacte à la fusion de données (insérer ce qui n&rsquo;existe pas et mettre  à jour ce qui existe) mais offre surtout un remède aux problèmes qui ont été  mentionnés plus haut. En effet, MERGE permet dans sa clause OUTPUT de récupérer  des données provenant tant des tables inserted et deleted que de la table  source (table au sens large, un ensemble de résultats).<br />
  Ainsi, si l&lsquo;on reprend le scénario qui nous inquiétait en considérant une  approche avec INSERT, il devient facile pendant l&rsquo;insertion (avec MERGE) de  ligne provenant de la table temporaire « parente/influente » d&rsquo;en  récupérer simultanément l&rsquo;identifiant d&rsquo;origine et l&rsquo;identifiant obtenu après  insertion. Cela peut donc permettre d&rsquo;établir rapidement la correspondance d&rsquo;un  identifiant vers l&rsquo;autre, ce qui permettra donc une « traduction »  des identifiants lors de l&rsquo;insertion des lignes de la seconde table « fille/dépendante ».</p>
<pre>

<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">DECLARE @Mapping TABLE ( <br />
&nbsp; externalID INT <br />
&nbsp; , internalID INT <br />
&nbsp; <br />
&nbsp; /* Ci dessous, une</div></div>

<a href="http://blog.developpez.com/mssql-sans-tabou/p11042/bon-a-savoir/vartable-index/">astuce</a> pour avoir un index sur externalID */ 
  , ID INT IDENTITY 
  , UNIQUE (externalID, ID) 
) 
 
MERGE INTO dbo.Vert AS Dst 
USING ( 
  SELECT 
    ID 
    , colonne1 
    , colonne2 
  FROM #tmpVert    
) AS Src 
ON ( 
  1 = 2 -- 100% nouvelles lignes 
) 
WHEN NOT MATCHED THEN 
  INSERT (colonne1, colonne2) 
  VALUES (Src.colonne1, Src.colonne2) 
OUTPUT Src.ID, inserted.ID INTO @Mapping(externalID, internalID) 
; -- Le point virgule fait partie de la syntaxe 
 
INSERT INTO dbo.Jaune ( 
  vertID 
  , colonne1 
) 
SELECT 
  M.internalID 
  , Src.colonne1 
FROM #tmpJaune AS Src 
INNER JOIN @Mapping AS M ON ( 
  M.externalID = Src.vertID 
) 
</pre>
<h2>
  Conclusion :</h2>
<p> Le simple fait d&rsquo;offrir une clause OUTPUT dans laquelle  les données de la table source peuvent être  récupérées, dote l&rsquo;opération MERGE d&rsquo;un avantage pouvant s&rsquo;avérer essentielle  face à l&rsquo;opération INSERT.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Des indexes dans vos variables tables</title>
		<link>https://blog.developpez.com/mssql-sans-tabou/p11042/bon-a-savoir/vartable_index</link>
		<comments>https://blog.developpez.com/mssql-sans-tabou/p11042/bon-a-savoir/vartable_index#comments</comments>
		<pubDate>Wed, 23 May 2012 09:01:45 +0000</pubDate>
		<dc:creator><![CDATA[Sergejack]]></dc:creator>
				<category><![CDATA[Bon à savoir]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Des indexes dans vos variables tables Contrairement à ce que l&#8217;on peut encore trop fréquemment lire, il est tout à fait possible, avec un peu d&#8217;astuce de définir des indexes sur les variables tables. Comment ? L&#8217;astuce en question est de tirer profit de la possibilité de définir pour un ou plusieurs ensembles de colonnes une contrainte d&#8217;unicité. La contrainte d&#8217;unicité que ce soit pour des variables tables, des tables temporaires ou des tables normales [&#8230;]]]></description>
				<content:encoded><![CDATA[<h1>Des indexes dans vos variables tables</h1>
<p> Contrairement à ce que l&rsquo;on peut encore trop fréquemment lire,  il est tout à fait possible, avec un peu d&rsquo;astuce de définir des indexes sur  les variables tables.</p>
<p><span id="more-5"></span></p>
<h2>  Comment ?</h2>
<p> L&rsquo;astuce en question est de tirer profit de la possibilité de  définir pour un ou plusieurs ensembles de colonnes une contrainte d&rsquo;unicité. La  contrainte d&rsquo;unicité que ce soit pour des variables tables, des tables  temporaires ou des tables normales provoque la construction d&rsquo;un index dont les  colonnes sont ordonnées dans le même ordre que celui présent dans la définition  de la contrainte (ceci est important).<br />
Illustrons cela par un exemple :</p>
<pre>

<div class="codecolorer-container text default" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap">DECLARE @Mapping TABLE ( <br />
&nbsp; special BIT <br />
&nbsp; , data1 VARCHAR(MAX) <br />
&nbsp; , data2 VARCHAR(MAX) <br />
&nbsp; <br />
&nbsp; , ID INT IDENTITY <br />
&nbsp; , UNIQUE (special, ID) <br />
)</div></div>

</pre>
<p> Dans cet exemple, la  contrainte d&rsquo;unicité va causer la création d&rsquo;un indexe [special, ID] (et en  aucun cas [ID, special] qui serait pourtant forcément tout aussi unique). L&rsquo;indexe  ainsi crée, permettra de facilement faire des queries sur cette variable table  en en filtrant le résultat selon la valeure du champ « special ».</p>
<h2> L&rsquo;unicité :</h2>
<p> Bien sûr avant de définir une  contrainte d&rsquo;unicité il faut être certain de pouvoir la respecter, aussi dans  vos tables temporaires, il n&rsquo;existe pas toujours un combinaison de colonnes (comprenant  celles pour lesquelles vous souhaitez construire un indexe) qui puissent  respecter l&rsquo;unicité. Sauf que rien ne vous empêche de rajouter un colonne supplémentaire  qui fera apparaître une telle combinaison. Et cela est d&rsquo;une simplicité  enfantine puisque, comme vous l&rsquo;aviez peut-être déjà constaté dans l&rsquo;exemple,  il suffit de rajouter une colonne (numérique) autoincrémenté à votre variable  table dont la présence dans n&rsquo;importe quelle combinaison de colonnes, garantira  que cette combinaison respectera l&rsquo;unicité (puisque cette colonne a elle seule la  garanti déjà).</p>
<h2> Conclusion :</h2>
<p> Pour créer un indexe sur une  table variable, vous devez définir que les ensembles ordonnées des colonnes qui  composent ces indexes ont des valeurs uniques et pour permettre cela, vous  pouvez rajouter une colonne autoincrémentée qui terminera la définition de  chacune de vos contrainte d&rsquo;unicité.</p>
<pre><code class="codecolorer text default"><span class="text">UNIQUE ([colonne 1], [colonne &nbsp;2], …, [colonne n], [Auto ID])</span></code></pre>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Et si on se passait des clés étrangères ?</title>
		<link>https://blog.developpez.com/mssql-sans-tabou/p11039/le-cote-obscure/sans_fk</link>
		<comments>https://blog.developpez.com/mssql-sans-tabou/p11039/le-cote-obscure/sans_fk#comments</comments>
		<pubDate>Tue, 22 May 2012 11:28:08 +0000</pubDate>
		<dc:creator><![CDATA[Sergejack]]></dc:creator>
				<category><![CDATA[Le côté obscure]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Et si on se passait des clés étrangères ? Calmons tout de suite l&#8217;appréhension qu&#8217;une telle question pourrait faire naître dans l&#8217;esprit aguerri des routards  du SQL. Ce sujet n&#8217;a pas pour but de convaincre le lecteur que toutes les clés étrangères seraient inutiles voire néfastes (car ce n&#8217;est pas le cas). Plutôt, il existe des situations dans lesquelles ne pas définir les clés étrangères est un choix légitime et faire connaître ces situations est [&#8230;]]]></description>
				<content:encoded><![CDATA[<h1>Et si on se passait des clés étrangères ?</h1>
<p><span id="more-1"></span></p>
<p> Calmons tout de suite l&rsquo;appréhension qu&rsquo;une telle question pourrait  faire naître dans l&rsquo;esprit aguerri des routards  du SQL. Ce sujet n&rsquo;a pas pour but de  convaincre le lecteur que toutes les clés étrangères seraient inutiles voire  néfastes (car ce n&rsquo;est pas le cas). Plutôt, il existe des situations dans  lesquelles ne pas définir les clés étrangères est un choix légitime et faire  connaître ces situations est l&rsquo;objectif que je me donne dans ce sujet.</p>
<p> Avant d&rsquo;aller plus loin, il me faut préciser que ce sujet ne  pourrait voir l&rsquo;application de son propos que régit à la plus grande des  prudences. En effet, les clés étrangères sont de précieux garde-fous et ne devraient  pas être ignorées à la légère.</p>
<p>Note :<br />
  Quand je parle de clé étrangère, je parle de la contrainte (« FOREIGN  KEY ») et non de la présence d&rsquo;un ensemble de données permettant la relation  entre les lignes d&rsquo;une ou plusieurs tables.</p>
<h2>Pourquoi et Quand ?</h2>
<p> Lorsque que vous avez une relation (entre plusieurs tables ou  récursive) qui rendent certaines lignes indissociables de certaines autres  (scénario dans lequel vous auriez habituellement des clés étrangères), alors,  suite à la suppression de lignes (influentes) dont en dépendent d&rsquo;autres  (dépendantes), il vous faudrait tôt ou tard répercuter cette suppression à ces  lignes dépendantes (afin de ne pas conserver de données inutiles).<br />
  Lorsque la charge de vos serveurs le permet, vous pouvez  tout à fait réaliser ces suppressions « en même temps » que les suppressions  qui les provoquent.<br />
  Vous faites habituellement cela par la clause « ON DELETE CASCADE » d&rsquo;une  contrainte de clé étrangère et/ou par le biais de triggers.<br />
  Dans ce cas-là, l&rsquo;intérêt d&rsquo;avoir des clés étrangères est entier et ce sujet ne  relativisera pas leur emploie
</p>
<p align="center"><em><img src="http://blog.developpez.com/media/DelTrigger.png" alt="Trigger on delete" width="394" height="220" /><br />
La suppression de « C » dans la  « table verte » cause « immédiatement » la suppression de « 5 »  dans la « table jaune ».</em></p>
<p>
  Par contre,  lorsque  la charge de vos serveurs vous incite à différer ces suppressions, vous devez  choisir une stratégie pour permettre à ces suppressions d&rsquo;avoir ultérieurement  lieu.<br />
  Couramment, il est choisi de rompre la dépendance tout en marquant les lignes  qui doivent être supprimée par une seule et même action. Soit en recréant un  lien entre ces lignes à supprimer et une ligne factice et prédéterminée qui sert  d&rsquo;indicateur (il s&rsquo;agit de la clause « ON DELETE SET DEFAULT »). Soit  en laissant le lien se perdre (clause « ON DELETE SET NULL »).<br />
  Ces deux façon de faire ont un impact sur les performances  qui, bien que généralement moindre à une suppression effective, restent  significatifs.<br />
En effet, toutes les lignes dépendantes à celles qui sont supprimés sont « simultanément »  mises à jour (écritures disques, ré-indexation potentielle et risques de race  condition).</p>
<p align="center"><em><img src="http://blog.developpez.com/media/DelSetNull.png" width="394" height="212" alt="On delete set null" /><br />
  Suite à la suppression de « C »,  cause « immédiatement » la modification de « 5 » pour que  la liaison soit supprimée (« SET NULL »).</em>
</p>
<p>C&rsquo;est en partant du constat de cet impact sur les  performances que commence à s&rsquo;envisager la possibilité de se passer de la clé  étrangère.</p>
<h2>Pourquoi pas ?</h2>
<p> Comme précisé plus haut, il faut être et resté prudent  (prudence est mère de sûreté).<br />
  En se passant de la clé étrangère et en n&rsquo;employant aucun marqueur sur les  lignes dépendantes qui doivent à terme être supprimées (à partir de ce point,  je dirai de telles lignes qu&rsquo;elles sont virtuellement supprimées), il devient moins  évident qu&rsquo;une ligne virtuellement supprimée, l&rsquo;est.<br />
En effet, pour se rendre compte que le lien d&rsquo;une ligne dépendante à une ligne influente  est rompu, il faudra systématiquement vérifier l&rsquo;existence de la ligne  influente. Cela n&rsquo;est pas gênant quand, dans toutes vos requêtes, vous soumettez  déjà les lectures des lignes dépendantes à leur relation aux lignes influentes  (typiquement, « INNER JOIN »). Mais, dans le cas contraire, si dans certaines  requêtes, vous consultez les lignes dépendantes sans « vérifier » leur  relation aux lignes influentes dont elles dépendent, vous serez alors en train  de consulter des lignes virtuellement supprimées. Ce dernier scénario pourrait,  vous vous en rendez compte, vous causez un certain nombre de surprises et donc  de problèmes.</p>
<p align="center"> <em><img src="http://blog.developpez.com/media/From.png" width="367" height="197" alt="Select from" /><br /> <br />
  Si la relation de la « table jaune » à la « table verte » n&rsquo;est  pas vérifiée, la ligne « 5 » virtuellement supprimée pourrait  apparaître dans le résultat.</em></p>
<p align="center"> <em><img src="http://blog.developpez.com/media/FromInner.png" width="355" height="197" alt="Select from inner join" /><br /> <br />
Si la relation de la « table jaune » à la « table verte » est  vérifiée, la ligne « 5 » virtuellement supprimée ne pourrait pas apparaître  dans le résultat.</em></p>
<h2>Comment ?</h2>
<p> Voici la partie la plus simple, pour de nouvelles tables il  suffit de ne pas définir la clé étrangère et pour les tables existantes, il  suffit de la supprimer. Par ailleurs il faut quand même créer/adapter les  processus de nettoyage pour qu&rsquo;ils détectent les lignes virtuellement  supprimées non plus directement grâce à un marqueur (clé étrangère à « NULL »  par exemple) mais en  vérifiant l&rsquo;existantes  des lignes influentes.</p>
<h2>Conclusion :</h2>
<p> Les clés étrangères sont des garde-fous importants mais avec  une réflexion et une prudence suffisante, ce qu&rsquo;elles coûtent aux performances  lors des suppressions peut vous faire préférer d&rsquo;en éviter certaines. Atteindre  certains sommets implique une prise de risque que seule une grande maîtrise  pourrait justifier. Donc, si vous êtes sûrs de vous, je vous le dit : ne  pas définir une clé étrangère ne vous tuera pas et a des bénéfices certains.</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>
