<?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 - Etienne ZINZINDOHOUE &#187; SQL SERVER 2008</title>
	<atom:link href="https://blog.developpez.com/zinzineti/pcategory/sql-server-2008/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.developpez.com/zinzineti</link>
	<description>Bases de données</description>
	<lastBuildDate>Thu, 09 Aug 2012 13:57: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>IPv4 : Stockage et Manipulations</title>
		<link>https://blog.developpez.com/zinzineti/p11081/sql-server-2008/ipv4_stockage_et_manipulations</link>
		<comments>https://blog.developpez.com/zinzineti/p11081/sql-server-2008/ipv4_stockage_et_manipulations#comments</comments>
		<pubDate>Fri, 08 Jun 2012 17:26:29 +0000</pubDate>
		<dc:creator><![CDATA[zinzineti]]></dc:creator>
				<category><![CDATA[SQL SERVER 2008]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[La norme SQL n&#8217;a rien proposé jusqu&#8217;à ce jour pour stocker les types de données network (IPv4, IPv6, masque de sous-réseau,&#8230;) . Les &#171;&#160;grands&#160;&#187; éditeurs de SGBD ne disposent pas encore de types de données capable de stocker et de manipuler aisément les données de types IP par exemple. Dans ce domaine PostGreSQL est largement en avance. Pour illustrer mes propos prenons un exemple simple : Imaginer une application web qui stocke l&#8217;adresse IP des [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>La norme SQL n&rsquo;a rien proposé jusqu&rsquo;à ce jour pour stocker les types de données network (IPv4, IPv6, masque de sous-réseau,&#8230;) . Les &laquo;&nbsp;grands&nbsp;&raquo; éditeurs de SGBD ne disposent pas encore de types de données capable de stocker et de manipuler aisément les données de types IP par exemple. Dans ce domaine PostGreSQL est largement en avance. Pour illustrer mes propos prenons un exemple simple :<br />
Imaginer une application web qui stocke l&rsquo;adresse IP des ordinateurs des utilisateurs dans une table nommée &laquo;&nbsp;TableDesIPs&nbsp;&raquo;  pour faciliter un certain nombre d&rsquo;opérations : identification, redirection, restriction, filtrage, &#8230; SQL Server et ORACLE sont aujourd&rsquo;hui incapables de donner une réponse favorable à la requête suivante : <strong><code class="codecolorer text default"><span class="text">SELECT IP_VISITEUR FROM TableDesIPs WHERE IP_VISITEUR &gt; IP_MIN AND IP_VISITEUR &lt; IP_ADR_MAX</span></code></strong><br />
C&rsquo;est à dire lister les adresses IP membre d&rsquo;une plage d&rsquo;adresse IP. La borne inférieure de la plage est IP_MIN et la borne supérieure IP_MAX.<br />
Par contre PostGreSQL se démarque en implémentant les types de données : <strong>CIDR</strong> et <strong>INET</strong> qui permettent de stocker et de manipuler aisément les données de type IP. Voyons quelques rustines pour stocker des données IPv4 et MAC-48 sous SQL Server <span id="more-105"></span><br />
Quelques rustines pour stocker des données IPv4 sous SQL Server<br />
=> <strong>Stocker IPv4 sous SQL Server</strong></p>
<p><strong>&#8211;> Création de la fonction de validation d&rsquo;IPv4</strong></p>
<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 [dbo].[IsIPv4](@IsIPv4 CHAR(19)) <br />
RETURNS SMALLINT <br />
AS <br />
BEGIN <br />
DECLARE @IsOK SMALLINT; <br />
-- Vérifier si on que des nombres en dehors des points <br />
IF (ISNUMERIC(REPLACE(@IsIPv4,'.',''))=1 <br />
-- Vérifier s'il y a 3 points dans la chaine &nbsp; &nbsp;<br />
&nbsp; &nbsp; AND LEN(@IsIPv4) - LEN(REPLACE(@IsIPv4,'.','')) = 3 <br />
-- Vérifier que les nombres de bits pour l'IPV4 est compris entre 0 et 255 &nbsp; &nbsp; &nbsp; <br />
&nbsp; &nbsp; AND 0 &lt;= CAST(PARSENAME(@IsIPv4,1) as SMALLINT) AND CAST(PARSENAME(@IsIPv4,1) as SMALLINT) &lt;= 255 <br />
&nbsp; &nbsp; AND 0 &lt;= CAST(PARSENAME(@IsIPv4,2) as SMALLINT) AND CAST(PARSENAME(@IsIPv4,2) as SMALLINT) &lt;= 255 <br />
&nbsp; &nbsp; AND 0 &lt;= CAST(PARSENAME(@IsIPv4,3) as SMALLINT) AND CAST(PARSENAME(@IsIPv4,3) as SMALLINT) &lt;= 255 <br />
&nbsp; &nbsp; AND 0 &lt;= CAST(PARSENAME(@IsIPv4,4) as SMALLINT) AND CAST(PARSENAME(@IsIPv4,4) as SMALLINT) &lt;= 255 <br />
&nbsp; &nbsp; ) <br />
SELECT @IsOK = 1 &nbsp;<br />
ELSE <br />
SET @IsOK = 0 <br />
RETURN @IsOK &nbsp;<br />
END</div></div>
<p><strong>&#8211;> Test de Stockage d&rsquo;IP</strong></p>
<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 TABLE TableIPv4 (ID INT IDENTITY, IPv4 CHAR(19) CHECK (dbo.IsIPv4 (IPv4)=1)) <br />
INSERT TableIPv4 VALUES('10.276.33.25')</div></div>
<blockquote><p>Msg 547, Level 16, State 0, Line 1<br />
The INSERT statement conflicted with the CHECK constraint &laquo;&nbsp;CK__TableIPv4__IPv4__090A5324&Prime;. The conflict occurred in database &laquo;&nbsp;AdventureWorks&nbsp;&raquo;, table &laquo;&nbsp;dbo.TableIPv4&Prime;, column &lsquo;IPv4&prime;.<br />
The statement has been terminated.</p></blockquote>
<p><code class="codecolorer text default"><span class="text">INSERT TableIPv4 VALUES('10.226.33.25')</span></code></p>
<blockquote><p>
(1 row(s) affected)
</p></blockquote>
<p>Comment stocker des adresses mac sous SQL Server ?<br />
Rappelons que l&rsquo;adresse MAC-48 est constituée de 48 bits (6 octets) se représente sous la forme hexadécimale avec les octets séparés par deux point (:) ou par un tiret (-)<br />
Exemples :<br />
&#8212;&#8212;&#8211;<br />
08-00-2B-01-02-03<br />
08:00:2B:01:02:03 </p>
<p>=> <strong>Stocker ADRESSE MAC sous SQL Server </strong></p>
<p><strong>&#8211;> Création de la fonction de validation MAC-48</strong></p>
<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 &nbsp;FUNCTION [dbo].[IsMacAddress48](@MacAddress CHAR(17)) <br />
RETURNS SMALLINT <br />
AS <br />
BEGIN <br />
DECLARE @IsOK SMALLINT; <br />
-- Vérifier si on que des nombres en dehors des points <br />
IF (LEN(REPLACE(REPLACE(@MacAddress,'-',''),':',''))= 12 <br />
-- Vérifier s'il y a 3 points dans la chaine &nbsp; &nbsp;<br />
&nbsp; &nbsp; AND (@MacAddress LIKE '[0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F][0-9a-fA-F]-[0-9a-fA-F][0-9a-fA-F]' <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;OR @MacAddress LIKE '[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]:[0-9a-fA-F][0-9a-fA-F]' <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;) &nbsp;<br />
&nbsp; &nbsp; ) <br />
SELECT @IsOK = 1 &nbsp;<br />
ELSE <br />
SET @IsOK = 0 <br />
RETURN @IsOK &nbsp;<br />
END</div></div>
<p><strong>&#8211;> Test de Stockage de MAC-48</strong></p>
<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 TABLE TableMacAddress48 (ID INT IDENTITY, MacAddress48 CHAR(17) CHECK (dbo.IsMacAddress48(MacAddress48)=1)) <br />
INSERT TableMacAddress48 VALUES('08-00-2G-01-02-03')</div></div>
<blockquote><p>
Msg 547, Level 16, State 0, Line 1<br />
The INSERT statement conflicted with the CHECK constraint &laquo;&nbsp;CK__TableMacA__MacAd__0CDAE408&Prime;. The conflict occurred in database &laquo;&nbsp;AdventureWorks&nbsp;&raquo;, table &laquo;&nbsp;dbo.TableMacAddress48&Prime;, column &lsquo;MacAddress48&prime;.<br />
The statement has been terminated.
</p></blockquote>
<p>INSERT TableMacAddress48 VALUES(&rsquo;08-00-2B-01-02-03&prime;)</p>
<blockquote><p>
(1 row(s) affected)
</p></blockquote>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
&#8212; Etienne ZINZINDOHOUE<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>Transactions implicite, explicite et autocommit</title>
		<link>https://blog.developpez.com/zinzineti/p11007/sql-server-2008/transactions_implicite_explicite_et_auto</link>
		<comments>https://blog.developpez.com/zinzineti/p11007/sql-server-2008/transactions_implicite_explicite_et_auto#comments</comments>
		<pubDate>Tue, 01 May 2012 00:24:07 +0000</pubDate>
		<dc:creator><![CDATA[zinzineti]]></dc:creator>
				<category><![CDATA[SQL SERVER 2008]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Petite discussion sur les transactions implicite, explicite et auto-commit d&#8217;une part et variation de la variable @@TRANCOUNT en fonction de l&#8217;option SET IMPLICIT_TRANSACTIONS d&#8217;autre part. Il s&#8217;agit ici des transactions SQL, des transactions au sein du SGBD SQL Server. => Transaction explicite &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;- Une transaction est dite explicite lorsqu&#8217;on indique le début (BEGIN TRANSACTION) et la fin (COMMIT TRANSACTION ou ROLLBACK TRANSACTION) de la transaction Exemple &#8212;&#8212;&#8211; BEGIN TRANSACTION -- ici instruction(s) SQL create table [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Petite discussion sur les transactions implicite, explicite et auto-commit d&rsquo;une part et variation de la variable @@TRANCOUNT en fonction de l&rsquo;option SET IMPLICIT_TRANSACTIONS d&rsquo;autre part. Il s&rsquo;agit ici des transactions SQL, des transactions au sein du SGBD SQL Server.<br />
<span id="more-104"></span><br />
=> <strong>Transaction  explicite</strong><br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Une transaction est dite explicite lorsqu&rsquo;on indique le début (BEGIN TRANSACTION) et la fin (COMMIT TRANSACTION ou ROLLBACK TRANSACTION) de la transaction</p>
<p>Exemple<br />
&#8212;&#8212;&#8211;</p>
<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">BEGIN TRANSACTION <br />
-- ici instruction(s) SQL <br />
create table t (col char(1)) <br />
insert into t values ('1') <br />
COMMIT TRANSACTION</div></div>
<p>=> <strong>Transaction implicite</strong><br />
Une transaction est dite implicite lorsqu&rsquo;il n&rsquo;y a pas de BEGIN TRANSACTION pour indiquer le début de la transaction. Cette dernière est automatiquement démarrée par l&rsquo;une des commandes :<br />
<code class="codecolorer text default"><span class="text">ALTER TABLE, CREATE, DELETE, DROP, FETCH, GRANT, INSERT, OPEN, REVOKE, SELECT, TRUNCATE TABLE, UPDATE</span></code> <strong>mais il est indispensable de valider la transaction implicite par COMMIT TRANSACTION ou de l&rsquo;annuler avec ROLLBACK TRANSACTION</strong>.<br />
Pour activer ce mode de fonctionnement sous SQL Server, il suffit d&rsquo;exécuter la commande :<br />
<strong>SET IMPLICIT_TRANSACTIONS ON</strong></p>
<p>Exemple<br />
&#8212;&#8212;&#8211;</p>
<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">SET IMPLICIT_TRANSACTIONS ON <br />
-- ici instruction(s) SQL <br />
create table t (col char(1)) <br />
insert into t values ('1') <br />
COMMIT TRANSACTION</div></div>
<p>=> <strong>Autocommit</strong><br />
Si en l&rsquo;abscence des verbes <strong>BEGIN TRANSACTION</strong>, <strong>COMMIT TRANSACTION</strong> ou <strong>ROLLBACK TRANSACTION</strong> chaque instruction SQL est considérée comme une transaction alors on dit qu&rsquo;on est en mode autocommit. Dans ce cas chaque instruction SQL est automatiquement validée (s&rsquo;il n&rsquo;y a pas d&rsquo;erreur) ou annuler (s&rsquo;il y a erreur). C&rsquo;est le mode de fonctionnement par défaut sous SQL Server. Pour activer ce mode de fonctionnement sous SQL Server, il suffit d&rsquo;exécuter la commande :<br />
<strong>SET IMPLICIT_TRANSACTIONS OFF</strong></p>
<p>Exemple<br />
&#8212;&#8212;&#8211;</p>
<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">SET IMPLICIT_TRANSACTIONS OFF <br />
create table t (col char(1)) <br />
insert into t values ('1') <br />
insert into t values ('12') -- échec lors de l'insertion de cette ligne <br />
select * from t &nbsp;-- on a une ligne dans la table</div></div>
<p>=> <strong>@@TRANCOUNT en fonction de l&rsquo;option IMPLICIT_TRANSACTIONS</strong><br />
<strong>&#8211;>1# @@TRANCOUNT et SET IMPLICIT_TRANSACTIONS OFF</strong></p>
<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">SET IMPLICIT_TRANSACTIONS OFF <br />
print '@@TRANCOUNT avant begin : ' + cast (@@TRANCOUNT as char(1)) <br />
BEGIN TRANSACTION <br />
print '@@TRANCOUNT après begin : ' + cast (@@TRANCOUNT as char(1)) <br />
create table t (col char(1)) <br />
print '@@TRANCOUNT après create : ' + cast (@@TRANCOUNT as char(1)) <br />
insert into t values ('1') <br />
print '@@TRANCOUNT après insert : ' + cast (@@TRANCOUNT as char(1)) <br />
COMMIT TRANSACTION <br />
print '@@TRANCOUNT après commit : ' + cast (@@TRANCOUNT as char(1))</div></div>
<blockquote><p>Résultat<br />
&#8212;&#8212;&#8212;<br />
@@TRANCOUNT avant begin : 0<br />
@@TRANCOUNT après begin : 1<br />
@@TRANCOUNT après create : 1<br />
(1 row(s) affected)<br />
@@TRANCOUNT après insert : 1<br />
@@TRANCOUNT après commit : 0</p></blockquote>
<p><strong>&#8211;>2#@@TRANCOUNT et IMPLICIT_TRANSACTIONS ON avec BEGIN TRANSACTION </strong></p>
<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">SET IMPLICIT_TRANSACTIONS ON <br />
print '@@TRANCOUNT avant begin : ' + cast (@@TRANCOUNT as char(1)) <br />
BEGIN TRANSACTION <br />
print '@@TRANCOUNT après begin : ' + cast (@@TRANCOUNT as char(1)) <br />
create table t (col char(1)) <br />
print '@@TRANCOUNT après create : ' + cast (@@TRANCOUNT as char(1)) <br />
insert into t values ('1') <br />
print '@@TRANCOUNT après insert : ' + cast (@@TRANCOUNT as char(1)) <br />
COMMIT TRANSACTION <br />
print '@@TRANCOUNT après commit : ' + cast (@@TRANCOUNT as char(1))</div></div>
<blockquote><p>Résultat<br />
&#8212;&#8212;&#8212;<br />
@@TRANCOUNT avant begin : 0<br />
@@TRANCOUNT après begin : 2<br />
@@TRANCOUNT après create : 2<br />
(1 row(s) affected)<br />
@@TRANCOUNT après insert : 2<br />
@@TRANCOUNT après commit : 1
</p></blockquote>
<p><strong>&#8211;>3#@@TRANCOUNT et IMPLICIT_TRANSACTIONS ON sans BEGIN TRANSACTION</strong></p>
<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">SET IMPLICIT_TRANSACTIONS ON <br />
print '@@TRANCOUNT après set implicit on &nbsp;: ' + cast (@@TRANCOUNT as char(1)) <br />
create table t (col char(1)) <br />
print '@@TRANCOUNT après create : ' + cast (@@TRANCOUNT as char(1)) <br />
insert into t values ('1') <br />
print '@@TRANCOUNT après insert : ' + cast (@@TRANCOUNT as char(1)) <br />
COMMIT TRANSACTION <br />
print '@@TRANCOUNT après commit : ' + cast (@@TRANCOUNT as char(1))</div></div>
<blockquote><p>Résultat<br />
&#8212;&#8212;&#8212;<br />
@@TRANCOUNT après set implicit on  : 1<br />
@@TRANCOUNT après create : 1<br />
(1 row(s) affected)<br />
@@TRANCOUNT après insert : 1<br />
@@TRANCOUNT après commit : 0
</p></blockquote>
<p>Bonne fête du travail <img src="https://blog.developpez.com/zinzineti/wp-includes/images/smilies/icon_smile.gif" alt=":-)" class="wp-smiley" /> </p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Etienne ZINZINDOHOUE<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>19</slash:comments>
		</item>
		<item>
		<title>blocage &#8211; interblocage : lock &#8211; deadlock</title>
		<link>https://blog.developpez.com/zinzineti/p10978/sql-server-2008/blocage_interblocage_lock_deadlock</link>
		<comments>https://blog.developpez.com/zinzineti/p10978/sql-server-2008/blocage_interblocage_lock_deadlock#comments</comments>
		<pubDate>Mon, 23 Apr 2012 18:47:33 +0000</pubDate>
		<dc:creator><![CDATA[zinzineti]]></dc:creator>
				<category><![CDATA[SQL SERVER 2008]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Lorsque deux processus (instructions sql) tentent simultanément de modifier (INSERT,UPDATE,DELETE) les mêmes données, le SGBD empêche les traitements simultanés de ces processus afin de protéger les données et l&#8217;intégrité de la base. SQL Server crée un verrou pour chaque processus et les gère selon un agorithme FIFO (First In First Out). Dans ce cas SQL Server impose un traitement en série dans instructions, ce qui entraine donc des temps d&#8217;attente, donc des blocages. Mais lorsque [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Lorsque deux processus (instructions sql) tentent simultanément de modifier (INSERT,UPDATE,DELETE) les mêmes données, le SGBD empêche les traitements simultanés de ces processus afin de protéger les données et l&rsquo;intégrité de la base. SQL Server crée un verrou pour chaque processus et les gère selon un agorithme FIFO (First In First Out). Dans ce cas SQL Server impose un traitement en série dans instructions, ce qui entraine donc des temps d&rsquo;attente, donc des blocages. Mais lorsque ce blocage dure longtemps, les applications qui pointent vers la base de données deviennent inutilisables et les utilisateurs ne sont pas contents &#8230;<br />
Dans ce billet, nous mettre en évidence quelques situations provoquant des blocages, des interblocages et des pistes pour anticiper/gérer aux mieux ces situations. <span id="more-103"></span></p>
<p>Dans les exemples qui vont suivre nous allons utiliser deux sessions via SQL Server Management Studio (ssms)<br />
Pour passer d&rsquo;une session à une autre j&rsquo;ai préféré la présentation ci-dessous pour plus de confort visuel.<br />
Une fois les deux fenêtre ouvertes, clique droit sur l&rsquo;onglet</p>
<p><img src="http://zinzineti.ftp-developpez.com/images/session1.jpg" alt="visu1" title="visu1" /></p>
<p>Choisir l&rsquo;affichage verticale des sessions</p>
<p><img src="http://zinzineti.ftp-developpez.com/images/session2.jpg" alt="visu1" title="visu1" /></p>
<p>=> <strong>Mise en évidence d&rsquo;un blocage </strong></p>
<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">-- Création de la table &nbsp;<br />
IF OBJECT_ID('T') IS NOT NULL DROP TABLE T &nbsp;<br />
CREATE TABLE T (id INT,col CHAR(1)) &nbsp;<br />
-- insertion des données dans la table &nbsp;<br />
INSERT INTO T (id,col) VALUES (1,'a') &nbsp;<br />
INSERT INTO T (id,col) VALUES (2,'b')</div></div>
<p>Supposons que deux utilisateurs veulent exécuter presque au même moment, la même transaction. </p>
<p>&#8211;> <strong>Session 1</strong> (utilisateur 1)  : démarrer la transaction suivante<br />
&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<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">BEGIN TRAN &nbsp;<br />
UPDATE T &nbsp;<br />
SET col = 'c' <br />
WHERE id = 1 <br />
/* <br />
Résultat : UPDATE effectué dans la session 1 <br />
*/</div></div>
<p>&#8211;> <strong>Session 2</strong> (utilisateur 2)  : démarrer la transaction suivante<br />
&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<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">BEGIN TRAN &nbsp;<br />
UPDATE T <br />
SET col = 'd' <br />
WHERE id = 1 <br />
/* <br />
Résultat : UPDATE mis en attente dans la session 2, le processus est bloqué ! <br />
*/</div></div>
<p><strong>&#8211;> Comment identifier les processus bloqués sur une instance ?</strong></p>
<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 session_id,start_time,wait_time,status,wait_type,command,blocking_session_id,transaction_id &nbsp; <br />
FROM sys.dm_exec_requests &nbsp;<br />
WHERE blocking_session_id &lt;&gt; 0 <br />
&nbsp;<br />
SELECT spid,open_tran,blocked &nbsp;<br />
FROM sys.sysprocesses WHERE blocked &lt;&gt; 0</div></div>
<p>L&rsquo;ID des sessions (session_id ou spid) responsables du blocage sont donc identifiés .</p>
<p><strong>&#8211;> Comment mettre fin à ce blocage ?</strong></p>
<p>Il suffit de tuer le processus responsable du blocage à l&rsquo;aide la commande suivante :</p>
<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">KILL &lt;spid&gt; <br />
Exemple <br />
-------- <br />
KILL 54</div></div>
<p>Cette commande annule toutes les transactions liées à ce processus. Cette opération est tracé dans les fichiers de<br />
d&rsquo;erreurs SQL Server (ERRLOG) et Event Viewver Application (\WINDOWS\system32\config\AppEvent.Evt)</p>
<p>=> <strong>Interblocage des transactions SQL  : deadlock &#8211; verrou mortel</strong><br />
Le phénomène d&rsquo;interblocage (deadlock) se présente lorsque plusieurs processus (transactions sql) se bloquent de façon à ce qu&rsquo;aucun de ces processus ne peut se débloquer quelque soit la durée d&rsquo;attente.<br />
Ceci apparaît lors de la modification des données(INSERT, UPDATE, DELETE). Dans ce cas le gestionnaire de verrou SQL Server détecte qu&rsquo;il y a un deadlock et va donc tuer un des processus, annuler la totalité des<br />
transactions sql du processus liquidé et renvoyé le code d&rsquo;erreur 1205 à la session concernée.<br />
Sur quels critères SQL Server choisit la processus à tuer ? Pour deux transactions ayant la même priorité, les règles de sélection du processus à liquider ne sont pas clairement détaillées par Microsoft .<br />
Rappelons que l&rsquo;option SET DEADLOCK_PRIORITY { LOW | NORMAL | HIGH } permet de spécifier la priorité d&rsquo;une transaction en cas de deadlock.</p>
<p>Ouvrons une petite parenthèse : Sous ORACLE le code d&rsquo;erreur des deadlocks est ORA-00060 (détection d&rsquo;interblocage pendant l&rsquo;attente d&rsquo;une ressource) et fermons cette parenthèse</p>
<p><strong>&#8211;> Mise en évidence du phénonème de verrou mortel (deadlock)</strong><br />
<strong>&#8211; # Cas une table : objet non distinct</strong></p>
<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">-- Création de la table <br />
IF OBJECT_ID('T') IS NOT NULL DROP TABLE T <br />
CREATE TABLE T (id INT,col CHAR(1)) &nbsp;<br />
-- insertion des données dans la table &nbsp;<br />
INSERT INTO T (id,col) VALUES (1,'a') &nbsp;<br />
INSERT INTO T (id,col) VALUES (2,'b')</div></div>
<p>Dans la <strong>session 1</strong>, démarrer la transaction suivante :</p>
<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">BEGIN TRAN &nbsp;<br />
UPDATE T <br />
SET col = 'c' <br />
WHERE id = 2</div></div>
<p>/*<br />
Résultat : UPDATE effectué dans la session 1<br />
*/</p>
<p>&#8212; Dans la <strong>session 2</strong>, démarrer la transaction suivante  :</p>
<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">BEGIN TRAN &nbsp;<br />
UPDATE T <br />
SET col = 'e' <br />
WHERE id = 1</div></div>
<p>/*<br />
Résultat : UPDATE en attente dans la session 2<br />
*/</p>
<p>&#8212; Dans la <strong>session 1</strong>, exécuter la requête suivante :</p>
<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">UPDATE T <br />
SET col = 'f' <br />
WHERE id = 2</div></div>
<p>/*<br />
Résultat : un message d&rsquo;erreur apparaît dans la session 1.<br />
Msg 1205, Level 13, State 45, Line 1<br />
Transaction (Process ID 52) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.</p>
<p>dans la session 2, la mis à jour est réalisée</p>
<p>&#8212; Vérification de la modification dans la session 2<br />
<code class="codecolorer text default"><span class="text">select * from T where id = 1</span></code></p>
<p>id	col<br />
&#8212;&#8212;-<br />
1	e</p>
<p>*/</p>
<p><strong>Comment résoudre ce problème dans ce cas  ?</strong></p>
<p>Pour contouner ce deadlock, il suffit de créer sur cette table un index du genre :<br />
<code class="codecolorer text default"><span class="text">CREATE INDEX IX ON T (id) INCLUDE (col)</span></code><br />
Et le tour est joué !</p>
<p><strong>&#8211; # Cas de deux tables distinctes : objets distincts </strong></p>
<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">-- Création des deux tables <br />
IF OBJECT_ID('T1') IS NOT NULL DROP TABLE T1 &nbsp;<br />
IF OBJECT_ID('T2') IS NOT NULL DROP TABLE T2 &nbsp;<br />
CREATE TABLE T1 (id1 INT,col1 CHAR(1)) &nbsp;<br />
CREATE TABLE T2 (id2 INT,col2 CHAR(1)) &nbsp;<br />
-- insertion des données dans la table &nbsp;<br />
INSERT INTO T1 (id1,col1) VALUES (1,'a') &nbsp;<br />
INSERT INTO T2 (id2,col2) VALUES (2,'b')</div></div>
<p>Dans la <strong>session 1</strong>, démarrer la transaction suivante :</p>
<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">BEGIN TRAN &nbsp;<br />
UPDATE T1 <br />
SET col1 = 'c' <br />
WHERE id1 = 1</div></div>
<p>/*<br />
Résultat : UPDATE effectué dans la session 1<br />
*/</p>
<p>Dans la <strong>session 2</strong>, démarrer la transaction suivante  :</p>
<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">BEGIN TRAN &nbsp;<br />
UPDATE T2 <br />
SET col2 = 'd' <br />
WHERE id2 = 2</div></div>
<p>/*<br />
Résultat : UPDATE effectué dans la session 2<br />
*/</p>
<p>Dans la <strong>session 1</strong>, exécuter la requête suivante :</p>
<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">UPDATE T2 <br />
SET col2 = 'e' <br />
WHERE id2 = 2</div></div>
<p>/*<br />
Résultat : UPDATE en attente dans la session 2<br />
*/</p>
<p>Dans la <strong>session 2</strong>, exécuter la requête suivante :</p>
<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">UPDATE T1 <br />
SET col1 = 'f' <br />
WHERE id1 = 1</div></div>
<p>/*<br />
Résultat :<br />
Msg 1205, Level 13, State 45, Line 1<br />
Transaction (Process ID 52) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.<br />
*/</p>
<p>Dans ce cas, la <strong>création des index sur les tables T1 et T2 ne permet pas de solutionner le problème</strong>. Une piste consiste à catcher l&rsquo;erreur 1205 (TRY/CATCH) puis tenter de rejouer la transaction. D&rsquo;autres solutions existent, mais avant de les présenter examinons d&rsquo;abord comment identifier les instructions responsables du deadlock sur une instance</p>
<p>=> <strong>Comment identifier les instructions responsable du deadlock ?</strong><br />
<strong>&#8211; # Avec XEvents</strong><br />
Sous SQL Server 2008 et + on peut utiliser XEvents pour collecter les informations relatives aux deadlocks.<br />
La requête ci-dessous renvoie un fichier XML montrant les requêtes responsables des deadlocks. On peut faire des filtres horodatages pour cibler les deadlocks ayant lieu à un moment donnée.</p>
<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 @xml xml <br />
SELECT @xml = CAST(st.target_data as xml) <br />
FROM sys.dm_xe_session_targets st <br />
INNER JOIN sys.dm_xe_sessions s on s.address = st.event_session_address <br />
INNER JOIN sys.server_event_sessions es on s.name = es.name <br />
WHERE s.name = 'system_health' AND st.target_name = 'ring_buffer' <br />
-- and convert(varchar(8),s.create_time,112) = '20120422' -- cibler une date particulière <br />
SELECT @xml.query('/RingBufferTarget/event [@name=&quot;xml_deadlock_report&quot;]')</div></div>
<p><strong>&#8211; # Avec la trace 1222 </strong><br />
Activer Trace Flag 1222 option -1 :<br />
{1222} : pour collecter toutes les informations relatives aux deadlocks.<br />
{-1} : appliquer la collecte des traces deadlock à toutes les sessions et à tous les utilisateurs</p>
<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">DBCC TRACEON (1222, -1) <br />
&nbsp;<br />
-- voir le status des traces <br />
DBCC TRACESTATUS(1222,-1)</div></div>
<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">TraceFlag &nbsp; &nbsp;Status &nbsp; &nbsp;Global &nbsp; &nbsp; &nbsp; &nbsp;Session <br />
----------------------------------------------------- <br />
1222 &nbsp;----- &nbsp; &nbsp; &nbsp;1 &nbsp; ----- &nbsp; &nbsp; &nbsp; &nbsp; 1 &nbsp; &nbsp; ----- &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 0</div></div>
<p>L&rsquo;activation de la trace 1222 option -1 permet de cibler le problème et d&rsquo;identifier les instructions responsables de l&rsquo;interblocage. Voici ce qu&rsquo;on observe dans le fichier ERRORLOG après activation des traces et reproduction du premier scénario (cas d&rsquo;un même objet) deadlock </p>
<blockquote><p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
&#8212; Fichier de log emplacement : \Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\Log\ERRORLOG<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
2012-04-22 19:43:47.15 spid56      DBCC TRACEON 1222, server process ID (SPID) 56. This is an informational message only; no user action is required.<br />
2012-04-22 19:44:11.92 spid53      Starting up database &lsquo;AdventureWorks2008R2&prime;.<br />
2012-04-22 19:44:36.56 spid19s     deadlock-list<br />
2012-04-22 19:44:36.56 spid19s      deadlock victim=processd0bc78<br />
2012-04-22 19:44:36.56 spid19s       process-list<br />
2012-04-22 19:44:36.56 spid19s        process id=processd0bc78 taskpriority=0 logused=208 waitresource=RID: 6:1:15160:0 waittime=1429 ownerId=170698 transactionname=user_transaction lasttranstarted=2012-04-19T19:44:21.183 XDES=0x4338ac08 lockMode=U schedulerid=1 kpid=5488 status=suspended spid=56 sbid=0 ecid=0 priority=0 trancount=2 lastbatchstarted=2012-04-19T19:44:35.137 lastbatchcompleted=2012-04-19T19:44:21.190 lastattention=2012-04-19T16:41:08.620 clientapp=Microsoft SQL Server Management Studio &#8211; Query hostname=XX-XX hostpid=4064 loginname=xx isolationlevel=read committed (2) xactid=170698 currentdb=6 lockTimeout=4294967295 clientoption1=671090784 clientoption2=390200<br />
2012-04-22 19:44:36.56 spid19s         executionStack<br />
2012-04-22 19:44:36.56 spid19s          frame procname=adhoc line=1 stmtstart=58 sqlhandle=0x020000003737ba3a21719d4dd9cf31b6b7211a6fb6e2e5d1<br />
2012-04-22 19:44:36.56 spid19s     UPDATE [T] set [col] = @1  WHERE [id]=@2<br />
2012-04-22 19:44:36.56 spid19s          frame procname=adhoc line=1 sqlhandle=0x02000000fcf4a3305a4444f97c3ec147da8f12aa2b69fcd2<br />
2012-04-22 19:44:36.56 spid19s     UPDATE T<br />
2012-04-22 19:44:36.56 spid19s     SET col = &lsquo;f&rsquo;<br />
2012-04-22 19:44:36.56 spid19s     WHERE id = 2<br />
2012-04-22 19:44:36.56 spid19s         inputbuf<br />
&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;..<br />
&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;..<br />
&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;&#8230;..
</p></blockquote>
<p>Dans le fichier de trace ERRLOG on identifie clairement :<br />
* les requêtes responsables du deadlock,<br />
* les objets concernés (nom du serveur,nom de la base,nom des tables,&#8230;)<br />
<strong>&#8211; # Autres méthodes d&rsquo;identification des deadlocks</strong> :<br />
Mise en place des traces via SQL Server Profiler ou ajout de l&rsquo;option &lsquo;-T1222&prime; lors du redémarrage de l&rsquo;instance SQL SERVER</p>
<p>=> <strong>Quelques pistes pour anticiper/gérer les deadlocks</strong></p>
<p>&#8211;# Ecrire des requêtes courtes et précises sur des tables bien indexées.<br />
&#8211;# Respecter le même ordre d&rsquo;exécution des requêtes dans les transactions (voir exemple plus bas)<br />
&#8211;# Catcher (TRY/CATCH) l&rsquo;erreur 1205  puis  ré-exécuter la transaction (voir exemple plus bas)<br />
&#8211;# Utiliser si possible l&rsquo;option SET DEADLOCK_PRIORITY pour générer les priorités entre les transactions en cas de deadlock</p>
<p><strong>&#8211;> Exemples : Gestion des erreurs 1205 (TRY/CATCH)</strong></p>
<p><strong>&#8211; Exemple 1 </strong> : TRY/CATCH simple<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</p>
<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">BEGIN TRY <br />
/* Mettre ici les opérations à effectuer */ <br />
END TRY <br />
BEGIN CATCH <br />
IF ERROR_NUMBER() = 1205 RAISERROR('deadlock erreur !', 16, 1); &nbsp;<br />
ELSE RAISERROR('Pas de deadlock mais une autre erreur est survenue ! ', 16, 1); &nbsp;<br />
END CATCH;</div></div>
<p><strong>&#8211; Exemple 2</strong> : TRY/CATCH avec Re-exécution de la requête<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</p>
<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 @RetriesCounter int; &nbsp;<br />
SET @RetriesCounter = 3; &nbsp;<br />
WHILE @RetriesCounter &gt; 0 &nbsp;<br />
BEGIN &nbsp;<br />
&nbsp; &nbsp; BEGIN TRY &nbsp;<br />
/* Mettre ici les opérations à effectuer */ &nbsp;<br />
&nbsp; &nbsp; END TRY &nbsp;<br />
&nbsp; &nbsp; BEGIN CATCH &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; IF ERROR_NUMBER() = 1205 &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; BEGIN &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SET @RetriesCounter = @RetriesCounter - 1; &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;IF @RetriesCounter = 0 RAISERROR('Impossible de terminer la transaction !', 16, 1); &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; END &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; ELSE &nbsp;<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; RAISERROR('Pas de deadlock mais autre erreur ! ', 16, 1); &nbsp;<br />
&nbsp; &nbsp; END CATCH &nbsp;<br />
END;</div></div>
<p>&#8211;> <strong>Exemple : Respecter le même ordre d&rsquo;exécution des transactions</strong></p>
<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">IF OBJECT_ID('T1') IS NOT NULL DROP TABLE T1 &nbsp;<br />
IF OBJECT_ID('T2') IS NOT NULL DROP TABLE T2 &nbsp;<br />
CREATE TABLE T1 (id1 INT,col1 CHAR(1)) &nbsp;<br />
CREATE TABLE T2 (id2 INT,col2 CHAR(1)) &nbsp;<br />
-- insertion des données dans la table &nbsp;<br />
INSERT INTO T1 (id1,col1) VALUES (1,'a') &nbsp;<br />
INSERT INTO T2 (id2,col2) VALUES (2,'b')</div></div>
<p>exécuter dans la <strong>session 1</strong></p>
<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">BEGIN TRAN <br />
UPDATE t1 SET col1 = 'x' <br />
WAITFOR DELAY '00:00:05' <br />
UPDATE t2 SET col2 = 'y' <br />
ROLLBACK TRAN</div></div>
<p>exécuter dans la <strong>session 2</strong></p>
<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">BEGIN TRAN <br />
UPDATE t2 SET col2 = 'y' <br />
WAITFOR DELAY '00:00:05' <br />
UPDATE t1 SET col1 = 'x' <br />
ROLLBACK TRAN</div></div>
<p>/* Résultat :<br />
(1 row(s) affected)<br />
Msg 1205, Level 13, State 45, Line 6<br />
Transaction (Process ID 55) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.<br />
*/</p>
<p>&#8211;> <strong>Solution : Exécution des requêtes dans le même ordre</strong></p>
<p>exécuter dans la <strong>session 1</strong></p>
<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">BEGIN TRAN <br />
UPDATE t1 SET col1 = 'x' <br />
WAITFOR DELAY '00:00:05' <br />
UPDATE t2 SET col2 = 'y' <br />
ROLLBACK TRAN</div></div>
<p>exécuter dans la <strong>session 2</strong></p>
<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">BEGIN TRAN <br />
UPDATE t1 SET col1 = 'x' <br />
WAITFOR DELAY '00:00:05' <br />
UPDATE t2 SET col2 = 'y' <br />
ROLLBACK TRAN</div></div>
<p>/* Résultat : Opération effectuée avec succès */</p>
<p>Il y a encore des choses à dire sur les deadlocks. Mais l&rsquo;idée c&rsquo;est d&rsquo;avoir ici une petite synthèse<br />
du phénomène et d&rsquo;essayer de présenter quelques pistes pour gérer au mieux les blocages et interblocages</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Etienne ZINZINDOHOUE<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>TRANSACTION SQL : mode d&#8217;accès &#8211; niveau d&#8217;isolation &#8211; niveau de diagnostic</title>
		<link>https://blog.developpez.com/zinzineti/p10932/sql-server-2008/transaction_sql_mode_d_acces_niveau_d_is</link>
		<comments>https://blog.developpez.com/zinzineti/p10932/sql-server-2008/transaction_sql_mode_d_acces_niveau_d_is#comments</comments>
		<pubDate>Wed, 11 Apr 2012 18:17:15 +0000</pubDate>
		<dc:creator><![CDATA[zinzineti]]></dc:creator>
				<category><![CDATA[SQL SERVER 2008]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Une transaction SQL est caractérisée par un mode d&#8217;accès (READ ONLY ou READ WRITE),un niveau d&#8217;isolation (READ UNCOMMITED,READ COMMITED,REPEATABLE READ,SERIALIZABLE) et un niveau de diagnostic (DIAGNOSTIC SIZE).Le mode d&#8217;accès permet de préciser explicitement les opérations (lecture seule ou lecture/écriture) à réaliser sur la transaction. Le niveau d&#8217;isolation permet d&#8217;indiquer avec quel acharnement on souhaite protéger les transactions les unes des autres. Le niveau de diagnostic permet de limiter le nombre d&#8217;ereur dans la transaction. Selon [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Une transaction SQL est caractérisée par un mode d&rsquo;accès (READ ONLY ou READ WRITE),un niveau d&rsquo;isolation (READ UNCOMMITED,READ COMMITED,REPEATABLE READ,SERIALIZABLE) et un niveau de diagnostic (DIAGNOSTIC SIZE).Le mode d&rsquo;accès permet de préciser explicitement les opérations (lecture seule ou lecture/écriture) à réaliser sur la transaction. Le niveau d&rsquo;isolation permet d&rsquo;indiquer avec quel acharnement on souhaite protéger les transactions les unes des autres. Le niveau de diagnostic permet de limiter le nombre d&rsquo;ereur dans la transaction. Selon la norme SQL une transaction doit être paramétrée de la façon suivante :</p>
<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">SET [LOCAL] TRANSACTION [READ ONLY | READ WRITE] <br />
[ISOLATION LEVEL {READ COMMITTED | READ UNCOMMITTED | <br />
REPEATABLE READ | SERIALIZABLE}] <br />
[DIAGNOSTIC SIZE int]</div></div>
<p><em>LOCAL</em> : indique que le mode d&rsquo;accès et le niveau d&rsquo;isolation indiqué s&rsquo;applique à la session courante. si le mot clé LOCAL n&rsquo;est pas indiqué la transaction suivante peut changer même si on est sur la même session<br />
<em>READ ONLY</em> : indique que la transaction est une opération de lecture seule<br />
<em>READ WRITE</em> : indique que la transaction peut lire ou modifier des données dans la base<br />
<em>ISOLATION LEVEL</em> : indique le niveau de protection des transactions<br />
<em>DIAGNOSTIC SIZE int</em> : limite le nombre d&rsquo;erreur dans la transaction. plus ce nombre est élévé plus il y a consommation de ressources système. la commande GET DIAGNOSTICS permet de recupérer les messages d&rsquo;erreurs.</p>
<p><strong>SQL SERVER ne respecte pas rigoureusement cette syntaxe</strong>. Il est impossible sous SQL SERVER de préciser de façon explicite le :<br />
<strong>1) mode d&rsquo;accès (READ_ONLY ou READ_WRITE)<br />
2) niveau de DIAGNOSTIC </strong></p>
<p>Seul le niveau d&rsquo;isolation peut être explicitement indiqué sous SQL SERVER. Rappelons que les niveaux d&rsquo;isolation permettent de resoudre les problèmes de lecture sale (dirty read), lecture qui ne peut être répétée (non-repeatable read) et de lecture fantôme (phantom read). Dans ce billet nous allons examiner ces problèmes et mettre en évidence le mécanisme de verrou (lock) posé par SQL SERVER sur une table.<span id="more-102"></span></p>
<p>Dans les exemples qui vont suivre nous allons utiliser deux sessions via SQL Server Management Studio (ssms)<br />
Pour passer d&rsquo;une session à une autre j&rsquo;ai préféré la présentation ci-dessous pour plus de confort visuel.</p>
<p>Une fois les deux fenêtre ouvertes, clique droit sur l&rsquo;onglet</p>
<p><img src="http://zinzineti.ftp-developpez.com/images/session1.jpg" alt="presentation session" title="presentation session" /></p>
<p>Choisir l&rsquo;affichage verticale des sessions</p>
<p><img src="http://zinzineti.ftp-developpez.com/images/session2.jpg" alt="presentation session" title="presentation session" /></p>
<p>Voyons maintenant les différents niveaux d&rsquo;isolation.</p>
<p>=> <strong>READ COMMITED</strong> (c&rsquo;est niveau d&rsquo;isolation par défaut sous SQL SERVER) : isole la transaction des problèmes de lecture sale</p>
<p>&#8211;> <strong>Session 1</strong>  : dans la session 1, exécuter la requête de création et de remplissage de la table<br />
&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<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">-- Création de la table <br />
IF OBJECT_ID('ExpertSGBD') IS NOT NULL DROP TABLE ExpertSGBD <br />
CREATE TABLE ExpertSGBD (id int,prenom varchar(20),nbpoints int,date_inscription datetime) <br />
-- insertion des données dans la table <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (1,'Frédéric',17581,'20020501') <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (2,'David',6704,'20020801') <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (3,'Nicolas',8488,'20050101') <br />
-- vérification des données insérées <br />
SELECT * FROM ExpertSGBD <br />
&nbsp;<br />
--Démarrer dans la session 1 la transaction suivante : <br />
BEGIN TRAN <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (4,'Etienne',2408,'20100301') <br />
-- Vérifier l'insertion de la nouvelle ligne dans la session 1, remarquer que jusque là on n'a pas encore COMMITé ni ROLLBACKé la transaction &nbsp;<br />
SELECT * FROM ExpertSGBD</div></div>
<p>&#8211;> <strong>Session 2</strong> : dans la session 2, exécuter la requête suivante :<br />
&#8212;&#8212;&#8212;&#8212;-<br />
<code class="codecolorer text default"><span class="text">SELECT * FROM ExpertSGBD</span></code></p>
<p><strong><font color="#0000ff"># Résultat session 2 : La requête ne s&rsquo;exécute pas, elle reste bloquée.<br />
<strong>## Explication</strong> : La transaction en cours d&rsquo;exécution dans la session 1 fait que SQL Server a posé un verrou (LOCK) sur la table, il est donc impossible à d&rsquo;autre session de lire les données de la table </font></strong> </p>
<p>&#8211;> <strong>Session 1</strong>  : Revenons dans la session 1 et validons la transaction en cours avec un COMMIT<br />
&#8212;&#8212;&#8212;&#8212;&#8211;<br />
<code class="codecolorer text default"><span class="text">COMMIT TRANSACTION</span></code></p>
<p>&#8211;> <strong>Session 2</strong> : Dans la fenêtre de la session 2, exécutons la requête suivante :<br />
&#8212;&#8212;&#8212;&#8212;&#8211;<br />
<code class="codecolorer text default"><span class="text">SELECT * FROM ExpertSGBD</span></code></p>
<p><font color="#0000ff"><strong> # Résultat session 2 : La requête s&rsquo;exécute correctement et la nouvelle ligne apparaît après COMMIT (validation) dans la session 1<br />
<strong> ## Explication</strong> : après validation (COMMIT) de la transaction dans la session 1, le verrou posé par SQL SERVER sur la table est lévé et les données ainsi validées sont accessibles à toutes les sessions</strong> </font></p>
<p><strong><font color="#008000">Mais comment éviter le verrou sur la totalité des lignes de la TABLE et permettre l&rsquo;accessibilité des données initiales validées avant le démarrage de la transaction ? </font></strong><br />
<font color="#0000ff"><strong><br />
## Astuce : faire en sorte que SQL Server utilise par exemple un index et pas un <strong>Table Scan</strong> lors de la lecture des données.</strong></font></p>
<p><strong>Exemple</strong><br />
&#8212;&#8212;&#8211;</p>
<p>&#8211;> <strong>Session 1</strong>  : dans la session 1, exécuter la requête de création et de remplissage de la table<br />
&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<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">IF OBJECT_ID('ExpertSGBD') IS NOT NULL DROP TABLE ExpertSGBD <br />
CREATE TABLE ExpertSGBD (id int,prenom varchar(20),nbpoints int,date_inscription datetime) <br />
-- insertion des données dans la table <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (1,'Frédéric',17581,'20020501') <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (2,'David',6704,'20020801') <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (3,'Nicolas',8488,'20050101') <br />
-- posons un index sur la table &nbsp;<br />
CREATE INDEX IX ON ExpertSGBD (id,prenom,nbpoints,date_inscription)</div></div>
<p>&#8211;> <strong>Session 1</strong> : Démarrer depuis la session 1 la transaction suivante :<br />
&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<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">BEGIN TRAN <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (4,'Etienne',2408,'20100301') <br />
&nbsp;<br />
-- Vérifier l'insertion de la nouvelle ligne depuis la session 1, remarquer que jusque là on n'a pas encore COMMITé ni ROLLBACKé la transaction &nbsp;<br />
SELECT * FROM ExpertSGBD</div></div>
<p>&#8211;> <strong>Session 2</strong> : Dans la fenêtre de la session 2, exécuter la requête suivante<br />
&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p><code class="codecolorer text default"><span class="text">SELECT * FROM ExpertSGBD WHERE ID &lt; 4</span></code></p>
<p><font color="#0000ff"><strong> # Résultat session 2 : Cette fois-ci la requête s&rsquo;exécute. Les données présentes dans la table avant le début de transaction sont affichées.<br />
## Explication : SQL Server utilise l&rsquo;index créé (INDEX SEEK) pour parcourir la table. une analyse du plan d&rsquo;exécution de la requête le montre. Par contre la nouvelle ligne insérée (non COMMITée) visible depuis la session 1 n&rsquo;est pas visible par la session 2 car l&rsquo;exécution de la requête <code class="codecolorer text default"><span class="text">SELECT * FROM ExpertSGBD WHERE ID &lt; 5</span></code> bloque.</strong></font></p>
<p>&#8211;> <strong>Session 1</strong> : Faire un COMMIT TRANSACTION pour valider la transaction<br />
&#8212;&#8212;&#8212;&#8212;&#8211;<br />
<code class="codecolorer text default"><span class="text">COMMIT TRANSACTION</span></code></p>
<p><strong><font color="#008000">Quelles sont les limites du niveau d&rsquo;isolation READ COMMITED ? </font> </strong></p>
<p>READ COMMITED ne protège pas de la lecture non répétable, c&rsquo;est quoi la lecture non répétable ? Pour une même requête, les données affichées variées. Prenons un exemple :</p>
<p><strong>Exemple</strong><br />
&#8212;&#8212;&#8211;</p>
<p>&#8211;> <strong>Session 1</strong> : Exécuter la requête de création et de remplissage de la table<br />
&#8212;&#8212;&#8212;&#8212;&#8211;<br />
&#8212; table</p>
<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">IF OBJECT_ID('ExpertSGBD') IS NOT NULL DROP TABLE ExpertSGBD <br />
CREATE TABLE ExpertSGBD (id int,prenom varchar(20),nbpoints int,date_inscription datetime) <br />
-- insertion des données dans la table <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (1,'Frédéric',17581,'20020501') <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (2,'David',6704,'20020801') <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (3,'Nicolas',8488,'20050101')</div></div>
<p>&#8212; Exécutons dans la session 1 la requête suivante :<br />
<code class="codecolorer text default"><span class="text">SELECT * FROM ExpertSGBD WHERE nbpoints &gt; 17580 -- on a 1 ligne qui s'affiche</span></code></p>
<p><font color="#0000ff"><strong> # Résultat session 1 : On a 1 ligne affichée </strong></font></p>
<p>&#8211;> <strong>Session 2</strong> : Depuis la session 2 exécuter une requête de mise à jour :<br />
&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<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">UPDATE ExpertSGBD &nbsp;<br />
&nbsp; &nbsp; &nbsp;SET nbpoints = 17580 <br />
WHERE id = 1</div></div>
<p><font color="#0000ff"><strong> # Résultat session 2 : Update effectué avec succès </strong></font></p>
<p>&#8211;> <strong>Session 1</strong> : Rejouons à nouveau la même requête SELECT que précedemment<br />
&#8212;&#8212;&#8212;&#8212;&#8211;<br />
<code class="codecolorer text default"><span class="text">SELECT * FROM ExpertSGBD WHERE nbpoints &gt; 17580 -- on a 0 ligne qui s'affiche</span></code></p>
<p><font color="#0000ff"><strong> # Résultat session 1 : Cette fois-ci on a 0 ligne ! la requête SELECT * FROM ExpertSGBD WHERE nbpoints > 17580 n&rsquo;est donc pas repetable sous la session 1 un coup on a 1 ligne et un autre coup on a 0 ligne ! Voilà un des problèmes du niveau d&rsquo;isolation READ COMMITED </strong></font></p>
<p>=> <strong>REPEATABLE READ : protège la transaction des problèmes de lecture sale et lecture qui ne peut être répétée<br />
</strong></p>
<p>&#8212; <strong>Session 1</strong> : Exécuter la requête de création et de remplissage de la table<br />
&#8212;&#8212;&#8212;&#8212;&#8211;<br />
&#8212; Création de la table</p>
<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">IF OBJECT_ID('ExpertSGBD') IS NOT NULL DROP TABLE ExpertSGBD <br />
CREATE TABLE ExpertSGBD (id int,prenom varchar(20),nbpoints int,date_inscription datetime) <br />
-- insertion des données dans la table <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (1,'Frédéric',17581,'20020501') <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (2,'David',6704,'20020801') <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (3,'Nicolas',8488,'20050101')</div></div>
<p>&#8211;> <strong>Session 1</strong> : Démarrer depuis la session 1 la transaction suivante :<br />
&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<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">SET TRANSACTION ISOLATION LEVEL REPEATABLE READ <br />
BEGIN TRAN <br />
SELECT * FROM ExpertSGBD WHERE nbpoints &gt; 17580 -- on a une ligne qui qui s'affiche</div></div>
<p>&#8211;> <strong>Session 2</strong> : Depuis la session 2, mettons à jour une ligne de la table à l&rsquo;aide de la requête suivante :<br />
&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<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">UPDATE ExpertSGBD &nbsp;<br />
&nbsp; &nbsp; &nbsp;SET nbpoints = 17580 <br />
WHERE id = 1</div></div>
<p><font color="#0000ff"><strong> # Résultat session 2 : Impossible de mettre à jour la ligne, LOCK de la Table </strong></font></p>
<p>&#8211;> <strong>Session 1</strong> : Rejouons à nouveau dans la session 1 la requête suivante :<br />
&#8212;&#8212;&#8212;&#8212;&#8211;<br />
<code class="codecolorer text default"><span class="text">SELECT * FROM ExpertSGBD WHERE nbpoints &gt; 17580 -- on a une ligne qui s'affiche</span></code></p>
<p><font color="#0000ff"><strong> # Résultat session 1 : On a toujours une ligne affichée, la requête renvoie toujours les mêmes données.<br />
## Explication : SQL SERVER a posé un verrou sur les lignes en cours de lecture, ainsi aucune modification de ces lignes n&rsquo;est possible par une autre session. Le problème de lecture qui ne peut être répétée est ainsi résolu avec le niveau d&rsquo;isolation REPEATABLE READ </strong></font></p>
<p>&#8211;> <strong>Session 1</strong> : Dans la session 1 faisons un COMMIT pour valider la transaction<br />
&#8212;&#8212;&#8212;&#8212;&#8211;<br />
<code class="codecolorer text default"><span class="text">COMMIT TRANSACTION</span></code></p>
<p><strong><font color="#008000">Quelles sont les limites du niveau d&rsquo;isolation REPEATABLE READ ? </font> </strong> REPEATABLE READ resoud le problème de lecture qui ne peut être répétée, mais pas le problème de lecture fantôme. Prenons un exemple.</p>
<p><strong>Exemple </strong><br />
&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<p>&#8211;> <strong>Session 1 </strong>: Exécuter la requête de création et de remplissage de la table<br />
&#8212;&#8212;&#8212;&#8212;&#8211;<br />
&#8212; Création de la table</p>
<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">IF OBJECT_ID('ExpertSGBD') IS NOT NULL DROP TABLE ExpertSGBD <br />
CREATE TABLE ExpertSGBD (id int,prenom varchar(20),nbpoints int,date_inscription datetime) <br />
-- insertion des données dans la table <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (1,'Frédéric',17581,'20020501') <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (2,'David',6704,'20020801') <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (3,'Nicolas',8488,'20050101')</div></div>
<p>&#8211;> <strong>Session 1</strong> : Démarrer depuis la session 1 la transaction suivante :<br />
&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<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">SET TRANSACTION ISOLATION LEVEL REPEATABLE READ <br />
BEGIN TRAN <br />
SELECT * FROM ExpertSGBD -- on a 3 lignes qui qui s'affiche</div></div>
<p>&#8211;> <strong>Session 2</strong> : Depuis la session 2, insérons une nouvelle à l&rsquo;aide de la requête suivante :<br />
&#8212;&#8212;&#8212;&#8212;&#8211;<br />
<code class="codecolorer text default"><span class="text">INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (4,'Etienne',2408,'20100301')</span></code></p>
<p><font color="#0000ff"><strong> # Résultat session 2 : insertion effectuée avec succès depuis la session 2 </strong></font></p>
<p>&#8211;> <strong>Session 1</strong> : Rejouons dans la session 1, la requête SELECT * FROM ExpertSGBD<br />
&#8212;&#8212;&#8212;&#8212;&#8211;<br />
<code class="codecolorer text default"><span class="text">SELECT * FROM ExpertSGBD -- on a 4 lignes qui s'affiche</span></code></p>
<p><font color="#0000ff"><strong> # Résultat session 1 : Cette fois-ci on a 4 lignes ! une ligne fantôme apparaît ! le niveau d&rsquo;isolation REPEATABLE READ ne resoud pas ce problème de ligne fantôme.</strong></font></p>
<p>&#8211;> <strong>Session 1</strong> : Validons la transaction (COMMIT)<br />
&#8212;&#8212;&#8212;&#8212;&#8211;<br />
<code class="codecolorer text default"><span class="text">COMMIT TRANSACTION</span></code></p>
<p>=> <strong>SERIALIZABLE</strong> : protège la transaction des problèmes de lecture sale, de lecture qui ne peut être répétée et de lecture fantôme</p>
<p>&#8211;> <strong>Session 1</strong> : Exécuter la requête de création et de remplissage de la table<br />
&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<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">-- Création de la table &nbsp;<br />
IF OBJECT_ID('ExpertSGBD') IS NOT NULL DROP TABLE ExpertSGBD <br />
CREATE TABLE ExpertSGBD (id int,prenom varchar(20),nbpoints int,date_inscription datetime) <br />
-- insertion des données dans la table <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (1,'Frédéric',17581,'20020501') <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (2,'David',6704,'20020801') <br />
INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (3,'Nicolas',8488,'20050101')</div></div>
<p>&#8211;> <strong>Session 1</strong> : Démarrer depuis la session 1 la transaction suivante :<br />
&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<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">SET TRANSACTION ISOLATION LEVEL SERIALIZABLE <br />
BEGIN TRAN <br />
SELECT * FROM ExpertSGBD -- on a 3 lignes qui qui s'affiche</div></div>
<p>&#8211;> <strong>Session 2</strong> : Depuis la session 2, insérons une nouvelle à l&rsquo;aide de la requête suivante :<br />
&#8212;&#8212;&#8212;&#8212;&#8211;<br />
<code class="codecolorer text default"><span class="text">INSERT INTO ExpertSGBD (id,prenom,nbpoints,date_inscription) VALUES (4,'Etienne',2408,'20100301')</span></code></p>
<p><font color="#0000ff"><strong> # Résultat session 2 : Impossible de faire l&rsquo;INSERT ! depuis la session 2 </strong> </font></p>
<p>&#8211;> <strong>Session 2</strong> : depuis la session 2, essayer un UDPATE<br />
&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<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">UPDATE ExpertSGBD &nbsp;<br />
&nbsp; &nbsp; &nbsp;SET nbpoints = 17580 <br />
WHERE id = 1</div></div>
<p><font color="#0000ff"><strong> # Résultat session 2 : Impossible de faire un UPDATE sur la table ! </strong></font></p>
<p>&#8211;> <strong>Session 1</strong> : Rejouons dans la session 1, la requête SELECT * FROM ExpertSGBD<br />
&#8212;&#8212;&#8212;&#8212;&#8211;<br />
<code class="codecolorer text default"><span class="text">SELECT * FROM ExpertSGBD</span></code></p>
<p><font color="#0000ff"><strong> # Résultat session 1 : On a toujours les mêmes données que précédemment : pas de lecture sale, pas de lecture qui ne peut être répétée, pas de lecture fantôme </strong></font></p>
<p>&#8211;> <strong>Session 1</strong> : Validons la transaction (COMMIT)<br />
&#8212;&#8212;&#8212;&#8212;&#8211;<br />
<code class="codecolorer text default"><span class="text">COMMIT TRANSACTION</span></code></p>
<p>=><strong> READ UNCOMMITED</strong><br />
Le niveau d&rsquo;isolation READ UNCOMMITED est le niveau de protection le plus bas. READ UNCOMMITED n&rsquo;assure aucune protection. Ne résoud donc aucun des problèmes d&rsquo;intégrité de données : lecture sale, lecture non repetatable,lecture fantôme. Il existe plusieurs possibilités pour positionner une transaction sur ce niveau d&rsquo;isolation : </p>
<p><strong>Exemple 1</strong> : SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED<br />
&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<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">SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED <br />
BEGIN TRAN <br />
SELECT * FROM ExpertSGBD <br />
COMMIT TRANSACTION</div></div>
<p><strong>Exemple 2</strong> : Utilisation dans la requête du mot clé (HINT) READUNCOMMITTED<br />
&#8212;&#8212;&#8212;&#8212;&#8211;<br />
<code class="codecolorer text default"><span class="text">SELECT * FROM ExpertSGBD WITH (READUNCOMMITTED)</span></code></p>
<p><strong>Exemple 3</strong> : Utilisation dans la requête du mot clé NOCLOCK<br />
&#8212;&#8212;&#8212;&#8212;&#8211;<br />
<code class="codecolorer text default"><span class="text">SELECT * FROM ExpertSGBD WITH (NOCLOCK)</span></code></p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;xxx&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</p>
<p>On peut conclure, au travers de ces scénarii que le niveau SERIALIZABLE assure une parfaite protection (isolation) des transactions et donc l&rsquo;intégrité des données. Les transactions sont dans ce cas traitées en série au sein du SGBD. Ce mécanisme de traitement en série des transactions SQL peut nuire au performance. Et c&rsquo;est ce niveau SERIALIZABLE que la norme SQL propose comme niveau par défaut pour les SGBDs. À mon humble avis lors du développement des procédures SQL pour les applications il faut essayer de trouver l&rsquo;équilibre entre performance (niveau d&rsquo;isolation faible, donc pas de traitement en série) et intégrité (niveau d&rsquo;isolation élevé) de données.</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
Etienne ZINZINDOHOUE<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AUTOCOMMIT ou COMMIT IMPLICITE : quelle confusion ?</title>
		<link>https://blog.developpez.com/zinzineti/p10924/sql-server-2008/autocommit_ou_commit_implicite_quelle_co</link>
		<comments>https://blog.developpez.com/zinzineti/p10924/sql-server-2008/autocommit_ou_commit_implicite_quelle_co#comments</comments>
		<pubDate>Thu, 05 Apr 2012 16:47:40 +0000</pubDate>
		<dc:creator><![CDATA[zinzineti]]></dc:creator>
				<category><![CDATA[ORACLE]]></category>
		<category><![CDATA[SQL SERVER 2008]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[La norme SQL standard (ANSI/ISO) affirme que : 1. la première instruction SQL lancée depuis une session est considérée comme le début d&#8217;une transaction (même si le verbe BEGIN TRANSACTION n&#8217;est pas indiqué). 2. la fin de toute transaction DOIT se terminer de façon EXPLICITE par un ROLLBACK ou un COMMIT. C&#8217;est ce que dit la norme SQL à propos de la notion de début et fin d&#8217;une transaction SQL. Rappelons qu&#8217;une transaction SQL est [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>La norme SQL standard (ANSI/ISO) affirme que :<br />
<strong>1.</strong> la première instruction SQL lancée depuis une session est considérée comme le début d&rsquo;une transaction (même si le verbe BEGIN TRANSACTION n&rsquo;est pas indiqué).<br />
<strong>2.</strong> la fin de toute transaction <strong>DOIT</strong> se terminer de façon <strong>EXPLICITE</strong> par un <strong>ROLLBACK</strong> ou un <strong>COMMIT</strong>.<br />
C&rsquo;est ce que dit la norme SQL à propos de la notion de début et fin d&rsquo;une transaction SQL.<br />
Rappelons qu&rsquo;une transaction SQL est une entité logique comportant une ou plusieurs instruction SQL.<br />
Par défaut SQL SERVER considère chaque instruction comme une transaction et fait des COMMITs implicites après chaque exécution : c&rsquo;est l&rsquo;AUTOCOMMIT.<br />
Par défaut ORACLE fait des commit implicites à la fin de chaque instruction DDL (CREATE, ALTER, DROP). Notons que sur ce point ORACLE est plus proche de la norme que SQL SERVER.<br />
Ce que je ne comprends pas c&rsquo;est la confusion que crée MS SQL SERVER autour de la définition de l&rsquo;option AUTOCOMMIT. De quoi s&rsquo;agit-il exactement ?<span id="more-101"></span><br />
D&rsquo;après la documentation officielle de MS SQL SERVER :<br />
Version originale<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</p>
<blockquote><p>SET IMPLICIT_TRANSACTIONS { ON | OFF }<br />
Sets implicit transaction mode for the connection.<br />
When ON, SET IMPLICIT_TRANSACTIONS sets the connection into implicit transaction mode.<br />
When OFF, it returns the connection to autocommit transaction mode.</p></blockquote>
<p>Traduction en français<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
<blockquote><p>SET IMPLICIT_TRANSACTIONS { ON | OFF }<br />
Définit le mode de transaction implicite pour la connexion.<br />
Si sa valeur est ON, SET IMPLICIT_TRANSACTIONS met la connexion en mode de transaction implicite.<br />
Si la valeur est OFF, la connexion est remise en mode de validation automatique.</p></blockquote>
<p>D&rsquo;après ce qui est écrit dans la documentation officielle :<br />
<strong>SET IMPLICIT_TRANSACTIONS OFF = AUTOCOMMIT (validation automatique).<br />
Mais le problème c&rsquo;est que si IMPLICIT_&#8230;. est OFF on peut penser qu&rsquo;on est en mode EXPLICIT_&#8230; et en EXPLICIT_&#8230; ce n&rsquo;est plus de l&rsquo;AUTOCOMMIT ! </strong></p>
<p>=> <strong>Sous SQL SERVER</strong><br />
D&rsquo;abord comment identifier l&rsquo;option SET IMPLICIT_TRANSACTIONS de ma session ?</p>
<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 CASE WHEN (@@OPTIONS &amp; 2 = 2) THEN &nbsp;'IMPLICIT_TRANSACTIONS ON' <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ELSE 'IMPLICIT_TRANSACTIONS OFF' <br />
&nbsp; &nbsp; &nbsp; &nbsp; END 'IMPLICIT_TRANSACTIONS'</div></div>
<p><strong>Exemple avec SET IMPLICIT_TRANSACTIONS OFF</strong><br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
Session 1<br />
&#8212;&#8212;&#8212;</p>
<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">IF OBJECT_ID(N't1', N'U') IS NOT NULL DROP TABLE t1; <br />
SET IMPLICIT_TRANSACTIONS OFF <br />
CREATE TABLE t1(c INT); <br />
INSERT INTO t1 VALUES (1);</div></div>
<p>Session 2<br />
&#8212;&#8212;&#8212;<br />
<code class="codecolorer text default"><span class="text">SELECT * FROM t1</span></code></p>
<p><strong>La session 2 affiche bien les données de la table t1</strong></p>
<p><strong>Exemple avec SET IMPLICIT_TRANSACTIONS ON</strong><br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
Session 1<br />
&#8212;&#8212;&#8212;</p>
<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">IF OBJECT_ID(N't1', N'U') IS NOT NULL DROP TABLE t1; <br />
SET IMPLICIT_TRANSACTIONS ON <br />
CREATE TABLE t1(c INT); <br />
INSERT INTO t1 VALUES (1);</div></div>
<p>Session 2<br />
&#8212;&#8212;&#8212;<br />
<code class="codecolorer text default"><span class="text">SELECT * FROM t1</span></code></p>
<p><strong>Impossible de lire les données de la table t1 depuis la session 2, puisque la transaction n&rsquo;est pas encore validée (commit) par la session 1</strong></p>
<p>=> <strong>Sous ORACLE </strong><br />
D&rsquo;abord comment identifier l&rsquo;option AUTOCOMMIT de ma session ?</p>
<p><code class="codecolorer text default"><span class="text">SQL&gt; SHOW AUTOCOMMIT;</span></code></p>
<p><strong>Exemple avec AUTOCOMMIT ON</strong><br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
Session 1<br />
&#8212;&#8212;&#8212;</p>
<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">SET AUTOCOMMIT ON <br />
CREATE TABLE t1(c INT); <br />
INSERT INTO t1 VALUES (1);</div></div>
<p>Session 2<br />
&#8212;&#8212;&#8212;<br />
<code class="codecolorer text default"><span class="text">SELECT * FROM t1</span></code><br />
<strong>La session 2 affiche bien les données de la table t1</strong></p>
<p><strong>Exemple avec AUTOCOMMIT OFF</strong><br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
Session 1<br />
&#8212;&#8212;&#8212;</p>
<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">SET AUTOCOMMIT OFF <br />
CREATE TABLE t1(c INT); <br />
INSERT INTO t1 VALUES (1);</div></div>
<p>Session 2<br />
&#8212;&#8212;&#8212;<br />
<code class="codecolorer text default"><span class="text">SELECT * FROM t1</span></code><br />
<strong>Impossible de lire les données de la table t1 depuis la session 2, puisque la transaction n&rsquo;est pas encore validée (commit) par la session 1</strong></p>
<p>Pour conclure notons que pour MS SQL SERVER SET IMPLICIT_TRANSACTIONS OFF = AUTOCOMMIT. À mon humble avis, MS SQL SERVER devrait plutôt mettre <strong>SET IMPLICIT_TRANSACTIONS ON = AUTOCOMMIT</strong></p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
Etienne ZINZINDOHOUE<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>SAVEPOINT vs SAVE TRANSACTION</title>
		<link>https://blog.developpez.com/zinzineti/p10871/sql-server-2008/savepoint_vs_save_transaction</link>
		<comments>https://blog.developpez.com/zinzineti/p10871/sql-server-2008/savepoint_vs_save_transaction#comments</comments>
		<pubDate>Fri, 23 Mar 2012 17:20:16 +0000</pubDate>
		<dc:creator><![CDATA[zinzineti]]></dc:creator>
				<category><![CDATA[SQL SERVER 2008]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[La norme SQL propose un moyen de poser des points de restaurations dans une transaction : les SAVEPOINTs. L&#8217;idée c&#8217;est de pouvoir revenir à une étape donnée dans la transaction. Selon la norme SQL la synthaxe pour créer un SAVEPOINT est la suivante :SAVEPOINT savepoint_name et pour revenir en arrière :ROLLBACK TO SAVEPOINT savepoint_name . La commande SAVEPOINT n&#8217;existe pas sous MS SQL SERVER mais une commande similaire exite : SAVE { TRAN &#124; TRANSACTION [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>La norme SQL propose un moyen de poser des points de restaurations dans une transaction : les <strong>SAVEPOINTs</strong>. L&rsquo;idée c&rsquo;est de pouvoir revenir à une étape donnée dans la transaction. Selon la norme SQL la synthaxe pour créer un SAVEPOINT est la suivante :<code class="codecolorer text default"><span class="text">SAVEPOINT savepoint_name</span></code> et pour revenir en arrière :<code class="codecolorer text default"><span class="text">ROLLBACK TO SAVEPOINT savepoint_name</span></code> . La commande SAVEPOINT n&rsquo;existe pas sous MS SQL SERVER mais une commande similaire exite : <code class="codecolorer text default"><span class="text">SAVE { TRAN | TRANSACTION } { savepoint_name | @savepoint_variable }</span></code> et pour revenir en arrière : <code class="codecolorer text default"><span class="text">ROLLBACK { TRAN | TRANSACTION } [ transaction_name | @tran_name_variable | savepoint_name | @savepoint_variable ]</span></code> </p>
<p>La norme SQL:2003 introduit la commande <code class="codecolorer text default"><span class="text">RELEASE SAVEPOINT savepoint_name</span></code>    qui permet de détruire un savepoint. Son équivalent SQL SERVER n&rsquo;existe pas. MS SQL SERVER supprime les points de restauration automatiquement à la fin des transaction (COMMIT ou ROLLACK)<br />
<span id="more-100"></span></p>
<p>=> <strong>SAVEPOINT /ROLLBACK TO SAVEPOINT</strong></p>
<p>&#8211;> Avec PostgreSQL</p>
<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">BEGIN; <br />
&nbsp; &nbsp; INSERT INTO table1 VALUES ('1'); <br />
&nbsp; &nbsp; SAVEPOINT savepoint1; -- création du savepoint1 &nbsp;<br />
&nbsp; &nbsp; INSERT INTO table1 VALUES ('2'); <br />
&nbsp; &nbsp; SAVEPOINT savepoint2; -- création du savepoint2 <br />
&nbsp; &nbsp; INSERT INTO table1 VALUES ('3'); <br />
&nbsp; &nbsp; ROLLBACK TO SAVEPOINT savepoint1; -- ROLLBACK TO savepoint1 &nbsp;<br />
COMMIT; <br />
&nbsp;<br />
SELECT * FROM table1; <br />
&nbsp;<br />
/*** résultat ***/ <br />
&nbsp;<br />
col <br />
--- <br />
1</div></div>
<p>&#8212;> Avec MS SQL SERVER</p>
<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">DROP TABLE table1; <br />
CREATE TABLE table1 (col CHAR(1)); <br />
BEGIN TRAN Transaction_Principale <br />
&nbsp; INSERT INTO table1 VALUES ('1') <br />
&nbsp; SAVE TRAN savepoint1 &nbsp;-- création du savepoint1 &nbsp;<br />
&nbsp; BEGIN TRAN Transaction_Imbriquée <br />
&nbsp; &nbsp; INSERT INTO table1 VALUES ('2') <br />
&nbsp; &nbsp; SAVE TRAN savepoint2 -- création du savepoint2 <br />
&nbsp; &nbsp; INSERT INTO table1 VALUES ('3'); <br />
&nbsp; ROLLBACK TRAN savepoint1 -- ROLLBACK TO savepoint1 &nbsp;<br />
COMMIT ; <br />
&nbsp;<br />
SELECT * FROM table1 <br />
&nbsp;<br />
/*** résultat ***/ <br />
&nbsp;<br />
col <br />
--- <br />
1</div></div>
<p>=> <strong>SAVEPOINTS avec les mêmes noms</strong><br />
La norme SQL stipule que lorsqu&rsquo;on crée un savepoint avec le même nom qu&rsquo;un autre savepoint existant alors ce dernier doit être automatiquement détruit. le plus récent savepoint remplace donc l&rsquo;ancien ayant le même nom.</p>
<p>&#8211;> Avec PostgreSQL</p>
<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">DROP TABLE table1; <br />
CREATE TABLE table1 (col CHAR(1)); <br />
BEGIN; <br />
&nbsp; &nbsp; INSERT INTO table1 VALUES ('1'); <br />
&nbsp; &nbsp; SAVEPOINT savepoint1; <br />
&nbsp; &nbsp; INSERT INTO table1 VALUES ('2'); <br />
&nbsp; &nbsp; SAVEPOINT savepoint1; <br />
&nbsp; &nbsp; INSERT INTO table1 VALUES ('3'); <br />
&nbsp; &nbsp; ROLLBACK TO SAVEPOINT savepoint1; <br />
COMMIT; <br />
&nbsp;<br />
SELECT * FROM table1; <br />
&nbsp;<br />
/*** résultat ***/ <br />
&nbsp;<br />
col <br />
--- <br />
1 <br />
2</div></div>
<p>&#8211;> Avec MS SQL SERVER</p>
<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">DROP TABLE table1; <br />
CREATE TABLE table1 (col CHAR(1)); <br />
BEGIN TRAN Transaction_Principale <br />
&nbsp; INSERT INTO table1 VALUES ('1') <br />
&nbsp; SAVE TRAN savepoint1 &nbsp;-- création du savepoint1 &nbsp;<br />
&nbsp; BEGIN TRAN Transaction_Imbriquée <br />
&nbsp; &nbsp; INSERT INTO table1 VALUES ('2') <br />
&nbsp; &nbsp; SAVE TRAN savepoint1 -- création du savepoint2 <br />
&nbsp; &nbsp; INSERT INTO table1 VALUES ('3') <br />
&nbsp; ROLLBACK TRAN savepoint1 -- ROLLBACK TO savepoint1 &nbsp;<br />
COMMIT ; <br />
&nbsp;<br />
SELECT * FROM table1 <br />
&nbsp;<br />
/*** résultat ***/ <br />
&nbsp;<br />
col <br />
--- <br />
1 <br />
2</div></div>
<p>=> <strong>RELEASE SAVEPOINT</strong></p>
<p>&#8211;> Avec PostgreSQL</p>
<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">DROP TABLE table1; <br />
CREATE TABLE table1 (col CHAR(1)); <br />
BEGIN; <br />
&nbsp; &nbsp; INSERT INTO table1 VALUES ('1'); <br />
&nbsp; &nbsp; SAVEPOINT savepoint1; <br />
&nbsp; &nbsp; INSERT INTO table1 VALUES ('2'); <br />
&nbsp; &nbsp; SAVEPOINT savepoint2; <br />
&nbsp; &nbsp; INSERT INTO table1 VALUES ('3'); <br />
&nbsp; &nbsp; RELEASE SAVEPOINT savepoint1; <br />
COMMIT; <br />
&nbsp;<br />
SELECT * FROM table1; <br />
&nbsp;<br />
/*** résultat ***/ <br />
&nbsp;<br />
col <br />
--- <br />
1 <br />
2 <br />
3</div></div>
<p>&#8211;> Avec MS SQL SERVER</p>
<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">DELETE FROM table1 <br />
BEGIN TRAN Transaction_Principale <br />
&nbsp; INSERT INTO table1 VALUES ('1') <br />
&nbsp; SAVE TRAN savepoint1 &nbsp;-- création du savepoint1 &nbsp;<br />
&nbsp; BEGIN TRAN Transaction_Imbriquée <br />
&nbsp; &nbsp; INSERT INTO table1 VALUES ('2') <br />
&nbsp; &nbsp; SAVE TRAN savepoint2 -- création du savepoint2 <br />
&nbsp; &nbsp; INSERT INTO table1 VALUES ('3') <br />
&nbsp; COMMIT TRAN savepoint1 -- ROLLBACK TO savepoint1 &nbsp;<br />
COMMIT ; <br />
&nbsp;<br />
SELECT * FROM table1 <br />
&nbsp;<br />
/*** résultat ***/ <br />
&nbsp;<br />
col <br />
--- <br />
1 <br />
2 <br />
3</div></div>
<p>Comme MS SQL SERVER, ORACLE ne supporte pas non plus la commande standard <strong>RELEASE SAVEPOINT</strong>. Notons que parmi les SGBDs les plus connus du marché, MS SQL SERVER est le seul à ne pas implémenter la commande  <code class="codecolorer text default"><span class="text">SAVEPOINT &nbsp;savepoint_name</span></code>. IBM DB2, ORACLE, PostGreSQL et &laquo;&nbsp;MySQL&nbsp;&raquo; ont tous implémenté la commande SAVEPOINT.</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
Etienne ZINZINDOHOUE<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Curseurs ouverts</title>
		<link>https://blog.developpez.com/zinzineti/p10824/sql-server-2008/curseurs_ouverts</link>
		<comments>https://blog.developpez.com/zinzineti/p10824/sql-server-2008/curseurs_ouverts#comments</comments>
		<pubDate>Mon, 12 Mar 2012 19:51:57 +0000</pubDate>
		<dc:creator><![CDATA[zinzineti]]></dc:creator>
				<category><![CDATA[SQL SERVER 2008]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Les curseurs font partie intégrante de la norme SQL. Ils sont utilisés dans les SGBDRs pour réaliser des traitements ligne par ligne. Le temps de traitement des transactions SQL utilisant des curseurs dépend du SGBDR, de la qualité/quantité des données traitées, de l&#8217;écriture de la transaction SQL contenant le curseur, &#8230;. Un curseur utilisé et qui n&#8217;est pas fermé (ne se termine pas par CLOSE nomCurseur et DEALLOCATE nomCurseur) aura des incidences sur la performance [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Les curseurs font partie intégrante de la norme SQL. Ils sont utilisés dans les SGBDRs pour réaliser des traitements ligne par ligne. Le temps de traitement des transactions SQL utilisant des curseurs dépend du SGBDR, de la qualité/quantité des données traitées, de l&rsquo;écriture de la transaction SQL contenant le curseur, &#8230;.<br />
Un curseur utilisé et qui n&rsquo;est pas fermé (ne se termine pas par CLOSE nomCurseur et DEALLOCATE nomCurseur) aura des incidences sur la performance du serveur de base de données.<span id="more-99"></span> </p>
<p>=> Liste des curseurs ouverts pour toutes les sessions</p>
<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 c.session_id <br />
,s.login_name <br />
,s.host_name <br />
,s.program_name <br />
,c.creation_time <br />
,c.name <br />
,c.is_open <br />
,c.is_close_on_commit <br />
,c.worker_time <br />
,c.dormant_duration <br />
,t.text as sql_text <br />
FROM sys.dm_exec_cursors(0)as c &nbsp;<br />
INNER JOIN sys.dm_exec_sessions as s ON c.session_id = s.session_id &nbsp;<br />
CROSS APPLY sys.dm_exec_sql_text(c.sql_handle) t</div></div>
<p>=> Pour fermer les curseurs : Solution brutale<br />
<code class="codecolorer text default"><span class="text">KILL c.session_id</span></code><br />
Exemple<br />
&#8212;&#8212;&#8211;<br />
<code class="codecolorer text default"><span class="text">KILL 52</span></code></p>
<p>=> Pour fermer les curseurs : Solution durable</p>
<p>Ajouter <strong>CLOSE nomCurseur et DEALLOCATE nomCurseur</strong> dans les procédures concernées</p>
<p>L&rsquo;option CURSOR_CLOSE_ON_COMMIT (pour la session ou pour la base de donnée) est sensée résoudre ce genre de problème selon les recommandations de la norme ISO, mais la réalité est tout autre. </p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8211;<br />
Etienne ZINZINDOHOUE</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>FOREIGN KEYs non indexés</title>
		<link>https://blog.developpez.com/zinzineti/p10823/sql-server-2008/foreign_key_non_indexes</link>
		<comments>https://blog.developpez.com/zinzineti/p10823/sql-server-2008/foreign_key_non_indexes#comments</comments>
		<pubDate>Mon, 12 Mar 2012 18:33:57 +0000</pubDate>
		<dc:creator><![CDATA[zinzineti]]></dc:creator>
				<category><![CDATA[SQL SERVER 2008]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[Il est souvent recommandé d&#8217;indexer les clés étrangères (FK). Cette opération d&#8217;indexation ne doit pas se faire de façon mécanique. Elle devrait passer par les phases d&#8217;analyse, de création et de test. Mais avant comment identifier les FKs non indexés ? SELECT &#160;a.TABLE_SCHEMA,a.TABLE_NAME, b.COLUMN_NAME, b.CONSTRAINT_NAME,a.IS_DEFERRABLE,a.INITIALLY_DEFERRED FROM &#160; &#160;INFORMATION_SCHEMA.TABLE_CONSTRAINTS a &#160; &#160; &#160; INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE b ON a.CONSTRAINT_NAME = b.CONSTRAINT_NAME WHERE &#160; a.CONSTRAINT_TYPE = 'FOREIGN KEY' --AND &#160;a.TABLE_NAME = 'Commandes' /* pour une table specifique [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>Il est souvent recommandé d&rsquo;indexer les clés étrangères (FK). Cette opération d&rsquo;indexation ne doit pas se faire de façon mécanique. Elle devrait passer par les phases d&rsquo;analyse, de création et de test. Mais avant comment identifier les FKs non indexés ?<span id="more-98"></span></p>
<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;a.TABLE_SCHEMA,a.TABLE_NAME, b.COLUMN_NAME, b.CONSTRAINT_NAME,a.IS_DEFERRABLE,a.INITIALLY_DEFERRED <br />
FROM &nbsp; &nbsp;INFORMATION_SCHEMA.TABLE_CONSTRAINTS a &nbsp;<br />
&nbsp; &nbsp; INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE b ON a.CONSTRAINT_NAME = b.CONSTRAINT_NAME <br />
WHERE &nbsp; a.CONSTRAINT_TYPE = 'FOREIGN KEY' <br />
--AND &nbsp;a.TABLE_NAME = 'Commandes' /* pour une table specifique */ <br />
AND a.TABLE_NAME + '.' + b.COLUMN_NAME NOT IN ( &nbsp;<br />
SELECT o.name +'.'+c.name <br />
FROM &nbsp;sys.objects o &nbsp; &nbsp; <br />
&nbsp; &nbsp; INNER JOIN sys.indexes i ON i.object_id = o.object_id &nbsp;<br />
&nbsp; &nbsp; INNER JOIN &nbsp;sys.columns c ON o.object_id = c.object_id &nbsp;<br />
&nbsp; &nbsp; INNER JOIN sys.index_columns ic ON ic.object_id = o.object_id AND i.index_id = ic.index_id &nbsp;AND ic.column_id = c.column_id &nbsp;<br />
&nbsp; &nbsp; WHERE &nbsp;o.type in ('U') &nbsp;<br />
)</div></div>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Etienne ZINZINDOHOUE</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mettre à jour des tables via une vue</title>
		<link>https://blog.developpez.com/zinzineti/p10666/sql-server-2008/mettre_a_jour_des_tables_via_une_vue</link>
		<comments>https://blog.developpez.com/zinzineti/p10666/sql-server-2008/mettre_a_jour_des_tables_via_une_vue#comments</comments>
		<pubDate>Sun, 22 Jan 2012 15:31:54 +0000</pubDate>
		<dc:creator><![CDATA[zinzineti]]></dc:creator>
				<category><![CDATA[SQL SERVER 2008]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[On désire mettre à jour plusieurs tables via une vue. --&#62; La vue CREATE VIEW V_Clients_Commandes (id_client,nom_client,tel_client,mobile_client,date_commande,etat_commande,date_livraison) &#160;AS SELECT cli.IDClient,cli.NomClient,cli.Tel,cli.Mobilephone,com.DateCommande,com.EtatCommande,com.DateLivraison FROM Clients cli inner join Commandes com ON cli.IDClient = com.IDClient &#160; --&#62; Les tables membres de la vue CREATE TABLE dbo.Clients( &#160; IDClient int IDENTITY(1,1) NOT NULL PRIMARY KEY, &#160; NomClient varchar(20) NULL, &#160; Tel varchar(20) NULL, &#160; Mobilephone [varchar](20) NULL ) CREATE TABLE dbo.Commandes( &#160; IDCommande int IDENTITY(1,1) NOT NULL PRIMARY KEY, &#160; [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>On désire mettre à jour plusieurs tables via une vue.</p>
<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">--&gt; La vue <br />
CREATE VIEW V_Clients_Commandes (id_client,nom_client,tel_client,mobile_client,date_commande,etat_commande,date_livraison) &nbsp;AS <br />
SELECT cli.IDClient,cli.NomClient,cli.Tel,cli.Mobilephone,com.DateCommande,com.EtatCommande,com.DateLivraison <br />
FROM Clients cli inner join Commandes com <br />
ON cli.IDClient = com.IDClient <br />
&nbsp;<br />
--&gt; Les tables membres de la vue <br />
CREATE TABLE dbo.Clients( <br />
&nbsp; IDClient int IDENTITY(1,1) NOT NULL PRIMARY KEY, <br />
&nbsp; NomClient varchar(20) NULL, <br />
&nbsp; Tel varchar(20) NULL, <br />
&nbsp; Mobilephone [varchar](20) NULL <br />
) <br />
CREATE TABLE dbo.Commandes( <br />
&nbsp; IDCommande int IDENTITY(1,1) NOT NULL PRIMARY KEY, <br />
&nbsp; IDClient int NOT NULL, <br />
&nbsp; EtatCommande varchar(20) NULL, <br />
&nbsp; DateCommande datetime NULL, <br />
&nbsp; DateLivraison datetime NULL, <br />
CONSTRAINT FK_IDCLI &nbsp;FOREIGN KEY (IDClient) REFERENCES Clients(IDClient) <br />
) <br />
--&gt; essaie de MAJ de la vue <br />
UPDATE V_Clients_Commandes <br />
SET tel_client = '0320XXXXXX', <br />
etat_commande ='Livrée', <br />
date_livraison = GETDATE() <br />
WHERE nom_client = 'ZINZINDOHOUE'</div></div>
<p><strong>&#8211;> échec du UPDATE</strong>.<br />
/*<br />
<strong>Msg 4405, Niveau 16, État 1, Ligne 1<br />
La vue ou la fonction &lsquo;V_Clients_Commandes&rsquo; ne peut pas être mise à jour car la modification porte sur plusieurs tables de base.</strong><br />
*/  <span id="more-54"></span></p>
<p><strong>&#8211;> Solution : trigger INSTEAD OF UPDATE sur la vue </strong></p>
<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">CREATE TRIGGER TR_INSTEAD_UPDATE_ON_V_Clients_Commandes ON V_Clients_Commandes INSTEAD OF UPDATE NOT FOR REPLICATION <br />
AS <br />
BEGIN <br />
-- s'assurer qu'au moins une ligne est affectée <br />
IF (@@ROWCOUNT &gt; 0) <br />
BEGIN <br />
SET NOCOUNT ON; <br />
BEGIN <br />
-- MAJ des colonnes concernées dans la table Clients &nbsp;<br />
UPDATE Clients <br />
SET NomClient = i.nom_client, <br />
Tel = i.tel_client, <br />
Mobilephone = i.mobile_client <br />
FROM inserted i <br />
WHERE i.id_client = Clients.IDClient; <br />
END; <br />
-- MAJ des colonnes concernées dans la table Commandes <br />
BEGIN <br />
-- Update columns in the base table <br />
UPDATE Commandes <br />
SET EtatCommande = i.etat_commande, <br />
DateCommande = i.date_commande, <br />
DateLivraison = i.date_livraison <br />
FROM inserted i <br />
WHERE i.id_client = Commandes.IDClient <br />
END <br />
END <br />
END <br />
GO <br />
&nbsp;<br />
--&gt; MAJ des tables via la vue <br />
UPDATE V_Clients_Commandes <br />
SET tel_client = '0320XXXXXX', <br />
etat_commande ='Livrée', <br />
date_livraison = GETDATE() <br />
WHERE nom_client = 'ZINZINDOHOUE' <br />
&nbsp;<br />
--&gt; MAJ OK : vérification &nbsp;<br />
&nbsp;<br />
SELECT * FROM Clients <br />
SELECT * FROM Commandes</div></div>
<p>Les déclencheurs INSTEAD OF permettent de mettre à jour une vue multitable qui ne pouvaient être modifiée directement par UPDATE</p>
<p>Happy query !</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
Etienne ZINZINDOHOUE<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Alimenter plusieurs tables via une vue</title>
		<link>https://blog.developpez.com/zinzineti/p10667/sql-server-2008/alimenter_plusieurs_tables_via_une_vue</link>
		<comments>https://blog.developpez.com/zinzineti/p10667/sql-server-2008/alimenter_plusieurs_tables_via_une_vue#comments</comments>
		<pubDate>Sun, 22 Jan 2012 15:39:37 +0000</pubDate>
		<dc:creator><![CDATA[zinzineti]]></dc:creator>
				<category><![CDATA[SQL SERVER 2008]]></category>

		<guid isPermaLink="false"></guid>
		<description><![CDATA[On désire alimenter plusieurs tables via une vue. --&#62; la vue CREATE VIEW V_Clients_Commandes (id_client,nom_client,tel_client,mobile_client,date_commande,etat_commande,date_livraison) &#160;AS SELECT cli.IDClient,cli.NomClient,cli.Tel,cli.Mobilephone,com.DateCommande,com.EtatCommande,com.DateLivraison FROM Clients cli inner join Commandes com ON cli.IDClient = com.IDClient &#160; --&#62; Les tables CREATE TABLE dbo.Clients( &#160; IDClient int IDENTITY(1,1) NOT NULL PRIMARY KEY, &#160; NomClient varchar(20) NULL, &#160; Tel varchar(20) NULL, &#160; Mobilephone [varchar](20) NULL ) CREATE TABLE dbo.Commandes( &#160; IDCommande int IDENTITY(1,1) NOT NULL PRIMARY KEY, &#160; IDClient int NOT NULL, &#160; EtatCommande [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>On désire alimenter plusieurs tables via une vue.</p>
<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">--&gt; la vue <br />
CREATE VIEW V_Clients_Commandes (id_client,nom_client,tel_client,mobile_client,date_commande,etat_commande,date_livraison) &nbsp;AS <br />
SELECT cli.IDClient,cli.NomClient,cli.Tel,cli.Mobilephone,com.DateCommande,com.EtatCommande,com.DateLivraison <br />
FROM Clients cli inner join Commandes com <br />
ON cli.IDClient = com.IDClient <br />
&nbsp;<br />
--&gt; Les tables <br />
CREATE TABLE dbo.Clients( <br />
&nbsp; IDClient int IDENTITY(1,1) NOT NULL PRIMARY KEY, <br />
&nbsp; NomClient varchar(20) NULL, <br />
&nbsp; Tel varchar(20) NULL, <br />
&nbsp; Mobilephone [varchar](20) NULL <br />
) <br />
CREATE TABLE dbo.Commandes( <br />
&nbsp; IDCommande int IDENTITY(1,1) NOT NULL PRIMARY KEY, <br />
&nbsp; IDClient int NOT NULL, <br />
&nbsp; EtatCommande varchar(20) NULL, <br />
&nbsp; DateCommande datetime NULL, <br />
&nbsp; DateLivraison datetime NULL, <br />
CONSTRAINT FK_IDCLI &nbsp;FOREIGN KEY (IDClient) REFERENCES Clients(IDClient) <br />
)</div></div>
<p>&#8211;> <strong>Essaie d&rsquo;insertion</strong></p>
<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">INSERT INTO V_Clients_Commandes (nom_client,tel_client,mobile_client,date_commande,etat_commande,date_livraison) <br />
VALUES ('ZINZINDOHOUE','0102XXXXXX','06XXXXXXXX',GETDATE(),'Non livrée',null)</div></div>
<p><strong>&#8211;> échec de l&rsquo;INSERT.</strong><br />
/*<br />
<strong>Msg 4405, Niveau 16, État 1, Ligne 1<br />
La vue ou la fonction &lsquo;V_Clients_Commandes&rsquo; ne peut pas être mise à jour car la modification porte sur plusieurs tables de base.</strong><br />
*/<br />
<span id="more-55"></span></p>
<p><strong>&#8211;> Solution : trigger INSTEAD OF INSERT sur la vue </strong></p>
<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">CREATE TRIGGER TR_INSTEAD_INSERT_ON_V_Clients_Commandes <br />
ON V_Clients_Commandes INSTEAD OF INSERT NOT FOR REPLICATION <br />
AS <br />
BEGIN <br />
-- s'assurer qu'au moins une ligne est affectée <br />
IF (@@ROWCOUNT &gt; 0) <br />
BEGIN <br />
SET NOCOUNT ON; <br />
BEGIN <br />
-- Insertion dans la table Clients &nbsp;<br />
INSERT INTO Clients (NomClient,Tel,Mobilephone) <br />
SELECT i.nom_client,i.tel_client,i.mobile_client <br />
FROM inserted i <br />
END; <br />
-- Insertion dans la table Commandes <br />
BEGIN <br />
INSERT INTO Commandes (IDClient,DateCommande,EtatCommande,DateLivraison) <br />
SELECT SCOPE_IDENTITY(),i.date_commande,i.etat_commande,i.date_livraison <br />
FROM inserted i <br />
END <br />
END <br />
END <br />
GO <br />
--&gt; Insertion dans les tables membres de la vue <br />
INSERT INTO V_Clients_Commandes (nom_client,tel_client,mobile_client,date_commande,etat_commande,date_livraison) <br />
VALUES ('ZINZINDOHOUE','0102XXXXXX','06XXXXXXXX',GETDATE(),'Non livrée',null) <br />
&nbsp;<br />
--&gt; Insertion &nbsp;OK : vérification &nbsp;<br />
SELECT * FROM Clients <br />
SELECT * FROM Commandes</div></div>
<p>Les déclencheurs INSTEAD OF permettent de mettre à jour une vue multitable qui ne pouvaient être modifiée directement par INSERT</p>
<p>Happy query !</p>
<p>&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
Etienne ZINZINDOHOUE<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;</p>
]]></content:encoded>
			<wfw:commentRss></wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
