<?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>Thiago Zavaschi R2 &#187; Common Table Expression</title>
	<atom:link href="http://zavaschi.com/index.php/category/common-table-expression/feed/" rel="self" type="application/rss+xml" />
	<link>http://zavaschi.com</link>
	<description>www.zavaschi.com</description>
	<lastBuildDate>Sun, 11 Jul 2010 00:06:14 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Entendendo as Common Table Expressions &#8211; CTE &#8211; Parte 3 (O Retorno)</title>
		<link>http://zavaschi.com/index.php/2009/09/entendendo-as-common-table-expressions-cte-parte-3-o-retorno/</link>
		<comments>http://zavaschi.com/index.php/2009/09/entendendo-as-common-table-expressions-cte-parte-3-o-retorno/#comments</comments>
		<pubDate>Tue, 29 Sep 2009 12:24:12 +0000</pubDate>
		<dc:creator>Thiago Zavaschi</dc:creator>
				<category><![CDATA[Common Table Expression]]></category>
		<category><![CDATA[CTE]]></category>

		<guid isPermaLink="false">8DE5A8EFC1819ECA!399</guid>
		<description><![CDATA[Sabe quando você termina uma coisa, mas fica na cabeça de que esqueceu alguma coisa?
Poisé… alguns dos meus posts anteriores trataram de Common Table Expressions e eu não mencionei outra característica muito interessante.
Lembram que o result set da CTE só existe no escopo da query seguinte? Mas será que não é possivél eu ir juntando [...]]]></description>
			<content:encoded><![CDATA[<p>Sabe quando você termina uma coisa, mas fica na cabeça de que esqueceu alguma coisa?
<p>Poisé… alguns dos meus posts anteriores trataram de Common Table Expressions e eu não mencionei outra característica muito interessante.
<p>Lembram que o <em>result set</em> da CTE só existe no escopo da query seguinte? Mas será que não é possivél eu ir juntando várias CTE’s e no final fazer uma query que manipule o conjunto de todas?
<p>É sim! Vejam o seguinte exemplo: </p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:WhiteSmoke;overflow:auto"><span style="color:#000000">;</span><span style="color:#0000FF">WITH</span><span style="color:#000000"> cteA(a)
</span><span style="color:#0000FF">AS</span><span style="color:#000000">
(
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000">
    </span><span style="color:#0000FF">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000">
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> a </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000"> </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> cteA </span><span style="color:#0000FF">WHERE</span><span style="color:#000000"> a </span><span style="color:#808080">&lt;</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">10</span><span style="color:#000000">
), cteB(b)
</span><span style="color:#0000FF">AS</span><span style="color:#000000">
(
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000">
    </span><span style="color:#0000FF">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000">
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> b </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000"> </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> cteB </span><span style="color:#0000FF">WHERE</span><span style="color:#000000"> b </span><span style="color:#808080">&lt;</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">10</span><span style="color:#000000">
), cteC(c)
</span><span style="color:#0000FF">AS</span><span style="color:#000000">
(
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">5</span><span style="color:#000000">
)
</span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> ((</span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#FF00FF">SUM</span><span style="color:#000000">(b) </span><span style="color:#0000FF">from</span><span style="color:#000000"> cteB) </span><span style="color:#808080">+</span><span style="color:#000000"> (</span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#FF00FF">SUM</span><span style="color:#000000">(a) </span><span style="color:#0000FF">from</span><span style="color:#000000"> cteA)) </span><span style="color:#808080">/</span><span style="color:#000000">
((</span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#FF00FF">COUNT</span><span style="color:#000000">(b) </span><span style="color:#0000FF">from</span><span style="color:#000000"> cteB) </span><span style="color:#808080">+</span><span style="color:#000000"> (</span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#FF00FF">COUNT</span><span style="color:#000000">(a) </span><span style="color:#0000FF">from</span><span style="color:#000000"> cteA)) </span><span style="color:#808080">*</span><span style="color:#000000">
(</span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> c </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> cteC)</span></pre>
</div>
<p>São 3 CTEs sendo executadas. O retorno das duas primeiras já nos é conhecido: é a contagem de 1 a 10 (visto no post anterior), e o retorno da última é 5, bastante trivial.</p>
<p>Esse post não serve apenas para demonstrar essa capacidade, mas também para alertar para os perigos que podem vir atrelados. </p>
<p>E o meu recado é: <strong><em>CUIDADO com a comodidade!</em></strong> Senão a legibilidade ganha com as CTEs de nada servirá. A explicação é facilmente observada através do plano de execução da query acima.</p>
<p><font size="2" face="Courier New">StmtText<br />----------------------------------------------------------------------------------------------------------------------<br />  |--Compute Scalar(DEFINE:([Expr1026]=(([Expr1004]+[Expr1010])/([Expr1016]+[Expr1022]))*(5)))<br />       |--Nested Loops(Inner Join)<br />            |--Nested Loops(Inner Join)<br />            |    |--Nested Loops(Inner Join)<br />            |    |    |--Compute Scalar(DEFINE:([Expr1004]=CASE WHEN [Expr1045]=(0) THEN NULL ELSE [Expr1046] END))<br />            |    |    |    |--Stream Aggregate(DEFINE:([Expr1045]=COUNT_BIG([Recr1003]), [Expr1046]=SUM([Recr1003])))<br />            |    |    |         |--Index Spool(WITH STACK)<br />            |    |    |              |--Concatenation<br />            |    |    |                   |--Compute Scalar(DEFINE:([Expr1041]=(0)))<br />            |    |    |                   |    |--Constant Scan(VALUES:(((1))))<br />            |    |    |                   |--Assert(WHERE:(CASE WHEN [Expr1043]&gt;(100) THEN (0) ELSE NULL END))<br />            |    |    |                        |--Nested Loops(Inner Join, OUTER REFERENCES:([Expr1043], [Recr1001]))<br />            |    |    |                             |--Compute Scalar(DEFINE:([Expr1043]=[Expr1042]+(1)))<br />            |    |    |                             |    |--Table Spool(WITH STACK)<br />            |    |    |                             |--Compute Scalar(DEFINE:([Expr1002]=[Recr1001]+(1)))<br />            |    |    |                                  |--Filter(WHERE:(STARTUP EXPR([Recr1001]&lt;(10))))<br />            |    |    |                                       |--Constant Scan<br />            |    |    |--Compute Scalar(DEFINE:([Expr1022]=CONVERT_IMPLICIT(int,[Expr1051],0)))<br />            |    |         |--Stream Aggregate(DEFINE:([Expr1051]=COUNT([Recr1021])))<br />            |    |              |--Index Spool(WITH STACK)<br />            |    |                   |--Concatenation<br />            |    |                        |--Compute Scalar(DEFINE:([Expr1047]=(0)))<br />            |    |                        |    |--Constant Scan(VALUES:(((1))))<br />            |    |                        |--Assert(WHERE:(CASE WHEN [Expr1049]&gt;(100) THEN (0) ELSE NULL END))<br />            |    |                             |--Nested Loops(Inner Join, OUTER REFERENCES:([Expr1049], [Recr1019]))<br />            |    |                                  |--Compute Scalar(DEFINE:([Expr1049]=[Expr1048]+(1)))<br />            |    |                                  |    |--Table Spool(WITH STACK)<br />            |    |                                  |--Compute Scalar(DEFINE:([Expr1020]=[Recr1019]+(1)))<br />            |    |                                       |--Filter(WHERE:(STARTUP EXPR([Recr1019]&lt;(10))))<br />            |    |                                            |--Constant Scan<br />            |    |--Compute Scalar(DEFINE:([Expr1016]=CONVERT_IMPLICIT(int,[Expr1056],0)))<br />            |         |--Stream Aggregate(DEFINE:([Expr1056]=COUNT([Recr1015])))<br />            |              |--Index Spool(WITH STACK)<br />            |                   |--Concatenation<br />            |                        |--Compute Scalar(DEFINE:([Expr1052]=(0)))<br />            |                        |    |--Constant Scan(VALUES:(((1))))<br />            |                        |--Assert(WHERE:(CASE WHEN [Expr1054]&gt;(100) THEN (0) ELSE NULL END))<br />            |                             |--Nested Loops(Inner Join, OUTER REFERENCES:([Expr1054], [Recr1013]))<br />            |                                  |--Compute Scalar(DEFINE:([Expr1054]=[Expr1053]+(1)))<br />            |                                  |    |--Table Spool(WITH STACK)<br />            |                                  |--Compute Scalar(DEFINE:([Expr1014]=[Recr1013]+(1)))<br />            |                                       |--Filter(WHERE:(STARTUP EXPR([Recr1013]&lt;(10))))<br />            |                                            |--Constant Scan<br />            |--Compute Scalar(DEFINE:([Expr1010]=CASE WHEN [Expr1061]=(0) THEN NULL ELSE [Expr1062] END))<br />                 |--Stream Aggregate(DEFINE:([Expr1061]=COUNT_BIG([Recr1009]), [Expr1062]=SUM([Recr1009])))<br />                      |--Index Spool(WITH STACK)<br />                           |--Concatenation<br />                                |--Compute Scalar(DEFINE:([Expr1057]=(0)))<br />                                |    |--Constant Scan(VALUES:(((1))))<br />                                |--Assert(WHERE:(CASE WHEN [Expr1059]&gt;(100) THEN (0) ELSE NULL END))<br />                                     |--Nested Loops(Inner Join, OUTER REFERENCES:([Expr1059], [Recr1007]))<br />                                          |--Compute Scalar(DEFINE:([Expr1059]=[Expr1058]+(1)))<br />                                          |    |--Table Spool(WITH STACK)<br />                                          |--Compute Scalar(DEFINE:([Expr1008]=[Recr1007]+(1)))<br />                                               |--Filter(WHERE:(STARTUP EXPR([Recr1007]&lt;(10))))<br />                                                    |--Constant Scan </font></p>
<p>Poisé, é executado tudo separado. Se houverem processamento desnecessários nas CTEs, poderemos encontrar sérios gargalos nestas querys!</p>
<p>Então pessoal, era mais isso que eu queria mostrar deste fantástico recurso! Usem CTE’s, e para os que usarem, usem sabendo como funcionam! :)</p>
<p>E para quem não acompanhou, segue os links dos posts anteriores:</p>
<h6>Entendendo as Common Table Expressions – CTE – Parte 1</h6>
<p><a title="http://thiagozavaschi.spaces.live.com/blog/cns!8DE5A8EFC1819ECA!390.entry" href="http://thiagozavaschi.spaces.live.com/blog/cns!8DE5A8EFC1819ECA!390.entry">http://thiagozavaschi.spaces.live.com/blog/cns!8DE5A8EFC1819ECA!390.entry</a></p>
<h6>Entendendo as Common Table Expressions – CTE – Parte 2 (Final)</h6>
<p><a title="http://thiagozavaschi.spaces.live.com/blog/cns!8DE5A8EFC1819ECA!391.entry" href="http://thiagozavaschi.spaces.live.com/blog/cns!8DE5A8EFC1819ECA!391.entry">http://thiagozavaschi.spaces.live.com/blog/cns!8DE5A8EFC1819ECA!391.entry</a></p>
<p>Abraços,<br />Thiago Zavaschi</p>
]]></content:encoded>
			<wfw:commentRss>http://zavaschi.com/index.php/2009/09/entendendo-as-common-table-expressions-cte-parte-3-o-retorno/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Entendendo as Common Table Expressions &#8211; CTE &#8211; Parte 2 (Final)</title>
		<link>http://zavaschi.com/index.php/2009/09/entendendo-as-common-table-expressions-cte-parte-2-final/</link>
		<comments>http://zavaschi.com/index.php/2009/09/entendendo-as-common-table-expressions-cte-parte-2-final/#comments</comments>
		<pubDate>Sun, 20 Sep 2009 11:47:28 +0000</pubDate>
		<dc:creator>Thiago Zavaschi</dc:creator>
				<category><![CDATA[Common Table Expression]]></category>
		<category><![CDATA[SQL Server 2005]]></category>
		<category><![CDATA[SQL Server 2008]]></category>
		<category><![CDATA[SQL Server 2008 R2]]></category>
		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">8DE5A8EFC1819ECA!391</guid>
		<description><![CDATA[Exemplos de queries recursivas
Bom, mostrei um pouco de teoria, mostrei alguns exemplos didáticos (o de números é bastante utilizado na prática), mas vamos ver mais alguns exemplos:
Vou começar com um exemplo já mostrado aqui no blog (sim, finalmente este é o post sobre o qual falei no referido post :) ).
Este é o método baseado [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Exemplos de queries recursivas</strong></p>
<p>Bom, mostrei um pouco de teoria, mostrei alguns exemplos didáticos (o de números é bastante utilizado na prática), mas vamos ver mais alguns exemplos:</p>
<p>Vou começar com um <a href="http://thiagozavaschi.spaces.live.com/Blog/cns!8DE5A8EFC1819ECA!277.entry" target="_blank">exemplo já mostrado aqui no blog</a> (sim, finalmente este é o post sobre o qual falei no referido post :) ).</p>
<p>Este é o método baseado em CTE para realizar Split no SQL Server (atentar para o limite da recursividade, se necessário usar o maxrecursion).</p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:#f4f4f4;overflow:auto"><span style="color:#0000ff">DECLARE</span><span style="color:#000000"> </span><span style="color:#008000">@s</span><span style="color:#000000"> </span><span style="color:#0000ff">VARCHAR</span><span style="color:#000000">(</span><span style="color:#800000;font-weight:bold">8000</span><span style="color:#000000">), </span><span style="color:#008000">@d</span><span style="color:#000000"> </span><span style="color:#0000ff">VARCHAR</span><span style="color:#000000">(</span><span style="color:#800000;font-weight:bold">10</span><span style="color:#000000">)
</span><span style="color:#0000ff">SET</span><span style="color:#000000"> </span><span style="color:#008000">@s</span><span style="color:#000000"> </span><span style="color:#808080">=</span><span style="color:#000000"> </span><span style="color:#ff0000">'</span><span style="color:#ff0000">separar por espaço em branco</span><span style="color:#ff0000">'</span><span style="color:#000000">
</span><span style="color:#0000ff">SET</span><span style="color:#000000"> </span><span style="color:#008000">@d</span><span style="color:#000000"> </span><span style="color:#808080">=</span><span style="color:#000000"> </span><span style="color:#ff0000">'</span><span style="color:#ff0000"> </span><span style="color:#ff0000">'</span><span style="color:#000000"> 

;</span><span style="color:#0000ff">WITH</span><span style="color:#000000"> split(i,j) </span><span style="color:#0000ff">AS</span><span style="color:#000000">
(
</span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> i </span><span style="color:#808080">=</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000">, j </span><span style="color:#808080">=</span><span style="color:#000000"> </span><span style="color:#ff00ff">CHARINDEX</span><span style="color:#000000">(</span><span style="color:#008000">@d</span><span style="color:#000000">, </span><span style="color:#008000">@s</span><span style="color:#000000"> </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#008000">@d</span><span style="color:#000000">)
</span><span style="color:#0000ff">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000">
</span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> i </span><span style="color:#808080">=</span><span style="color:#000000"> j </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000">, j </span><span style="color:#808080">=</span><span style="color:#000000"> </span><span style="color:#ff00ff">CHARINDEX</span><span style="color:#000000">(</span><span style="color:#008000">@d</span><span style="color:#000000">, </span><span style="color:#008000">@s</span><span style="color:#000000"> </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#008000">@d</span><span style="color:#000000">, j </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000">) </span><span style="color:#0000ff">FROM</span><span style="color:#000000"> split
   </span><span style="color:#0000ff">WHERE</span><span style="color:#000000"> </span><span style="color:#ff00ff">CHARINDEX</span><span style="color:#000000">(</span><span style="color:#008000">@d</span><span style="color:#000000">, </span><span style="color:#008000">@s</span><span style="color:#000000"> </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#008000">@d</span><span style="color:#000000">, j </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000">) </span><span style="color:#808080">&lt;&gt;</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">0</span><span style="color:#000000">
)
</span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> </span><span style="color:#ff00ff">SUBSTRING</span><span style="color:#000000">(</span><span style="color:#008000">@s</span><span style="color:#000000">,i,j</span><span style="color:#808080">-</span><span style="color:#000000">i)
</span><span style="color:#0000ff">FROM</span><span style="color:#000000"> split
</span></pre>
</div>
<p>O método é facilmente adaptável para o seu outro grande uso: parser de textos CSV. Basta apenas trocar o delimitador. Simples não?</p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:#f4f4f4;overflow:auto"><span style="color:#0000ff">DECLARE</span><span style="color:#000000"> </span><span style="color:#008000">@s</span><span style="color:#000000"> </span><span style="color:#0000ff">VARCHAR</span><span style="color:#000000">(</span><span style="color:#800000;font-weight:bold">8000</span><span style="color:#000000">), </span><span style="color:#008000">@d</span><span style="color:#000000"> </span><span style="color:#0000ff">VARCHAR</span><span style="color:#000000">(</span><span style="color:#800000;font-weight:bold">10</span><span style="color:#000000">)
</span><span style="color:#0000ff">SET</span><span style="color:#000000"> </span><span style="color:#008000">@s</span><span style="color:#000000"> </span><span style="color:#808080">=</span><span style="color:#000000"> </span><span style="color:#ff0000">'</span><span style="color:#ff0000">123;4;thiago@zavaschi.com;2232323</span><span style="color:#ff0000">'</span><span style="color:#000000">
</span><span style="color:#0000ff">SET</span><span style="color:#000000"> </span><span style="color:#008000">@d</span><span style="color:#000000"> </span><span style="color:#808080">=</span><span style="color:#000000"> </span><span style="color:#ff0000">'</span><span style="color:#ff0000">;</span><span style="color:#ff0000">'</span><span style="color:#000000"> 

</span><span style="color:#008080">--</span><span style="color:#008080"> Continuação mostrada anteriormente.</span></pre>
</div>
<p>Um exemplo interessante que sempre cobro em aula (acho que vou ter que parar de cobrar :P) é para realizar o exemplo clássico do fatorial. O resultado final eu diria ser muito elegante.</p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:#f4f4f4;overflow:auto"><span style="color:#0000ff">WITH</span><span style="color:#000000"> fat(f, n) </span><span style="color:#0000ff">AS</span><span style="color:#000000">
(
    </span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> </span><span style="color:#ff00ff">CAST</span><span style="color:#000000"> (</span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000"> </span><span style="color:#0000ff">as</span><span style="color:#000000"> </span><span style="color:#0000ff">bigint</span><span style="color:#000000">) </span><span style="color:#0000ff">as</span><span style="color:#000000"> f, </span><span style="color:#800000;font-weight:bold">0</span><span style="color:#000000">  </span><span style="color:#0000ff">as</span><span style="color:#000000"> n </span><span style="color:#008080">--</span><span style="color:#008080"> fat de 0 é 1</span><span style="color:#008080">
</span><span style="color:#000000">    </span><span style="color:#0000ff">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000">
    </span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> </span><span style="color:#ff00ff">CAST</span><span style="color:#000000"> (</span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000"> </span><span style="color:#0000ff">as</span><span style="color:#000000"> </span><span style="color:#0000ff">bigint</span><span style="color:#000000">) </span><span style="color:#0000ff">as</span><span style="color:#000000"> f, </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000"> </span><span style="color:#0000ff">as</span><span style="color:#000000"> n </span><span style="color:#008080">--</span><span style="color:#008080"> fat de 1 é 1</span><span style="color:#008080">
</span><span style="color:#000000">    </span><span style="color:#0000ff">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000">
    </span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> f </span><span style="color:#808080">*</span><span style="color:#000000"> (n </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000">), n </span><span style="color:#808080">+</span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000">
    </span><span style="color:#0000ff">FROM</span><span style="color:#000000"> fat
    </span><span style="color:#0000ff">WHERE</span><span style="color:#000000"> n </span><span style="color:#808080">&lt;</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">20</span><span style="color:#000000"> </span><span style="color:#808080">AND</span><span style="color:#000000"> n </span><span style="color:#808080">&lt;&gt;</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">0</span><span style="color:#000000">
    </span><span style="color:#008080">--</span><span style="color:#008080"> 20 é o limite neste caso, pois o fatorial de 21 </span><span style="color:#008080">
</span><span style="color:#000000">    </span><span style="color:#008080">--</span><span style="color:#008080"> não cabe em um tipo bigint. O &lt;&gt; 0 é para cortar a recursão</span><span style="color:#008080">
</span><span style="color:#000000">    </span><span style="color:#008080">--</span><span style="color:#008080"> do primeiro âncora, senão repetiria tudo, faça o teste. </span><span style="color:#008080">
</span><span style="color:#000000">)
</span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> f
</span><span style="color:#0000ff">FROM</span><span style="color:#000000"> fat
</span><span style="color:#0000ff">WHERE</span><span style="color:#000000"> n </span><span style="color:#808080">=</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">6</span><span style="color:#000000"> </span><span style="color:#008080">--</span><span style="color:#008080"> troque n pelo fatorial que quer calcular</span></pre>
</div>
<p>Para melhorar a legibilidade das queries. podemos encapsular isso em uma função:</p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:#f4f4f4;overflow:auto"><span style="color:#0000ff">CREATE</span><span style="color:#000000"> </span><span style="color:#0000ff">FUNCTION</span><span style="color:#000000"> fatorial
(
    </span><span style="color:#008000">@n</span><span style="color:#000000"> </span><span style="color:#0000ff">INT</span><span style="color:#000000">
)
</span><span style="color:#0000ff">RETURNS</span><span style="color:#000000"> </span><span style="color:#0000ff">INT</span><span style="color:#000000">
</span><span style="color:#0000ff">AS</span><span style="color:#000000">
</span><span style="color:#0000ff">BEGIN</span><span style="color:#000000">
    </span><span style="color:#0000ff">DECLARE</span><span style="color:#000000"> </span><span style="color:#008000">@val</span><span style="color:#000000"> </span><span style="color:#0000ff">INT</span><span style="color:#000000">;
    </span><span style="color:#0000ff">WITH</span><span style="color:#000000"> fat(f, n) </span><span style="color:#0000ff">AS</span><span style="color:#000000">
    (
        </span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> </span><span style="color:#ff00ff">CAST</span><span style="color:#000000"> (</span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000"> </span><span style="color:#0000ff">as</span><span style="color:#000000"> </span><span style="color:#0000ff">bigint</span><span style="color:#000000">) </span><span style="color:#0000ff">as</span><span style="color:#000000"> f, </span><span style="color:#800000;font-weight:bold">0</span><span style="color:#000000">  </span><span style="color:#0000ff">as</span><span style="color:#000000"> n
        </span><span style="color:#0000ff">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000">
        </span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> </span><span style="color:#ff00ff">CAST</span><span style="color:#000000"> (</span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000"> </span><span style="color:#0000ff">as</span><span style="color:#000000"> </span><span style="color:#0000ff">bigint</span><span style="color:#000000">) </span><span style="color:#0000ff">as</span><span style="color:#000000"> f, </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000"> </span><span style="color:#0000ff">as</span><span style="color:#000000"> n
        </span><span style="color:#0000ff">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000">
        </span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> f </span><span style="color:#808080">*</span><span style="color:#000000"> (n </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000">), n </span><span style="color:#808080">+</span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000">
        </span><span style="color:#0000ff">FROM</span><span style="color:#000000"> fat
        </span><span style="color:#0000ff">WHERE</span><span style="color:#000000"> n </span><span style="color:#808080">&lt;</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">20</span><span style="color:#000000"> </span><span style="color:#808080">AND</span><span style="color:#000000"> n </span><span style="color:#808080">&lt;&gt;</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">0</span><span style="color:#000000">
    )
    </span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> </span><span style="color:#008000">@val</span><span style="color:#000000"> </span><span style="color:#808080">=</span><span style="color:#000000"> f
    </span><span style="color:#0000ff">FROM</span><span style="color:#000000"> fat
    </span><span style="color:#0000ff">WHERE</span><span style="color:#000000"> n </span><span style="color:#808080">=</span><span style="color:#000000"> </span><span style="color:#008000">@n</span><span style="color:#000000">
    </span><span style="color:#0000ff">RETURN</span><span style="color:#000000"> </span><span style="color:#008000">@val</span><span style="color:#000000">
</span><span style="color:#0000ff">END</span><span style="color:#000000">
</span><span style="color:#0000ff">GO</span><span style="color:#000000">

</span><span style="color:#008080">--</span><span style="color:#008080"> Testando a função</span><span style="color:#008080">
</span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> dbo.fatorial(</span><span style="color:#800000;font-weight:bold">3</span><span style="color:#000000">);
</span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> dbo.fatorial(</span><span style="color:#800000;font-weight:bold">4</span><span style="color:#000000">);
</span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> dbo.fatorial(</span><span style="color:#800000;font-weight:bold">7</span><span style="color:#000000">);</span></pre>
</div>
<p><strong>(Abrindo um parênteses importante:</strong></p>
<p>A única resalva que faço é quanto a estes calculos matemáticos intensos em queries. O SQL Server trabalha muito bem com as ditas <em>set based operations. </em>ou seja, operações baseadas em conjuntos. Operações envolvendo manipulação de strings e cálculos matemáticos complexos não são o forte do SQL Server. Como resolver então, de outra maneira?</p>
<p>Desde o SQL Server 2005 (apareceu junto com as CTEs) o SQL Server tem a CLR integrada ao seu core. Isso quer dizer que é possível criar objetos para o SQL Server (User Defined Types – UDT, Stored Procedures, Functions, etc) programando em uma linguagem compatível com a Common Language Runtime e adicionar o assembly gerado ao seu banco SQL Server. Não fiz testes para esse exemplo do fatorial (farei em um futuro breve), mas é provável que o tempo de execução seja menor se o mesmo estivesse sendo executado pela CLR.</p>
<p>Farei um post detalhando melhor e também na Mundo.NET da edição de Dezembro/2009 sairá um artigo meu sobre como programar com SQLCLR usando C#!</p>
<p><strong>Fechando o parênteses muito importante)</strong></p>
<p>Para o próximo exemplo eu preciso fazer algumas considerações.</p>
<p>Com o SQL Server 2008 surgiu o tipo de dados HIERACHYID e que é, na minha visão, o melhor método de se trabalhar com registros que possuem comportamento hierárquico. Por uma série de motivos: o banco é SQL Server 2005 (ou esta com Compability Level 90), ou é uma estrutura legada, e se você deseja tratar hierarquias? </p>
<p>Commom Table Expressions podem fazer, e fazem, esse papel muito bem. Digamos que temos uma estrutura hirárquica de empregados e cada registro faz uma referência a outro registro que seria o seu supervisor dentro de uma empresa. O diretor da empresa não possui ligação com ninguém, uma vez que ele não teria um supervisor.</p>
<p>Para esse exemplo poderia usar a tabela de empregados do AdventureWorks, mas como nem sempre todos a possuem instalada (deveriam, pois há centenas de exemplos que a utilizam), eu mesmo montarei uma pequena “base”.</p>
<p>Segue o modelo de dados e script para criação da estrutura.</p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:#f4f4f4;overflow:auto"><span style="color:#008080">--</span><span style="color:#008080"> Cria tabela base</span><span style="color:#008080">
</span><span style="color:#0000ff">CREATE</span><span style="color:#000000"> </span><span style="color:#0000ff">TABLE</span><span style="color:#000000"> Empregado
(
    id </span><span style="color:#0000ff">INT</span><span style="color:#000000"> </span><span style="color:#0000ff">PRIMARY</span><span style="color:#000000"> </span><span style="color:#0000ff">KEY</span><span style="color:#000000">,
    nome </span><span style="color:#0000ff">VARCHAR</span><span style="color:#000000">(</span><span style="color:#800000;font-weight:bold">50</span><span style="color:#000000">) </span><span style="color:#808080">NOT</span><span style="color:#000000"> </span><span style="color:#0000ff">NULL</span><span style="color:#000000">,
    cargo </span><span style="color:#0000ff">VARCHAR</span><span style="color:#000000">(</span><span style="color:#800000;font-weight:bold">50</span><span style="color:#000000">) </span><span style="color:#808080">NOT</span><span style="color:#000000"> </span><span style="color:#0000ff">NULL</span><span style="color:#000000">,
    id_supervisor </span><span style="color:#0000ff">INT</span><span style="color:#000000"> </span><span style="color:#0000ff">NULL</span><span style="color:#000000">
        </span><span style="color:#0000ff">CONSTRAINT</span><span style="color:#000000"> fk_productSales_pid
        </span><span style="color:#0000ff">FOREIGN</span><span style="color:#000000"> </span><span style="color:#0000ff">KEY</span><span style="color:#000000"> </span><span style="color:#0000ff">REFERENCES</span><span style="color:#000000"> Empregado(id)
);

</span><span style="color:#008080">--</span><span style="color:#008080"> Popula a tabela (reparem na sintaxe do insert, </span><span style="color:#008080">
--</span><span style="color:#008080"> só é permitida no SQL Server 2008 ou superior).</span><span style="color:#008080">
</span><span style="color:#0000ff">INSERT</span><span style="color:#000000"> </span><span style="color:#0000ff">INTO</span><span style="color:#000000"> Empregado </span><span style="color:#0000ff">VALUES</span><span style="color:#000000">
(</span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000">, </span><span style="color:#ff0000">'</span><span style="color:#ff0000">Thiago Zavaschi</span><span style="color:#ff0000">'</span><span style="color:#000000">, </span><span style="color:#ff0000">'</span><span style="color:#ff0000">Diretor</span><span style="color:#ff0000">'</span><span style="color:#000000">, </span><span style="color:#0000ff">null</span><span style="color:#000000">),
(</span><span style="color:#800000;font-weight:bold">2</span><span style="color:#000000">, </span><span style="color:#ff0000">'</span><span style="color:#ff0000">Angelina Jolie</span><span style="color:#ff0000">'</span><span style="color:#000000">, </span><span style="color:#ff0000">'</span><span style="color:#ff0000">Gerente de Vendas</span><span style="color:#ff0000">'</span><span style="color:#000000">, </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000">),
(</span><span style="color:#800000;font-weight:bold">3</span><span style="color:#000000">, </span><span style="color:#ff0000">'</span><span style="color:#ff0000">Megan Fox</span><span style="color:#ff0000">'</span><span style="color:#000000">, </span><span style="color:#ff0000">'</span><span style="color:#ff0000">Gerente de Marketing</span><span style="color:#ff0000">'</span><span style="color:#000000">, </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000">),
(</span><span style="color:#800000;font-weight:bold">4</span><span style="color:#000000">, </span><span style="color:#ff0000">'</span><span style="color:#ff0000">Uma Thurman</span><span style="color:#ff0000">'</span><span style="color:#000000">, </span><span style="color:#ff0000">'</span><span style="color:#ff0000">Vendedor</span><span style="color:#ff0000">'</span><span style="color:#000000">, </span><span style="color:#800000;font-weight:bold">2</span><span style="color:#000000">),
(</span><span style="color:#800000;font-weight:bold">5</span><span style="color:#000000">, </span><span style="color:#ff0000">'</span><span style="color:#ff0000">Jessica Alba</span><span style="color:#ff0000">'</span><span style="color:#000000">, </span><span style="color:#ff0000">'</span><span style="color:#ff0000">Vendedor</span><span style="color:#ff0000">'</span><span style="color:#000000">, </span><span style="color:#800000;font-weight:bold">2</span><span style="color:#000000">),
(</span><span style="color:#800000;font-weight:bold">6</span><span style="color:#000000">, </span><span style="color:#ff0000">'</span><span style="color:#ff0000">Julia Roberts</span><span style="color:#ff0000">'</span><span style="color:#000000">, </span><span style="color:#ff0000">'</span><span style="color:#ff0000">Estagiário</span><span style="color:#ff0000">'</span><span style="color:#000000">, </span><span style="color:#800000;font-weight:bold">3</span><span style="color:#000000">);

</span></pre>
</div>
<p>Ao executar um simples select nesta tabela temos:</p>
<p><font size="2" face="Courier New">id       nome                       cargo                            id_supervisor<br />-------- -------------------------- -------------------------------- -------------<br />1        Thiago Zavaschi            Diretor                          NULL<br />2        Angelina Jolie             Gerente de Vendas                1<br />3        Megan Fox                  Gerente de Marketing             1<br />4        Uma Thurman                Vendedor                         2<br />5        Jessica Alba               Vendedor                         2<br />6        Julia Roberts              Estagiário                       3 </font></p>
<p>Creio que todos deverão concordar comigo que esta visulização torna praticamente impossível a noção de hierarquia (e olhe que temos poucos registros, imagine termos centenas dos mesmos.</p>
<p>É fácil retornar qual é o nome do supervisor, mas isso de maneira recursiva não seria nada tranquilo. Vou montar uma query que me retorne o nível da pessoa na hierarquia. Ou seja, para o Diretor o valor será 1, para os gerentes será 2, e para o restante será 3. Com a minah CTE retornando isso, a query subsequente, que consome a CTE podera formatar os resultados, filtrar, enfim, tudo baseado no resultado da consulta recursiva já processada, bacana né? Vamos lá!</p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:#f4f4f4;overflow:auto"><span style="color:#000000">;</span><span style="color:#0000ff">WITH</span><span style="color:#000000"> hierarquia(id, nome, cargo, id_supervisor, nivel_hierarquia)
</span><span style="color:#0000ff">AS</span><span style="color:#000000">
(
    </span><span style="color:#0000ff">SELECT</span><span style="color:#000000">  id, nome, cargo, id_supervisor, </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000"> </span><span style="color:#0000ff">as</span><span style="color:#000000"> nivel_hierarquia
    </span><span style="color:#0000ff">FROM</span><span style="color:#000000"> Empregado
    </span><span style="color:#0000ff">WHERE</span><span style="color:#000000"> id_supervisor </span><span style="color:#0000ff">IS</span><span style="color:#000000"> </span><span style="color:#0000ff">NULL</span><span style="color:#000000">
    </span><span style="color:#008080">--</span><span style="color:#008080"> Perceba que aqui no âncora temos como retorno somente o diretor.</span><span style="color:#008080">
</span><span style="color:#000000">    </span><span style="color:#0000ff">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000"> </span><span style="color:#008080">--</span><span style="color:#008080"> Ligação para a recursão</span><span style="color:#008080">
</span><span style="color:#000000">    </span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> e.id, e.nome, e.cargo, e.id_supervisor, nivel_hierarquia </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000">
    </span><span style="color:#0000ff">FROM</span><span style="color:#000000"> Empregado e
    </span><span style="color:#0000ff">INNER</span><span style="color:#000000"> </span><span style="color:#808080">JOIN</span><span style="color:#000000"> hierarquia h </span><span style="color:#0000ff">ON</span><span style="color:#000000"> h.id</span><span style="color:#808080">=</span><span style="color:#000000"> e.id_supervisor
)
</span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> </span><span style="color:#808080">*</span><span style="color:#000000"> </span><span style="color:#0000ff">FROM</span><span style="color:#000000"> hierarquia</span></pre>
</div>
<p>A saída deste script é (reparem na coluna nivel_herarquia):</p>
<p><font size="2" face="Courier New">id       nome                 cargo                       id_supervisor nivel_hierarquia<br />-------- -------------------- --------------------------- ------------- ----------------<br />1        Thiago Zavaschi      Diretor                     NULL          1<br />2        Angelina Jolie       Gerente de Vendas           1             2<br />3        Megan Fox            Gerente de Marketing        1             2<br />6        Julia Roberts        Estagiário                  3             3<br />4        Uma Thurman          Vendedor                    2             3<br />5        Jessica Alba         Vendedor                    2             3</font> </p>
<p><em>“Ok thiago, mas ainda assim não estou conseguindo visualizar, por exemplo, quem é o supervisor, tem como melhorar?”</em></p>
<p>Tem sim. uma das utilidades das CTEs é: a mesma retornar um result set “complexo de ser calculado” (como o caso da hierarquia) e deixar para o select mais abaixo fazer a formatação/filtragem/ordenação. Isso eixar a estrutura da sua CTE mais genérica e menos “engessada”, fazendo até mesmo com que você possa reaproveitar esse código em outro lugar.</p>
<p>Por exemplo: quero uma listagem com todo o time operacional (maior valor em nivel_hierarquia) da empresa, com seus respectivos supervisores. A query poderia ficar assim:</p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:#f4f4f4;overflow:auto"><span style="color:#000000">;</span><span style="color:#0000ff">WITH</span><span style="color:#000000"> hierarquia(id, nome, cargo, id_supervisor, nivel_hierarquia)
</span><span style="color:#0000ff">AS</span><span style="color:#000000">
(
    </span><span style="color:#0000ff">SELECT</span><span style="color:#000000">  id, nome, cargo, id_supervisor, </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000"> </span><span style="color:#0000ff">as</span><span style="color:#000000"> nivel_hierarquia
    </span><span style="color:#0000ff">FROM</span><span style="color:#000000"> Empregado
    </span><span style="color:#0000ff">WHERE</span><span style="color:#000000"> id_supervisor </span><span style="color:#0000ff">IS</span><span style="color:#000000"> </span><span style="color:#0000ff">NULL</span><span style="color:#000000">
    </span><span style="color:#008080">--</span><span style="color:#008080"> Perceba que aqui no âncora temos como retorno somente o diretor.</span><span style="color:#008080">
</span><span style="color:#000000">    </span><span style="color:#0000ff">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000"> </span><span style="color:#008080">--</span><span style="color:#008080"> Ligação para a recursão</span><span style="color:#008080">
</span><span style="color:#000000">    </span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> e.id, e.nome, e.cargo, e.id_supervisor, nivel_hierarquia </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000">
    </span><span style="color:#0000ff">FROM</span><span style="color:#000000"> Empregado e
    </span><span style="color:#0000ff">INNER</span><span style="color:#000000"> </span><span style="color:#808080">JOIN</span><span style="color:#000000"> hierarquia h </span><span style="color:#0000ff">ON</span><span style="color:#000000"> h.id</span><span style="color:#808080">=</span><span style="color:#000000"> e.id_supervisor
)
</span><span style="color:#0000ff">SELECT</span><span style="color:#000000">
h.nome </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#ff0000">'</span><span style="color:#ff0000">/</span><span style="color:#ff0000">'</span><span style="color:#000000"> </span><span style="color:#808080">+</span><span style="color:#000000"> h.cargo </span><span style="color:#0000ff">as</span><span style="color:#000000"> </span><span style="color:#ff0000">[</span><span style="color:#ff0000">Operacional</span><span style="color:#ff0000">]</span><span style="color:#000000">, </span><span style="color:#008080">--</span><span style="color:#008080"> perceba a formatação ocorrendo aqui</span><span style="color:#008080">
</span><span style="color:#000000">s.nome </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#ff0000">'</span><span style="color:#ff0000">/</span><span style="color:#ff0000">'</span><span style="color:#000000"> </span><span style="color:#808080">+</span><span style="color:#000000"> s.cargo </span><span style="color:#0000ff">as</span><span style="color:#000000"> </span><span style="color:#ff0000">[</span><span style="color:#ff0000">Tático</span><span style="color:#ff0000">]</span><span style="color:#000000">
</span><span style="color:#0000ff">FROM</span><span style="color:#000000"> hierarquia h </span><span style="color:#808080">JOIN</span><span style="color:#000000"> Empregado s </span><span style="color:#0000ff">ON</span><span style="color:#000000"> h.id_supervisor </span><span style="color:#808080">=</span><span style="color:#000000"> s.id
</span><span style="color:#0000ff">WHERE</span><span style="color:#000000"> h.nivel_hierarquia </span><span style="color:#808080">=</span><span style="color:#000000">
(</span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> </span><span style="color:#ff00ff">MAX</span><span style="color:#000000">(nivel_hierarquia) </span><span style="color:#0000ff">FROM</span><span style="color:#000000"> hierarquia) </span><span style="color:#008080">--</span><span style="color:#008080"> perceba que o filtro ocorreu aqui</span><span style="color:#008080">
</span></pre>
</div>
<p>Com o seguinte output:</p>
<p><font size="2" face="Courier New">Operacional                     Tático<br />------------------------------- ------------------------------------<br />Julia Roberts/Estagiário        Megan Fox/Gerente de Marketing<br />Uma Thurman/Vendedor            Angelina Jolie/Gerente de Vendas<br />Jessica Alba/Vendedor           Angelina Jolie/Gerente de Vendas</font> </p>
<p>Tranquilo né? E assim poderíamos evoluir de “N” formas, cada uma atendendo a uma necessidade específica.</p>
<p>Common table Expressions também podem ser utilizadas de outras formas, para fazer tratamento de datas, carga, e muitas outras funções (ver links no final do post para maiores informações).</p>
<p><strong>O que mais saber sobre Common Table Expressions (CTE) ?</strong></p>
<p>Bom, já vimos bastante coisa sobre o que é uma CTE, como funciona, onde se aplica (com alguns exemplos), mas o que mais devemos saber?</p>
<p>Devemos saber que há <em>restrições</em> sobre oq eu podemos usar ou não em CTEs. Vou listar alguams das que eu considero principais (no Books Online – BOL, há a lista completa, ver link ao final do post).</p>
<p>Bom acredito que a primeira coisa que é importante saber é que NÃO é permitido fazer uma referência recursiva dentro de uma subquery. Vou usar o exemplo anterior para demonstrar:</p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:#f4f4f4;overflow:auto"><span style="color:#000000">...
</span><span style="color:#0000ff">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000">
    </span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> e.id, e.nome, e.cargo, e.id_supervisor,
    (</span><span style="color:#0000ff">SELECT</span><span style="color:#000000"> h.nivel_hierarquia_2 </span><span style="color:#0000ff">FROM</span><span style="color:#000000"> hierarquia) </span><span style="color:#008080">--</span><span style="color:#008080"> ISSO NÃO É PERMITIDO.</span><span style="color:#008080">
</span><span style="color:#000000">    </span><span style="color:#0000ff">FROM</span><span style="color:#000000"> Empregado e
    ...
)
...</span></pre>
</div>
<p>O que nos gera um erro similar a:</p>
<p><font color="#ff0000" size="2" face="Courier New">Msg 465, Level 16, State 1, Line 32<br />Recursive references are not allowed in subqueries.</font> </p>
<p>O que mais não é permitido na definição do membro recursivo? Segue a lista: </p>
<ul>
<li>SELECT DISTINCT
<li>GROUP BY
<li>HAVING
<li>Scalar aggregation
<li>TOP
<li>LEFT, RIGHT, OUTER JOIN (obs: o INNER JOIN é permitido)
<li>A hint applied to a recursive reference to a CTE inside a <em>CTE_query_definition</em>.</li>
</li>
</li>
</li>
</li>
</li>
</li>
</ul>
<p>Você, leitor, deve ter percebido que eu normalmente iniciei minhas CTE’s com um ponto e vírgula (‘;’), não, não fiquei maluco e nem coloquei por engano.<br />A questão é que a sintaxe para execução da CTE <strong>obriga</strong> que o statement anterior termine com ponto e vírgula, então acabo sempre colocando, já que o statement anterior normalmente não possui “;” ao seu término!</p>
<p>Há algumas guidelines no books online e vou apenas trancrever aqui, em inglês mesmo (consultar o link no final para o texto completo – em inglês), omiti sobre o maxrecursion que eu já comentei.</p>
<li>All columns returned by the recursive CTE are nullable regardless of the nullability of the columns returned by the participating SELECT statements.
<li>A view that contains a recursive common table expression cannot be used to update data.
<li>Cursors may be defined on queries using CTEs. The CTE is the <em>select_statement</em> argument that defines the result set of the cursor. Only fast forward-only and static (snapshot) cursors are allowed for recursive CTEs. If another cursor type is specified in a recursive CTE, the cursor type is converted to static.
<li>Tables on remote servers may be referenced in the CTE. If the remote server is referenced in the recursive member of the CTE, a spool is created for each remote table so the tables can be repeatedly accessed locally. If it is a CTE query, Index Spool/Lazy Spools is displayed in the query plan and will have the additional WITH STACK predicate. This is one way to confirm proper recursion.
<li>SQL Server 2008 does not allow for analytic and aggregate functions in the recursive part of the CTE.
<p> </p>
<p>Com alguns cuidados básicos é possível extrair o máximo da capacidade das CTEs!</p>
<p><strong>Para saber mais</strong></p>
<p>Seguem alguns links aonde é possível encontrar mais informações sobre as Common Table Expressions. A diferença entre CTE no SQL Server 2008 para o 2008 são mínimas, mas como a própria Microsoft mantém em links separados, seguem ambos os links!</p>
<p>Using Common Table Expressions (SQL Server 2008):<br /><a title="http://msdn.microsoft.com/en-us/library/ms190766.aspx" href="http://msdn.microsoft.com/en-us/library/ms190766.aspx">http://msdn.microsoft.com/en-us/library/ms190766.aspx</a>.</p>
<p>Using Common Table Expressions (SQL Server 2005): <br /><a title="http://msdn.microsoft.com/en-us/library/ms190766%28SQL.90%29.aspx" href="http://msdn.microsoft.com/en-us/library/ms190766(SQL.90).aspx">http://msdn.microsoft.com/en-us/library/ms190766%28SQL.90%29.aspx</a></p>
<p>WITH common_table_expression (Transact-SQL) (SQL Server 2008, contém diversos exemplos, muito bom!):<br /><a title="http://msdn.microsoft.com/en-us/library/ms175972.aspx" href="http://msdn.microsoft.com/en-us/library/ms175972.aspx">http://msdn.microsoft.com/en-us/library/ms175972.aspx</a></p>
<p>WITH common_table_expression (Transact-SQL) (SQL Server 2005):<br /><a title="http://msdn.microsoft.com/en-us/library/ms175972%28SQL.90%29.aspx" href="http://msdn.microsoft.com/en-us/library/ms175972(SQL.90).aspx">http://msdn.microsoft.com/en-us/library/ms175972%28SQL.90%29.aspx</a></p>
<p>While x CTE (com exemplos de como fazer carga de dados usando CTE):<br /><a title="http://sqlfromhell.wordpress.com/2009/09/12/while-vs-cte-popular-tabela-de-testes/" href="http://sqlfromhell.wordpress.com/2009/09/12/while-vs-cte-popular-tabela-de-testes/">http://sqlfromhell.wordpress.com/2009/09/12/while-vs-cte-popular-tabela-de-testes/</a></p>
<p>Recursive Queries Using Common Table Expressions (SQL Server 2008):<br /><a title="http://msdn.microsoft.com/en-us/library/ms186243.aspx" href="http://msdn.microsoft.com/en-us/library/ms186243.aspx">http://msdn.microsoft.com/en-us/library/ms186243.aspx</a></p>
<p>Recursive Queries Using Common Table Expressions (SQL Server 2005):<br /><a title="http://msdn.microsoft.com/en-us/library/ms186243%28SQL.90%29.aspx" href="http://msdn.microsoft.com/en-us/library/ms186243(SQL.90).aspx">http://msdn.microsoft.com/en-us/library/ms186243%28SQL.90%29.aspx</a></p>
<h6>Mundo .NET Ed. № 16 – Recursividade, Hierarquias, CTEs e Consultas SQL aplicadas ao Marketing de Rede</h6>
<p>(Tive a oportunidade de revisar este artigo do Gustavo Maia Aguiar – MVP em SQL Server, vale a pena conferir! O artigo se encontra apenas na revista)<br /><a title="http://gustavomaiaaguiar.spaces.live.com/blog/cns!F4F5C630410B9865!695.entry" href="http://gustavomaiaaguiar.spaces.live.com/blog/cns!F4F5C630410B9865!695.entry">http://gustavomaiaaguiar.spaces.live.com/blog/cns!F4F5C630410B9865!695.entry</a></p>
</li>
<p>Using CLR Integration in SQL Server 2005 (Se alguém quiser ir se adiantando um pouco no assunto!)<br /><a title="http://msdn.microsoft.com/en-us/library/ms345136%28SQL.90%29.aspx" href="http://msdn.microsoft.com/en-us/library/ms345136(SQL.90).aspx">http://msdn.microsoft.com/en-us/library/ms345136%28SQL.90%29.aspx</a></p>
<p><strong>Finalizando</strong></p>
<p>UFA! Bom pessoal, espero que todos tenham chegado até aqui, e espero também que gostem tanto do artigo quanto eu gostei de fazê-lo!</p>
<p>Utilizem como referência para consultas futuras e qualquer sugestão estou aberto para ouvir!</p>
<p>Comentei que há o tipo de dados HIERARCHYID, presente no SQL Server 2008 e superirores) para manipular registros essencialmente hierárquicos, mas acho que isso não compete a este post, abordo isso mais profundamente numa próxima oportunidade!</p>
<p>Grande abraço!<br />Thiago Zavaschi</p>
<p><em>PS: Tive que dividir o artigo em duas partes, pois o Spaces não deixa publicar posts grandes (para a primeira parte veja o post anterior).</em></p>
</li>
</li>
</li>
</li>
</p></p>
]]></content:encoded>
			<wfw:commentRss>http://zavaschi.com/index.php/2009/09/entendendo-as-common-table-expressions-cte-parte-2-final/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Entendendo as Common Table Expressions &#8211; CTE &#8211; Parte 1</title>
		<link>http://zavaschi.com/index.php/2009/09/entendendo-as-common-table-expressions-cte-parte-1/</link>
		<comments>http://zavaschi.com/index.php/2009/09/entendendo-as-common-table-expressions-cte-parte-1/#comments</comments>
		<pubDate>Sun, 20 Sep 2009 11:40:40 +0000</pubDate>
		<dc:creator>Thiago Zavaschi</dc:creator>
				<category><![CDATA[Common Table Expression]]></category>
		<category><![CDATA[Tutorial]]></category>

		<guid isPermaLink="false">8DE5A8EFC1819ECA!390</guid>
		<description><![CDATA[Antes de mais nada devo dizer que não é um recurso novo, mas muitos desenvolvedores nunca ouviram falar sobre, ou se ouviram desistiram de tentar usar. Este recurso existe desde a versão 2005 do SQL Server e continua existindo nas versões posteriores (2008, 2008 R2 “Kilimanjaro”).
A uma primeira vista parece que as coisas “complicaram”, mas [...]]]></description>
			<content:encoded><![CDATA[<p>Antes de mais nada devo dizer que não é um recurso novo, mas muitos desenvolvedores nunca ouviram falar sobre, ou se ouviram desistiram de tentar usar. Este recurso existe desde a versão 2005 do SQL Server e continua existindo nas versões posteriores (2008, 2008 R2 “Kilimanjaro”).</p>
<p>A uma primeira vista parece que as coisas “complicaram”, mas depois de escrever uma ou duas CTE’s é facilmente perceptível o quão fácil (e vantajoso) é trabalhar com as mesmas.</p>
<p>Antes de dar uma descrição formal de uma CTE, vou executar um dos exemplos mais simples que pode ser feito.</p>
</p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:#F4F4F4;overflow:auto"><span style="color:#000000">;</span><span style="color:#0000FF">WITH</span><span style="color:#000000"> Simples(valor, nome)
</span><span style="color:#0000FF">AS</span><span style="color:#000000">
(
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000">, </span><span style="color:#FF0000">'</span><span style="color:#FF0000">thiago</span><span style="color:#FF0000">'</span><span style="color:#000000">
)
</span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#808080">*</span><span style="color:#000000"> </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> Simples</span></pre>
</div>
<p>E o resultado é: </p>
<p><font size="2" face="Courier New">valor       nome<br />----------- ------<br />1           thiago</font> </p>
<p>(1 row(s) affected) </p>
<p><p>Ahmm, ok, eu poderia ter feito o mesmo usando simplesmente algo como:</p>
</p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:#F4F4F4;overflow:auto"><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000">, </span><span style="color:#FF0000">'</span><span style="color:#FF0000">thiago</span><span style="color:#FF0000">'</span><span style="color:#000000"> </span></pre>
</div>
<p>Mas a vantagem (a primeira das muitas) é que você pode referenciar várias vezes a cte em sua query. Mas antes de demonstrar isso, creioq eu cabe a necessidade de uma definição formal do que seja uma Common Table Expression. Definição segundo o Books Online do SQL Server 2008:</p>
<p>Uma CTE (Common Table Expression) pode ser encarada como um result set que tem seu escopo definido pela execução de um única cláusula SELECT, INSERT, UPDATE, DELETE ou CREATE VIEW. O result set da CTE existe apenas durante a execução da query.<br />Uma CTE tem duas grandes capacidades: pode se auto referenciar (CTE recursiva) e pode ser referenciada várias vezes na mesma query. </p>
<p>A sintaxe de uma CTE é demonstrada a seguir: </p>
<p><code>WITH expression_name [ ( column_name [,...n] ) ]<br /></code><code>AS<br /></code><code>( CTE_query_definition )</code> </p>
<p>A lista de nomes de colunas é opcional somente se a query da CTE retornar colunas com nomes únicos. Contudo, é uma boa prática definir os nomes. </p>
<p>O comando para rodar a CTE seria: </p>
<p><code>SELECT &lt;column_list&gt;<br /></code><code>FROM expression_name;</code> </p>
<p>Um exemplo de uma CTE sendo referenciada várias vezes na mesma query: </p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:#F4F4F4;overflow:auto"><span style="color:#000000">;</span><span style="color:#0000FF">WITH</span><span style="color:#000000"> VariasVezes(valor)
</span><span style="color:#0000FF">AS</span><span style="color:#000000">
(
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000">
    </span><span style="color:#0000FF">UNION</span><span style="color:#000000">
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">33</span><span style="color:#000000">
    </span><span style="color:#0000FF">UNION</span><span style="color:#000000">
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">8</span><span style="color:#000000">
)
</span><span style="color:#0000FF">SELECT</span><span style="color:#000000">
(</span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#FF00FF">MAX</span><span style="color:#000000">(valor) </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> VariasVezes) </span><span style="color:#808080">+</span><span style="color:#000000">
(</span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#FF00FF">SUM</span><span style="color:#000000">(valor) </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> VariasVezes) </span><span style="color:#0000FF">AS</span><span style="color:#000000"> val</span></pre>
</div>
<p>Perceba que no select que consome a CTE eu a referenciei (“VariasVezes”) duas vezes. Cuidado que nas funções agregadas o conteúdo da CTE sofre scan para cada função agregada, e, dependendo,<em> uma abordagem diferente pode ser mais performática</em>, mas ganha-se muito com a legibilidade da query. Observe os scans no plano de execução da CTE VariasVezes.</p>
<p>Para facilitar a análise, segue o plano de execução em modo texto (instrução <em>SET SHOWPLAN_TEXT</em>). </p>
<p><font size="2" face="Courier New">StmtText<br />----------------------------------------------------------------------------------------------------------<br />  |--Compute Scalar(DEFINE:([Expr1012]=[Expr1004]+[Expr1010]))<br />       |--Nested Loops(Inner Join)<br />            |--Stream Aggregate(DEFINE:([Expr1004]=MAX([Union1003])))<br />            |    |--Concatenation<br />            |        </font><font size="2"><font face="Courier New"><strong>|--Constant Scan(VALUES:(((1))))<br /></strong>            | </font></font><font size="2"><font face="Courier New"><strong>        |--Constant Scan(VALUES:(((33))))<br /></strong>            |<strong>         |--Constant Scan(VALUES:(((8))))</strong><br />            |--Compute Scalar(DEFINE:([Expr1010]=CASE WHEN [Expr1019]=(0) THEN NULL ELSE [Expr1020] END))<br />                 |--Stream Aggregate(DEFINE:([Expr1019]=Count(*), [Expr1020]=SUM([Union1009])))<br />                      |--Concatenation<br />                          </font></font><strong><font size="2" face="Courier New">|--Constant Scan(VALUES:(((1))))<br />                           |--Constant Scan(VALUES:(((33))))<br />                           |--Constant Scan(VALUES:(((8))))</font></strong> </p>
<p>Essa característica já seria o suficiente para tornar o uso de CTEs muito interessante, mas na minha opinião a capacidade de auto-referenciar (queries recursivas) é que torna CTE um recurso tão poderoso.</p>
<p><strong>Queries Recursivas com Common Table Expressions</strong></p>
<p>Como já mencionei as CTEs podem se auto referenciar, isto é, dentro da própria definição da CTE.<br />Já ministrei diversos treinamentos em que CTE era um dos itens da ementa, e percebi que o melhor jeito de entender o funcionamento das mesmas é através de exemplos. Começarei com alguns simples e vou até alguns mais elaborados, com o passo a passo de cada execução para facilitar. :)</p>
<p>Vamos pensar um pouco, em que seria interessante a utilização de uma query recursiva? O exemplo clássico seria o cálculo de fatorial (faremos mais adiante), geração de números sequênciais, etc, etc.</p>
<p>Mas para usar uma CTE recursiva basta apenas referenciar ela mesma? Sim e não, na verdade não. É necessário a utilização do operador UNION ALL além da introdução de dois novos termos/conceitos: Membro Âncora e o Membro Recursivo. Segue a estruturação na forma de um exemplo que gera números sequênciais de 1 até 10:</p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:#F4F4F4;overflow:auto"><span style="color:#000000">;</span><span style="color:#0000FF">WITH</span><span style="color:#000000"> NumerosSequenciais(num)
</span><span style="color:#0000FF">AS</span><span style="color:#000000">
(
     </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000"> </span><span style="color:#0000FF">AS</span><span style="color:#000000"> num </span><span style="color:#008080">--</span><span style="color:#008080"> Este é o membro âncora, será o primeiro a ser executado. </span><span style="color:#008080">
</span><span style="color:#000000">     </span><span style="color:#0000FF">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000"> </span><span style="color:#008080">--</span><span style="color:#008080"> Fará a ligação do membro âncora com o membro recursivo.</span><span style="color:#008080">
</span><span style="color:#000000">     </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> num </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000"> </span><span style="color:#0000FF">AS</span><span style="color:#000000"> num </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> NumerosSequenciais
     </span><span style="color:#0000FF">WHERE</span><span style="color:#000000"> num </span><span style="color:#808080">&lt;</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">10</span><span style="color:#000000"> </span><span style="color:#008080">--</span><span style="color:#008080"> perceba que o WHERE é bastante importante para delimitar a parada da recursão.</span><span style="color:#008080">
</span><span style="color:#000000">)
</span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#808080">*</span><span style="color:#000000"> </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> NumerosSequenciais</span></pre>
</div>
<p>Uma coisa comum, que vejo muitos se confundirem (o que é razoavél, uma vez que essa recursão é um pouco diferente das demais), é achar que o membro âncora será o critério de parada, e isso <strong>NÃO</strong> é verdade. Na realidade, ele é o primeiro a ser executado. O critério de parada é o membro recursivo parar de fornecer novas linhas de retorno para serem “processadas”. Veja a execução do passo a passo mais adiante.</p>
<p>Segue mais um exemplo, mas agora manipulando um elemento do tipo VARCHAR. Um ponto importante a se observar é que não funcionará sem a utilização da função CONVERT, e se deve ao fato de que os (os? veja adiante) membros âncoras e recursicos (plural novamente? veja adiante)  devem retornar o mesmo tipo de dado em cada coluna (no caso, VARCHAR(4000)).</p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:#F4F4F4;overflow:auto"><span style="color:#000000">;</span><span style="color:#0000FF">WITH</span><span style="color:#000000"> Nome(nome)
</span><span style="color:#0000FF">AS</span><span style="color:#000000">
(
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> nome </span><span style="color:#808080">=</span><span style="color:#000000"> </span><span style="color:#FF00FF">CONVERT</span><span style="color:#000000">(</span><span style="color:#0000FF">VARCHAR</span><span style="color:#000000">(</span><span style="color:#800000;font-weight:bold">4000</span><span style="color:#000000">),</span><span style="color:#FF0000">'</span><span style="color:#FF0000">Thiago</span><span style="color:#FF0000">'</span><span style="color:#000000">)
    </span><span style="color:#0000FF">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000">
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#FF00FF">CONVERT</span><span style="color:#000000">(</span><span style="color:#0000FF">VARCHAR</span><span style="color:#000000">(</span><span style="color:#800000;font-weight:bold">4000</span><span style="color:#000000">),nome </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#FF0000">'</span><span style="color:#FF0000">Zavaschi</span><span style="color:#FF0000">'</span><span style="color:#000000">) </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> Nome
    </span><span style="color:#0000FF">WHERE</span><span style="color:#000000"> </span><span style="color:#FF00FF">LEN</span><span style="color:#000000">(nome) </span><span style="color:#808080">&lt;</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">30</span><span style="color:#000000">
)
</span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> nome </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> Nome</span></pre>
</div>
<p>O resultado da query recursiva acima é:</p>
<p><font size="2" face="Courier New">Thiago<br />ThiagoZavaschi<br />ThiagoZavaschiZavaschi<br />ThiagoZavaschiZavaschiZavaschi</font> </p>
<p><strong>Queries recursivas com mais de um membro âncora/recursivo</strong></p>
<p>Aqui é que as coisas podem parecer confusas (e até são de certo modo), tenho que confessar que nunca (que eu me lembre) precisei usar mais de um membro recursivo na mesma CTE, mas devemos conhecer todas as possibilidades!</p>
<p>Vamos voltar a query anterior que concatenava ‘Zavaschi’ ao nome ‘Thiago’ e adicionarei um novo âncora.</p>
<p>Mas e como fazer isso? Basta adicionar mais um SELECT que não faça referência à propria CTE (senão seria um membro recursivo, hehe). o resultado fica algo parecido com:</p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:#F4F4F4;overflow:auto"><span style="color:#000000">;</span><span style="color:#0000FF">WITH</span><span style="color:#000000"> Nome(nome)
</span><span style="color:#0000FF">AS</span><span style="color:#000000">
(
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> nome </span><span style="color:#808080">=</span><span style="color:#000000"> </span><span style="color:#FF00FF">CONVERT</span><span style="color:#000000">(</span><span style="color:#0000FF">VARCHAR</span><span style="color:#000000">(</span><span style="color:#800000;font-weight:bold">4000</span><span style="color:#000000">),</span><span style="color:#FF0000">'</span><span style="color:#FF0000">Thiago</span><span style="color:#FF0000">'</span><span style="color:#000000">)
    </span><span style="color:#0000FF">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000">
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> nome </span><span style="color:#808080">=</span><span style="color:#000000"> </span><span style="color:#FF00FF">CONVERT</span><span style="color:#000000">(</span><span style="color:#0000FF">VARCHAR</span><span style="color:#000000">(</span><span style="color:#800000;font-weight:bold">4000</span><span style="color:#000000">),</span><span style="color:#FF0000">'</span><span style="color:#FF0000">José</span><span style="color:#FF0000">'</span><span style="color:#000000">)
    </span><span style="color:#0000FF">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000">
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#FF00FF">CONVERT</span><span style="color:#000000">(</span><span style="color:#0000FF">VARCHAR</span><span style="color:#000000">(</span><span style="color:#800000;font-weight:bold">4000</span><span style="color:#000000">),nome </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#FF0000">'</span><span style="color:#FF0000">Zavaschi</span><span style="color:#FF0000">'</span><span style="color:#000000">) </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> Nome
    </span><span style="color:#0000FF">WHERE</span><span style="color:#000000"> </span><span style="color:#FF00FF">LEN</span><span style="color:#000000">(nome) </span><span style="color:#808080">&lt;</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">30</span><span style="color:#000000">
)
</span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> nome </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> Nome</span></pre>
</div>
<p>Conseguem imaginar o que irá acontecer? Na verdade o que eu acho que um desenvolvedor tem que se preocupar é a ORDEM com que será retornado os registros (supondo que já haja um entendimento sobre CTEs).</p>
<p>O resultado desta query é:</p>
<p><font size="2" face="Courier New">nome<br />-------------------------------------<br />Thiago<br />José<br />JoséZavaschi<br />JoséZavaschiZavaschi<br />JoséZavaschiZavaschiZavaschi<br />JoséZavaschiZavaschiZavaschiZavaschi<br />ThiagoZavaschi<br />ThiagoZavaschiZavaschi<br />ThiagoZavaschiZavaschiZavaschi</font> </p>
<p>(9 row(s) affected) </p>
<p>Por que isso ocorre? Ou melhor, “me explica o que ocorreu”. Vamos lá! Obs: vou ir negritando os registros e colocando a query que foi “executada” para facilitar no entendimento do output encontrado. </p>
<p>1) Como a “parte” que é executada primeiro é a da âncora, ou das âncoras, e por ordem no UNIOL ALL, temos o ‘Thiago’ e depois o ‘José’. </p>
<p><font size="2" face="Courier New">SELECT nome = CONVERT(VARCHAR(4000),'Thiago')<br />UNION ALL<br />SELECT nome = CONVERT(VARCHAR(4000),'José')</font> </p>
<p><font size="2"><font face="Courier New"><strong>Thiago<br />José</strong><br />JoséZavaschi<br />JoséZavaschiZavaschi<br />JoséZavaschiZavaschiZavaschi<br />JoséZavaschiZavaschiZavaschiZavaschi<br />ThiagoZavaschi<br />ThiagoZavaschiZavaschi<br />ThiagoZavaschiZavaschiZavaschi</font></font> </p>
<p>2) Foram executados os âncoras agora é a vez da execução do membro recursivo.</p>
<p><font size="2" face="Courier New">SELECT CONVERT(VARCHAR(4000),nome + 'Zavaschi') FROM Nome<br />    WHERE LEN(nome) &lt; 30</font> </p>
<p>O conteúdo do “nome” atual é “José” (última âncora selecionada), perceba que no FROM temos referenciada a própria CTE! </p>
<p>A execução da query seria algo como (de maneira BEM simplificada):</p>
<p><font size="2" face="Courier New">SELECT ‘José’ + 'Zavaschi' WHERE LEN(‘José’ + 'Zavaschi') &lt; 30</font></p>
<p>Esse registro retornou alguma linha? Sim. então continua processando esta query recursiva.</p>
<p><font size="2"><font face="Courier New"><strong>Thiago<br />José</strong><br /></font></font><font size="2"><font face="Courier New"><strong>JoséZavaschi<br /></strong>JoséZavaschiZavaschi<br />JoséZavaschiZavaschiZavaschi<br />JoséZavaschiZavaschiZavaschiZavaschi<br />ThiagoZavaschi<br />ThiagoZavaschiZavaschi<br />ThiagoZavaschiZavaschiZavaschi</font></font> </p>
<p>3) Mas qual é o conteúdo do “nome” neste momento? “JoséZavaschi”.</p>
<p>A query recursiva retorna mais alguma linha (atentar para o WHERE)? Sim. entao retorna o valor presente em “nome” e continua.<br />O processo se mantém enquanto o conteúdo de “nome” atender à nossa restrição no WHERE (o WHERE é feito com o conteúdo de nome da iteracao anterior, ANTES, da concatenação).</p>
<p><font size="2"><font face="Courier New"><strong>Thiago<br />José</strong><br /></font></font><font size="2"><font face="Courier New"><strong>JoséZavaschi<br />JoséZavaschiZavaschi<br />JoséZavaschiZavaschiZavaschi<br />JoséZavaschiZavaschiZavaschiZavaschi</strong><br />ThiagoZavaschi<br />ThiagoZavaschiZavaschi<br />ThiagoZavaschiZavaschiZavaschi</font></font> </p>
<p>4) o Conteúdo de “nome” agora é:  JoséZavaschiZavaschiZavaschiZavaschi que possui mais de 30 caracteres, então a query recursiva não retorna mais registros e se encerra…ria, caso só tivéssemos uma única âncora. Mas o processo continua para a âncora anterior e assim por diante. E é por isto que temos a saída do Thiago, ThiagoZavaschi, e assim por diante.</p>
<p><font size="2"><font face="Courier New"><strong>Thiago<br />José</strong><br /></font></font><font size="2"><font face="Courier New"><strong>JoséZavaschi<br />JoséZavaschiZavaschi<br />JoséZavaschiZavaschiZavaschi<br />JoséZavaschiZavaschiZavaschiZavaschi</strong><br /></font></font><strong><font size="2" face="Courier New">ThiagoZavaschi<br />ThiagoZavaschiZavaschi<br />ThiagoZavaschiZavaschiZavaschi</font></strong> </p>
<p>5) Quando a query recursiva não retornar mais nada, a CTE verifica se há alguma outra âncora “esperando”, se não há então já temos o result set que servirá de base para o SELECT, INSERT, DELETE, etc., que estiver após a CTE. Caso tenha uma outra âncora, esta será processada e assim por diante.</p>
<p>---</p>
<p>E para mais de um membro recursivo, como ficaria? Bem não vou fazer execução detalha, pois ficaria muito grande e seria muito repetitivo, mas vejam os seguintes scripts (e os testem).</p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:#F4F4F4;overflow:auto"><span style="color:#000000">;</span><span style="color:#0000FF">WITH</span><span style="color:#000000"> Nome(nome)
</span><span style="color:#0000FF">AS</span><span style="color:#000000">
(
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> nome </span><span style="color:#808080">=</span><span style="color:#000000"> </span><span style="color:#FF00FF">CONVERT</span><span style="color:#000000">(</span><span style="color:#0000FF">VARCHAR</span><span style="color:#000000">(</span><span style="color:#800000;font-weight:bold">4000</span><span style="color:#000000">),</span><span style="color:#FF0000">'</span><span style="color:#FF0000">Thiago</span><span style="color:#FF0000">'</span><span style="color:#000000">)
    </span><span style="color:#0000FF">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000">
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#FF00FF">CONVERT</span><span style="color:#000000">(</span><span style="color:#0000FF">VARCHAR</span><span style="color:#000000">(</span><span style="color:#800000;font-weight:bold">4000</span><span style="color:#000000">),nome </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#FF0000">'</span><span style="color:#FF0000">Zavaschi</span><span style="color:#FF0000">'</span><span style="color:#000000">) </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> Nome
    </span><span style="color:#0000FF">WHERE</span><span style="color:#000000"> </span><span style="color:#FF00FF">LEN</span><span style="color:#000000">(nome) </span><span style="color:#808080">&lt;</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">30</span><span style="color:#000000">
    </span><span style="color:#0000FF">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000">
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#FF00FF">CONVERT</span><span style="color:#000000">(</span><span style="color:#0000FF">VARCHAR</span><span style="color:#000000">(</span><span style="color:#800000;font-weight:bold">4000</span><span style="color:#000000">),nome </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#FF0000">'</span><span style="color:#FF0000">Silva</span><span style="color:#FF0000">'</span><span style="color:#000000">) </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> Nome
    </span><span style="color:#0000FF">WHERE</span><span style="color:#000000"> </span><span style="color:#FF00FF">LEN</span><span style="color:#000000">(nome) </span><span style="color:#808080">&lt;</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">30</span><span style="color:#000000">
)
</span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> nome </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> Nome</span></pre>
</div>
<p>Para esse teremos (omitindo o output): <br />(39 row(s) affected)</p>
<p>E se colocarmos mais uma âncora? Vai dobrar o número de resultados, certo? Errado. Esqueceu do WHERE LEN(nome) &lt; 30?</p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:#F4F4F4;overflow:auto"><span style="color:#000000">;</span><span style="color:#0000FF">WITH</span><span style="color:#000000"> Nome(nome)
</span><span style="color:#0000FF">AS</span><span style="color:#000000">
(
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> nome </span><span style="color:#808080">=</span><span style="color:#000000"> </span><span style="color:#FF00FF">CONVERT</span><span style="color:#000000">(</span><span style="color:#0000FF">VARCHAR</span><span style="color:#000000">(</span><span style="color:#800000;font-weight:bold">4000</span><span style="color:#000000">),</span><span style="color:#FF0000">'</span><span style="color:#FF0000">Thiago</span><span style="color:#FF0000">'</span><span style="color:#000000">)
    </span><span style="color:#0000FF">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000">
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> nome </span><span style="color:#808080">=</span><span style="color:#000000"> </span><span style="color:#FF00FF">CONVERT</span><span style="color:#000000">(</span><span style="color:#0000FF">VARCHAR</span><span style="color:#000000">(</span><span style="color:#800000;font-weight:bold">4000</span><span style="color:#000000">),</span><span style="color:#FF0000">'</span><span style="color:#FF0000">José</span><span style="color:#FF0000">'</span><span style="color:#000000">)
    </span><span style="color:#0000FF">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000">
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#FF00FF">CONVERT</span><span style="color:#000000">(</span><span style="color:#0000FF">VARCHAR</span><span style="color:#000000">(</span><span style="color:#800000;font-weight:bold">4000</span><span style="color:#000000">),nome </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#FF0000">'</span><span style="color:#FF0000">Zavaschi</span><span style="color:#FF0000">'</span><span style="color:#000000">) </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> Nome
    </span><span style="color:#0000FF">WHERE</span><span style="color:#000000"> </span><span style="color:#FF00FF">LEN</span><span style="color:#000000">(nome) </span><span style="color:#808080">&lt;</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">30</span><span style="color:#000000">
    </span><span style="color:#0000FF">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000">
    </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#FF00FF">CONVERT</span><span style="color:#000000">(</span><span style="color:#0000FF">VARCHAR</span><span style="color:#000000">(</span><span style="color:#800000;font-weight:bold">4000</span><span style="color:#000000">),nome </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#FF0000">'</span><span style="color:#FF0000">Silva</span><span style="color:#FF0000">'</span><span style="color:#000000">) </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> Nome
    </span><span style="color:#0000FF">WHERE</span><span style="color:#000000"> </span><span style="color:#FF00FF">LEN</span><span style="color:#000000">(nome) </span><span style="color:#808080">&lt;</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">30</span><span style="color:#000000">
)
</span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> nome </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> Nome</span></pre>
</div>
<p>Se utilizarmos os mesmos valores para âncoras e nas partes recursivas aí sim a adição de um âncora dobraria o total atual de registros retornados.</p>
<p>--</p>
<p><strong>Limite de Recursividade</strong></p>
<p>Para este caso utilizarei um dos exemplos já mostrados:</p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:#F4F4F4;overflow:auto"><span style="color:#000000">;</span><span style="color:#0000FF">WITH</span><span style="color:#000000"> NumerosSequenciais(num)
</span><span style="color:#0000FF">AS</span><span style="color:#000000">
(
     </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000"> </span><span style="color:#0000FF">AS</span><span style="color:#000000"> num </span><span style="color:#008080">--</span><span style="color:#008080"> Este é o membro âncora, será o primeiro a ser executado. </span><span style="color:#008080">
</span><span style="color:#000000">     </span><span style="color:#0000FF">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000"> </span><span style="color:#008080">--</span><span style="color:#008080"> Fará a ligação do membro âncora com o membro recursivo.</span><span style="color:#008080">
</span><span style="color:#000000">     </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> num </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000"> </span><span style="color:#0000FF">AS</span><span style="color:#000000"> num </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> NumerosSequenciais
     </span><span style="color:#0000FF">WHERE</span><span style="color:#000000"> num </span><span style="color:#808080">&lt;</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">10</span><span style="color:#000000"> </span><span style="color:#008080">--</span><span style="color:#008080"> perceba que o WHERE é bastante importante para delimitar a parada da recursão.</span><span style="color:#008080">
</span><span style="color:#000000">)
</span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#808080">*</span><span style="color:#000000"> </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> NumerosSequenciais</span></pre>
</div>
<p>É conhecido que o resultado desta CTE será uma sequência de 1 até 10. A query a seguir retornaria o que?.</p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:#F4F4F4;overflow:auto"><span style="color:#000000">;</span><span style="color:#0000FF">WITH</span><span style="color:#000000"> NumerosSequenciais(num)
</span><span style="color:#0000FF">AS</span><span style="color:#000000">
(
     </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000"> </span><span style="color:#0000FF">AS</span><span style="color:#000000"> num </span><span style="color:#008080">--</span><span style="color:#008080"> Este é o membro âncora, será o primeiro a ser executado. </span><span style="color:#008080">
</span><span style="color:#000000">     </span><span style="color:#0000FF">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000"> </span><span style="color:#008080">--</span><span style="color:#008080"> Fará a ligação do membro âncora com o membro recursivo.</span><span style="color:#008080">
</span><span style="color:#000000">     </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> num </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000"> </span><span style="color:#0000FF">AS</span><span style="color:#000000"> num </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> NumerosSequenciais
     </span><span style="color:#0000FF">WHERE</span><span style="color:#000000"> num </span><span style="color:#808080">&lt;</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1000</span><span style="color:#000000"> </span><span style="color:#008080">--</span><span style="color:#008080"> perceba que o WHERE é bastante importante para delimitar a parada da recursão.</span><span style="color:#008080">
</span><span style="color:#000000">)
</span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#808080">*</span><span style="color:#000000"> </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> NumerosSequenciais</span></pre>
</div>
<p>O que mudou foi o nosso “critério de parada” permitindo listar até o número 1000, e é isso que vai acontecer, certo?</p>
<p>Errado. A execução será interrompida em um certo momento e teremos um output semelhante a:</p>
<p><font size="2" face="Courier New">1<br />2<br />3<br />…<br />98<br />99<br />100<br />101<br /><font color="#ff0000">Msg 530, Level 16, State 1, Line 1<br />The statement terminated. The maximum recursion 100 has been exhausted before statement completion.</font></font></p>
<p><em>“Ahhhhm, não entendi.”</em></p>
<p>O SQL Server por padrão tem um mecanismo que serve para evitar que “descuidados” larguem queries recursivas infinitas, o que pode compromoter o desempenho do ambiente. É possível colocar um outro valor até o máximo de 32767. Basta adicionar ao final da query o hint: OPTION (maxrecursion &lt;n&gt;). No nosso exemplo anterior ficaria:</p>
<div style="padding-bottom:0px;margin:0px;padding-left:0px;padding-right:0px;display:inline;float:none;padding-top:0px">
<pre style="background-color:#F4F4F4;overflow:auto"><span style="color:#000000">;</span><span style="color:#0000FF">WITH</span><span style="color:#000000"> NumerosSequenciais(num)
</span><span style="color:#0000FF">AS</span><span style="color:#000000">
(
     </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000"> </span><span style="color:#0000FF">AS</span><span style="color:#000000"> num </span><span style="color:#008080">--</span><span style="color:#008080"> Este é o membro âncora, será o primeiro a ser executado. </span><span style="color:#008080">
</span><span style="color:#000000">     </span><span style="color:#0000FF">UNION</span><span style="color:#000000"> </span><span style="color:#808080">ALL</span><span style="color:#000000"> </span><span style="color:#008080">--</span><span style="color:#008080"> Fará a ligação do membro âncora com o membro recursivo.</span><span style="color:#008080">
</span><span style="color:#000000">     </span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> num </span><span style="color:#808080">+</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1</span><span style="color:#000000"> </span><span style="color:#0000FF">AS</span><span style="color:#000000"> num </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> NumerosSequenciais
     </span><span style="color:#0000FF">WHERE</span><span style="color:#000000"> num </span><span style="color:#808080">&lt;</span><span style="color:#000000"> </span><span style="color:#800000;font-weight:bold">1000</span><span style="color:#000000"> </span><span style="color:#008080">--</span><span style="color:#008080"> perceba que o WHERE é bastante importante para delimitar a parada da recursão.</span><span style="color:#008080">
</span><span style="color:#000000">)
</span><span style="color:#0000FF">SELECT</span><span style="color:#000000"> </span><span style="color:#808080">*</span><span style="color:#000000"> </span><span style="color:#0000FF">FROM</span><span style="color:#000000"> NumerosSequenciais
</span><span style="color:#0000FF">OPTION</span><span style="color:#000000"> (maxrecursion </span><span style="color:#800000;font-weight:bold">3000</span><span style="color:#000000">)</span></pre>
</div>
<p>Ainda assim a sua recursividade passa do limite de 32767? Então avalie: </p>
<p>É REALMENTE necessária uma recursão maior que isso?</p>
<p>Não há uma maneira alternativa (e menos custosa) de executar a mesma tarefa? Normalmente há.</p>
<p>Nunca precisei na prática, mas é possível colocar “ilimitadas” recursões: basta colocar o valor do “maxrecursion” para 0.<br /><em>Mas lembro: eu, Thiago Zavaschi, não vejo isso como uma boa prática.</em></p>
<p><em>Continua no próximo post..</em></p>
</p>
</p>
</p>
</p></p>
]]></content:encoded>
			<wfw:commentRss>http://zavaschi.com/index.php/2009/09/entendendo-as-common-table-expressions-cte-parte-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Repostagem &#8211; Fun&#231;&#227;o Split no SQL Server</title>
		<link>http://zavaschi.com/index.php/2009/06/repostagem-funo-split-no-sql-server/</link>
		<comments>http://zavaschi.com/index.php/2009/06/repostagem-funo-split-no-sql-server/#comments</comments>
		<pubDate>Mon, 01 Jun 2009 22:49:00 +0000</pubDate>
		<dc:creator>Thiago Zavaschi</dc:creator>
				<category><![CDATA[Common Table Expression]]></category>
		<category><![CDATA[SQL Server 2005]]></category>
		<category><![CDATA[SQL Server 2008]]></category>
		<category><![CDATA[CTE]]></category>
		<category><![CDATA[Function]]></category>
		<category><![CDATA[Split]]></category>

		<guid isPermaLink="false">http://zavaschi.com/?p=33</guid>
		<description><![CDATA[&#160;
Então pessoal, atenção que é uma repostagem! O post sobre Common Table Expressions (CTE) e recursividade já está postado! O de CLR ainda não hehe, mas um dia cumpro todas as promessas!
Função Split no SQL Server
Uma função que sempre senti falta no SQL Server é uma função para split, aonde passaríamos dois parâmetros: 


Frase a [...]]]></description>
			<content:encoded><![CDATA[<h6>&#160;</h6>
<p>Então pessoal, atenção que é uma repostagem! O post sobre Common Table Expressions (CTE) e recursividade já está postado! O de CLR ainda não hehe, mas um dia cumpro todas as promessas!</p>
<h6>Função Split no SQL Server</h6>
<p>Uma função que sempre senti falta no SQL Server é uma função para split, aonde passaríamos dois parâmetros: </p>
<ul>
<ul>
<li>Frase a ser “splitada”. </li>
<li>Delimitador. </li>
</ul>
</ul>
<p>E teríamos uma lista de palavras originadas da frase passada, separada em cada delimitador. </p>
<p>Já desenvolvi várias versões para fazer split, mas a que considero a melhor (e também bastante popular) é a seguinte: </p>
<div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:1ab03585-99ae-4fae-bb20-c5f66b7edd1d" class="wlWriterEditableSmartContent">
<pre style="background-color:White;overflow: auto;"><span style="color: #0000FF;">CREATE</span><span style="color: #000000;"> </span><span style="color: #0000FF;">FUNCTION</span><span style="color: #000000;"> dbo.fnSplit(
    </span><span style="color: #008000;">@frase</span><span style="color: #000000;"> </span><span style="color: #0000FF;">VARCHAR</span><span style="color: #000000;">(</span><span style="color: #FF00FF;">max</span><span style="color: #000000;">)
  , </span><span style="color: #008000;">@delimitador</span><span style="color: #000000;"> </span><span style="color: #0000FF;">VARCHAR</span><span style="color: #000000;">(</span><span style="color: #FF00FF;">max</span><span style="color: #000000;">) </span><span style="color: #808080;">=</span><span style="color: #000000;"> </span><span style="color: #FF0000;">'</span><span style="color: #FF0000;">,</span><span style="color: #FF0000;">'</span><span style="color: #000000;">
) </span><span style="color: #0000FF;">RETURNS</span><span style="color: #000000;"> </span><span style="color: #008000;">@result</span><span style="color: #000000;"> </span><span style="color: #0000FF;">TABLE</span><span style="color: #000000;"> (item </span><span style="color: #0000FF;">VARCHAR</span><span style="color: #000000;">(</span><span style="color: #800000; font-weight: bold;">8000</span><span style="color: #000000;">)) 

</span><span style="color: #0000FF;">BEGIN</span><span style="color: #000000;">
</span><span style="color: #0000FF;">DECLARE</span><span style="color: #000000;"> </span><span style="color: #008000;">@parte</span><span style="color: #000000;"> </span><span style="color: #0000FF;">VARCHAR</span><span style="color: #000000;">(</span><span style="color: #800000; font-weight: bold;">8000</span><span style="color: #000000;">)
</span><span style="color: #0000FF;">WHILE</span><span style="color: #000000;"> </span><span style="color: #FF00FF;">CHARINDEX</span><span style="color: #000000;">(</span><span style="color: #008000;">@delimitador</span><span style="color: #000000;">,</span><span style="color: #008000;">@frase</span><span style="color: #000000;">,</span><span style="color: #800000; font-weight: bold;">0</span><span style="color: #000000;">) </span><span style="color: #808080;">&lt;&gt;</span><span style="color: #000000;"> </span><span style="color: #800000; font-weight: bold;">0</span><span style="color: #000000;">
</span><span style="color: #0000FF;">BEGIN</span><span style="color: #000000;">
</span><span style="color: #0000FF;">SELECT</span><span style="color: #000000;">
  </span><span style="color: #008000;">@parte</span><span style="color: #808080;">=</span><span style="color: #FF00FF;">RTRIM</span><span style="color: #000000;">(</span><span style="color: #FF00FF;">LTRIM</span><span style="color: #000000;">(
          </span><span style="color: #FF00FF;">SUBSTRING</span><span style="color: #000000;">(</span><span style="color: #008000;">@frase</span><span style="color: #000000;">,</span><span style="color: #800000; font-weight: bold;">1</span><span style="color: #000000;">,
        </span><span style="color: #FF00FF;">CHARINDEX</span><span style="color: #000000;">(</span><span style="color: #008000;">@delimitador</span><span style="color: #000000;">,</span><span style="color: #008000;">@frase</span><span style="color: #000000;">,</span><span style="color: #800000; font-weight: bold;">0</span><span style="color: #000000;">)</span><span style="color: #808080;">-</span><span style="color: #800000; font-weight: bold;">1</span><span style="color: #000000;">))),
  </span><span style="color: #008000;">@frase</span><span style="color: #808080;">=</span><span style="color: #FF00FF;">RTRIM</span><span style="color: #000000;">(</span><span style="color: #FF00FF;">LTRIM</span><span style="color: #000000;">(</span><span style="color: #FF00FF;">SUBSTRING</span><span style="color: #000000;">(</span><span style="color: #008000;">@frase</span><span style="color: #000000;">,
          </span><span style="color: #FF00FF;">CHARINDEX</span><span style="color: #000000;">(</span><span style="color: #008000;">@delimitador</span><span style="color: #000000;">,</span><span style="color: #008000;">@frase</span><span style="color: #000000;">,</span><span style="color: #800000; font-weight: bold;">0</span><span style="color: #000000;">)
        </span><span style="color: #808080;">+</span><span style="color: #000000;"> </span><span style="color: #FF00FF;">LEN</span><span style="color: #000000;">(</span><span style="color: #008000;">@delimitador</span><span style="color: #000000;">), </span><span style="color: #FF00FF;">LEN</span><span style="color: #000000;">(</span><span style="color: #008000;">@frase</span><span style="color: #000000;">))))
</span><span style="color: #0000FF;">IF</span><span style="color: #000000;"> </span><span style="color: #FF00FF;">LEN</span><span style="color: #000000;">(</span><span style="color: #008000;">@parte</span><span style="color: #000000;">) </span><span style="color: #808080;">&gt;</span><span style="color: #000000;"> </span><span style="color: #800000; font-weight: bold;">0</span><span style="color: #000000;">
  </span><span style="color: #0000FF;">INSERT</span><span style="color: #000000;"> </span><span style="color: #0000FF;">INTO</span><span style="color: #000000;"> </span><span style="color: #008000;">@result</span><span style="color: #000000;"> </span><span style="color: #0000FF;">SELECT</span><span style="color: #000000;"> </span><span style="color: #008000;">@parte</span><span style="color: #000000;">
</span><span style="color: #0000FF;">END</span><span style="color: #000000;"> 

</span><span style="color: #0000FF;">IF</span><span style="color: #000000;"> </span><span style="color: #FF00FF;">LEN</span><span style="color: #000000;">(</span><span style="color: #008000;">@frase</span><span style="color: #000000;">) </span><span style="color: #808080;">&gt;</span><span style="color: #000000;"> </span><span style="color: #800000; font-weight: bold;">0</span><span style="color: #000000;">
</span><span style="color: #0000FF;">INSERT</span><span style="color: #000000;"> </span><span style="color: #0000FF;">INTO</span><span style="color: #000000;"> </span><span style="color: #008000;">@result</span><span style="color: #000000;"> </span><span style="color: #0000FF;">SELECT</span><span style="color: #000000;"> </span><span style="color: #008000;">@frase</span><span style="color: #000000;">
</span><span style="color: #0000FF;">RETURN</span><span style="color: #000000;">
</span><span style="color: #0000FF;">END</span><span style="color: #000000;">
</span><span style="color: #0000FF;">GO</span></pre>
<p><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>Para testar é simples: </p>
<div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:a77f7edc-8e66-4e02-b664-c70e3b17ce5e" class="wlWriterEditableSmartContent">
<pre style="background-color:#F9F9F9;overflow: auto;"><span style="color: #0000FF;">SELECT</span><span style="color: #000000;"> </span><span style="color: #808080;">*</span><span style="color: #000000;"> </span><span style="color: #0000FF;">FROM</span><span style="color: #000000;"> dbo.fnSplit(</span><span style="color: #FF0000;">'</span><span style="color: #FF0000;">separar por espaço em branco</span><span style="color: #FF0000;">'</span><span style="color: #000000;">, </span><span style="color: #FF0000;">'</span><span style="color: #FF0000;"> </span><span style="color: #FF0000;">'</span><span style="color: #000000;">)</span></pre>
<p><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>---- </p>
<p>Se desejar, há outra forma, utilizando tabela temporária, sem função: </p>
<div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:45097557-44c2-47f4-b23a-0e2badb17d77" class="wlWriterEditableSmartContent">
<pre style="background-color:White;overflow: auto;"><span style="color: #0000FF;">SET</span><span style="color: #000000;"> NOCOUNT </span><span style="color: #0000FF;">ON</span><span style="color: #000000;">
</span><span style="color: #0000FF;">DECLARE</span><span style="color: #000000;"> </span><span style="color: #008000;">@ARRAY</span><span style="color: #000000;"> </span><span style="color: #0000FF;">VARCHAR</span><span style="color: #000000;">(</span><span style="color: #800000; font-weight: bold;">8000</span><span style="color: #000000;">), </span><span style="color: #008000;">@DELIMITADOR</span><span style="color: #000000;"> </span><span style="color: #0000FF;">VARCHAR</span><span style="color: #000000;">(</span><span style="color: #800000; font-weight: bold;">100</span><span style="color: #000000;">), </span><span style="color: #008000;">@S</span><span style="color: #000000;"> </span><span style="color: #0000FF;">VARCHAR</span><span style="color: #000000;">(</span><span style="color: #800000; font-weight: bold;">8000</span><span style="color: #000000;">)  

</span><span style="color: #0000FF;">SELECT</span><span style="color: #000000;"> </span><span style="color: #008000;">@ARRAY</span><span style="color: #000000;"> </span><span style="color: #808080;">=</span><span style="color: #000000;"> </span><span style="color: #FF0000;">'</span><span style="color: #FF0000;">separar por espaço em branco</span><span style="color: #FF0000;">'</span><span style="color: #000000;">
</span><span style="color: #0000FF;">SELECT</span><span style="color: #000000;"> </span><span style="color: #008000;">@DELIMITADOR</span><span style="color: #000000;"> </span><span style="color: #808080;">=</span><span style="color: #000000;"> </span><span style="color: #FF0000;">'</span><span style="color: #FF0000;"> </span><span style="color: #FF0000;">'</span><span style="color: #000000;">  

</span><span style="color: #0000FF;">IF</span><span style="color: #000000;"> </span><span style="color: #FF00FF;">LEN</span><span style="color: #000000;">(</span><span style="color: #008000;">@ARRAY</span><span style="color: #000000;">) </span><span style="color: #808080;">&gt;</span><span style="color: #000000;"> </span><span style="color: #800000; font-weight: bold;">0</span><span style="color: #000000;"> </span><span style="color: #0000FF;">SET</span><span style="color: #000000;"> </span><span style="color: #008000;">@ARRAY</span><span style="color: #000000;"> </span><span style="color: #808080;">=</span><span style="color: #000000;"> </span><span style="color: #008000;">@ARRAY</span><span style="color: #000000;"> </span><span style="color: #808080;">+</span><span style="color: #000000;"> </span><span style="color: #008000;">@DELIMITADOR</span><span style="color: #000000;">
</span><span style="color: #0000FF;">CREATE</span><span style="color: #000000;"> </span><span style="color: #0000FF;">TABLE</span><span style="color: #000000;"> #ARRAY(ITEM_ARRAY </span><span style="color: #0000FF;">VARCHAR</span><span style="color: #000000;">(</span><span style="color: #800000; font-weight: bold;">8000</span><span style="color: #000000;">))  

</span><span style="color: #0000FF;">WHILE</span><span style="color: #000000;"> </span><span style="color: #FF00FF;">LEN</span><span style="color: #000000;">(</span><span style="color: #008000;">@ARRAY</span><span style="color: #000000;">) </span><span style="color: #808080;">&gt;</span><span style="color: #000000;"> </span><span style="color: #800000; font-weight: bold;">0</span><span style="color: #000000;">
</span><span style="color: #0000FF;">BEGIN</span><span style="color: #000000;">
    </span><span style="color: #0000FF;">SELECT</span><span style="color: #000000;"> </span><span style="color: #008000;">@S</span><span style="color: #000000;"> </span><span style="color: #808080;">=</span><span style="color: #000000;"> </span><span style="color: #FF00FF;">LTRIM</span><span style="color: #000000;">(</span><span style="color: #FF00FF;">SUBSTRING</span><span style="color: #000000;">(</span><span style="color: #008000;">@ARRAY</span><span style="color: #000000;">, </span><span style="color: #800000; font-weight: bold;">1</span><span style="color: #000000;">,
    </span><span style="color: #FF00FF;">CHARINDEX</span><span style="color: #000000;">(</span><span style="color: #008000;">@DELIMITADOR</span><span style="color: #000000;">, </span><span style="color: #008000;">@ARRAY</span><span style="color: #000000;">) </span><span style="color: #808080;">-</span><span style="color: #000000;"> </span><span style="color: #800000; font-weight: bold;">1</span><span style="color: #000000;">))
    </span><span style="color: #0000FF;">INSERT</span><span style="color: #000000;"> </span><span style="color: #0000FF;">INTO</span><span style="color: #000000;"> #ARRAY (ITEM_ARRAY) </span><span style="color: #0000FF;">VALUES</span><span style="color: #000000;"> (</span><span style="color: #008000;">@S</span><span style="color: #000000;">)
    </span><span style="color: #0000FF;">SELECT</span><span style="color: #000000;"> </span><span style="color: #008000;">@ARRAY</span><span style="color: #000000;"> </span><span style="color: #808080;">=</span><span style="color: #000000;"> </span><span style="color: #FF00FF;">SUBSTRING</span><span style="color: #000000;">(</span><span style="color: #008000;">@ARRAY</span><span style="color: #000000;">,
    </span><span style="color: #FF00FF;">CHARINDEX</span><span style="color: #000000;">(</span><span style="color: #008000;">@DELIMITADOR</span><span style="color: #000000;">, </span><span style="color: #008000;">@ARRAY</span><span style="color: #000000;">) </span><span style="color: #808080;">+</span><span style="color: #000000;"> </span><span style="color: #800000; font-weight: bold;">1</span><span style="color: #000000;">, </span><span style="color: #FF00FF;">LEN</span><span style="color: #000000;">(</span><span style="color: #008000;">@ARRAY</span><span style="color: #000000;">))
</span><span style="color: #0000FF;">END</span><span style="color: #000000;">  

</span><span style="color: #008080;">--</span><span style="color: #008080;"> MOSTRANDO O RESULTADO JÁ POPULADO NA TABELA TEMPORÁRIA  </span><span style="color: #008080;">
</span><span style="color: #0000FF;">SELECT</span><span style="color: #000000;"> </span><span style="color: #808080;">*</span><span style="color: #000000;"> </span><span style="color: #0000FF;">FROM</span><span style="color: #000000;"> #ARRAY
</span><span style="color: #0000FF;">DROP</span><span style="color: #000000;"> </span><span style="color: #0000FF;">TABLE</span><span style="color: #000000;"> #ARRAY
</span><span style="color: #0000FF;">SET</span><span style="color: #000000;"> NOCOUNT </span><span style="color: #0000FF;">OFF</span></pre>
<p><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>&#160;</p>
<p>Simples não?<br />
  <br />Estas maneiras mostradas resolvem a necessidade da falta do split? Sim! </p>
<p>Porém, desde o SQL Server 2005 temos o recurso das CTEs (Common Table Expression), que também podem ser utilizadas para split. A seguir mostro o exemplo que considero mais elegante para tal tarefa, utiliza recursividade ao invés do loop: </p>
<div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:8657ef61-b854-4cb2-a53b-944ba3677ec0" class="wlWriterEditableSmartContent">
<pre style="background-color:White;overflow: auto;"><span style="color: #0000FF;">DECLARE</span><span style="color: #000000;"> </span><span style="color: #008000;">@s</span><span style="color: #000000;"> </span><span style="color: #0000FF;">VARCHAR</span><span style="color: #000000;">(</span><span style="color: #800000; font-weight: bold;">8000</span><span style="color: #000000;">), </span><span style="color: #008000;">@d</span><span style="color: #000000;"> </span><span style="color: #0000FF;">VARCHAR</span><span style="color: #000000;">(</span><span style="color: #800000; font-weight: bold;">10</span><span style="color: #000000;">)
</span><span style="color: #0000FF;">SET</span><span style="color: #000000;"> </span><span style="color: #008000;">@s</span><span style="color: #000000;"> </span><span style="color: #808080;">=</span><span style="color: #000000;"> </span><span style="color: #FF0000;">'</span><span style="color: #FF0000;">separar por espaço em branco</span><span style="color: #FF0000;">'</span><span style="color: #000000;">
</span><span style="color: #0000FF;">SET</span><span style="color: #000000;"> </span><span style="color: #008000;">@d</span><span style="color: #000000;"> </span><span style="color: #808080;">=</span><span style="color: #000000;"> </span><span style="color: #FF0000;">'</span><span style="color: #FF0000;"> </span><span style="color: #FF0000;">'</span><span style="color: #000000;"> 

;</span><span style="color: #0000FF;">WITH</span><span style="color: #000000;"> split(i,j) </span><span style="color: #0000FF;">AS</span><span style="color: #000000;">
(
</span><span style="color: #0000FF;">SELECT</span><span style="color: #000000;"> i </span><span style="color: #808080;">=</span><span style="color: #000000;"> </span><span style="color: #800000; font-weight: bold;">1</span><span style="color: #000000;">, j </span><span style="color: #808080;">=</span><span style="color: #000000;"> </span><span style="color: #FF00FF;">CHARINDEX</span><span style="color: #000000;">(</span><span style="color: #008000;">@d</span><span style="color: #000000;">, </span><span style="color: #008000;">@s</span><span style="color: #000000;"> </span><span style="color: #808080;">+</span><span style="color: #000000;"> </span><span style="color: #008000;">@d</span><span style="color: #000000;">)
</span><span style="color: #0000FF;">UNION</span><span style="color: #000000;"> </span><span style="color: #808080;">ALL</span><span style="color: #000000;">
</span><span style="color: #0000FF;">SELECT</span><span style="color: #000000;"> i </span><span style="color: #808080;">=</span><span style="color: #000000;"> j </span><span style="color: #808080;">+</span><span style="color: #000000;"> </span><span style="color: #800000; font-weight: bold;">1</span><span style="color: #000000;">, j </span><span style="color: #808080;">=</span><span style="color: #000000;"> </span><span style="color: #FF00FF;">CHARINDEX</span><span style="color: #000000;">(</span><span style="color: #008000;">@d</span><span style="color: #000000;">, </span><span style="color: #008000;">@s</span><span style="color: #000000;"> </span><span style="color: #808080;">+</span><span style="color: #000000;"> </span><span style="color: #008000;">@d</span><span style="color: #000000;">, j </span><span style="color: #808080;">+</span><span style="color: #000000;"> </span><span style="color: #800000; font-weight: bold;">1</span><span style="color: #000000;">) </span><span style="color: #0000FF;">FROM</span><span style="color: #000000;"> split
   </span><span style="color: #0000FF;">WHERE</span><span style="color: #000000;"> </span><span style="color: #FF00FF;">CHARINDEX</span><span style="color: #000000;">(</span><span style="color: #008000;">@d</span><span style="color: #000000;">, </span><span style="color: #008000;">@s</span><span style="color: #000000;"> </span><span style="color: #808080;">+</span><span style="color: #000000;"> </span><span style="color: #008000;">@d</span><span style="color: #000000;">, j </span><span style="color: #808080;">+</span><span style="color: #000000;"> </span><span style="color: #800000; font-weight: bold;">1</span><span style="color: #000000;">) </span><span style="color: #808080;">&lt;&gt;</span><span style="color: #000000;"> </span><span style="color: #800000; font-weight: bold;">0</span><span style="color: #000000;">
)
</span><span style="color: #0000FF;">SELECT</span><span style="color: #000000;"> </span><span style="color: #FF00FF;">SUBSTRING</span><span style="color: #000000;">(</span><span style="color: #008000;">@s</span><span style="color: #000000;">,i,j</span><span style="color: #808080;">-</span><span style="color: #000000;">i)
</span><span style="color: #0000FF;">FROM</span><span style="color: #000000;"> split
</span></pre>
<p><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p><strong></strong></p>
<p><strong>Observação importante sobre CTEs</strong>: </p>
<p>Ao utilizar CTEs recursivas devemos tomar cuidado com o número máximo de recursões permitidas. </p>
<p>Por padrão esse número é igual a 100. Isso na prática significa que eu só poderia ter 100 delimitadores dentro da minha variável @s. </p>
<p>Caso a recursão máxima seja alcançada, termos uma mensagem igual a essa: </p>
<p>“The statement terminated. The maximum recursion 100 has been exhausted before statement completion.”</p>
<p>Para aumentar este valor, podemos acionar um parâmetro extra para a CTE: maxrecursion. </p>
<p>Na prática, para trocar a recursão máxima para 1000, devemos fazer: </p>
<div style="padding-bottom: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; float: none; padding-top: 0px" id="scid:57F11A72-B0E5-49c7-9094-E3A15BD5B5E6:ccfe6edb-541a-48b5-bc10-409dbdb19740" class="wlWriterEditableSmartContent">
<pre style="background-color:White;overflow: auto;"><span style="color: #0000FF;">DECLARE</span><span style="color: #000000;"> </span><span style="color: #008000;">@s</span><span style="color: #000000;"> </span><span style="color: #0000FF;">VARCHAR</span><span style="color: #000000;">(</span><span style="color: #800000; font-weight: bold;">8000</span><span style="color: #000000;">), </span><span style="color: #008000;">@d</span><span style="color: #000000;"> </span><span style="color: #0000FF;">VARCHAR</span><span style="color: #000000;">(</span><span style="color: #800000; font-weight: bold;">10</span><span style="color: #000000;">)
</span><span style="color: #0000FF;">SET</span><span style="color: #000000;"> </span><span style="color: #008000;">@s</span><span style="color: #000000;"> </span><span style="color: #808080;">=</span><span style="color: #000000;"> </span><span style="color: #FF0000;">'</span><span style="color: #FF0000;">separar por espaço em branco</span><span style="color: #FF0000;">'</span><span style="color: #000000;">
</span><span style="color: #0000FF;">SET</span><span style="color: #000000;"> </span><span style="color: #008000;">@d</span><span style="color: #000000;"> </span><span style="color: #808080;">=</span><span style="color: #000000;"> </span><span style="color: #FF0000;">'</span><span style="color: #FF0000;"> </span><span style="color: #FF0000;">'</span><span style="color: #000000;"> 

;</span><span style="color: #0000FF;">WITH</span><span style="color: #000000;"> split(i,j) </span><span style="color: #0000FF;">AS</span><span style="color: #000000;">
(
</span><span style="color: #0000FF;">SELECT</span><span style="color: #000000;"> i </span><span style="color: #808080;">=</span><span style="color: #000000;"> </span><span style="color: #800000; font-weight: bold;">1</span><span style="color: #000000;">, j </span><span style="color: #808080;">=</span><span style="color: #000000;"> </span><span style="color: #FF00FF;">CHARINDEX</span><span style="color: #000000;">(</span><span style="color: #008000;">@d</span><span style="color: #000000;">, </span><span style="color: #008000;">@s</span><span style="color: #000000;"> </span><span style="color: #808080;">+</span><span style="color: #000000;"> </span><span style="color: #008000;">@d</span><span style="color: #000000;">)
</span><span style="color: #0000FF;">UNION</span><span style="color: #000000;"> </span><span style="color: #808080;">ALL</span><span style="color: #000000;">
</span><span style="color: #0000FF;">SELECT</span><span style="color: #000000;"> i </span><span style="color: #808080;">=</span><span style="color: #000000;"> j </span><span style="color: #808080;">+</span><span style="color: #000000;"> </span><span style="color: #800000; font-weight: bold;">1</span><span style="color: #000000;">, j </span><span style="color: #808080;">=</span><span style="color: #000000;"> </span><span style="color: #FF00FF;">CHARINDEX</span><span style="color: #000000;">(</span><span style="color: #008000;">@d</span><span style="color: #000000;">, </span><span style="color: #008000;">@s</span><span style="color: #000000;"> </span><span style="color: #808080;">+</span><span style="color: #000000;"> </span><span style="color: #008000;">@d</span><span style="color: #000000;">, j </span><span style="color: #808080;">+</span><span style="color: #000000;"> </span><span style="color: #800000; font-weight: bold;">1</span><span style="color: #000000;">) </span><span style="color: #0000FF;">FROM</span><span style="color: #000000;"> split
   </span><span style="color: #0000FF;">WHERE</span><span style="color: #000000;"> </span><span style="color: #FF00FF;">CHARINDEX</span><span style="color: #000000;">(</span><span style="color: #008000;">@d</span><span style="color: #000000;">, </span><span style="color: #008000;">@s</span><span style="color: #000000;"> </span><span style="color: #808080;">+</span><span style="color: #000000;"> </span><span style="color: #008000;">@d</span><span style="color: #000000;">, j </span><span style="color: #808080;">+</span><span style="color: #000000;"> </span><span style="color: #800000; font-weight: bold;">1</span><span style="color: #000000;">) </span><span style="color: #808080;">&lt;&gt;</span><span style="color: #000000;"> </span><span style="color: #800000; font-weight: bold;">0</span><span style="color: #000000;">
)
</span><span style="color: #0000FF;">SELECT</span><span style="color: #000000;"> </span><span style="color: #FF00FF;">SUBSTRING</span><span style="color: #000000;">(</span><span style="color: #008000;">@s</span><span style="color: #000000;">,i,j</span><span style="color: #808080;">-</span><span style="color: #000000;">i)
</span><span style="color: #0000FF;">FROM</span><span style="color: #000000;"> split
</span><span style="color: #0000FF;">OPTION</span><span style="color: #000000;"> (maxrecursion </span><span style="color: #800000; font-weight: bold;">1000</span><span style="color: #000000;">)</span></pre>
<p><!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>&#160;</p>
<p>O valor máximo para o parâmetro MAXRECURSION é de 32767, então fiquem atentos. Caso seja necessário uma CTE recursiva com mais de 32767 iterações, então devemos pensar numa estratégia para executar mais de um split (ou mais de uma vez a operação desejada) por iteração. Em casos muito específicos, podemos deixar o máximo de recursão infinito (MAXRECURSION = 0).</p>
<p>Não entendeu muito bem o que é uma CTE? Pode recorrer ao BOL, ou esperar o meu próximo post (há este e o de cursores para postar) que abordarei BEM detalhadamente o que é uma CTE, sintaxe, como funciona, seus usos comuns, e a questão de como funciona a recursividade. Então fiquem atentos!&#160; </p>
<p>--- </p>
<p>Depois destas três maneiras mostradas, ainda há uma quarta possibilidade (a partir do SQL Server 2005 também) que é utilizar as chamadas Functions CLR (programadas em C# por exemplo). O por quê? Porque operações em strings são custosas para o SQL Server, então uma função CLR poderia se comportar de maneira muito interessante (performática) neste caso. </p>
<p>Também não sabe o que é CLR ou como funciona? Então aguarde os próximos posts também! </p>
<p>Mas eu ainda continuo na esperança de algum dia termos isso diretamente no SQL Server, de uma maneira mais otimizada. :) </p>
<p>Abraços,<br />
  <br />Thiago Zavaschi</p>
]]></content:encoded>
			<wfw:commentRss>http://zavaschi.com/index.php/2009/06/repostagem-funo-split-no-sql-server/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
