<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- iMasters Gerador RSS 2.0 -->
<rss version="2.0">
    <channel>
        <title>iMasters - Seção: sql_server</title>
        <description>iMasters - Comunidade de profissionais, estudantes e mestres em tecnlogias e ferramentas voltadas para o desenvolvimento web</description>
        <link>http://imasters.uol.com.br/</link>
        <lastBuildDate>Wed, 10 Feb 2010 08:58:06 +0100</lastBuildDate>
        <generator>iMasters Gerador RSS 2.0</generator>
        <item>
            <title>SQL Server no Linux é possível?</title>
            <link>http://imasters.uol.com.br/artigo/15795/sql_server/sql_server_no_linux_e_possivel/</link>
            <description>&lt;p&gt;Olá, pessoal. Neste artigo
vou apresentar um estudo que tenta responder a uma das perguntas mais
freqüentes em fóruns de bancos de dados: o SQL Server pode ser
executado no Linux? Vou apresentar algumas idéias, discutir
conceitos e mostrar até onde é possível trabalhar utilizando estes dois
produtos em conjunto: o SQL Server e o Linux.&lt;/p&gt;


&lt;p&gt;Podemos imaginar um cenário onde uma
empresa adquire um software do tipo ERP que pode ser executado em
diversas plataformas, porém requer obrigatoriamente o SQL Server
como banco de dados. Se isto é ou não uma venda casada, fica o
assunto para um próximo artigo. Por enquanto vamos nos preocupar em
estudar alguma solução para este cenário onde é preciso, de
alguma maneira, evitar que a empresa compre licenças e possa
testar o seu ERP diretamente no Linux.&lt;/p&gt;


&lt;p&gt;Neste cenário, é preciso instalar a
parte servidora do SQL Server para que o software possa ser
utilizado. A parte cliente do banco de dados já está programada na
aplicação. Aliás, se alguém precisar executar a parte cliente do
SQL Server no Linux já existem alternativas. Já discuti aqui no
iMasters &lt;a href=&quot;http://imasters.uol.com.br/artigo/2048/sql_server/conectando_o_java_ao_sql_server/&quot;&gt;o
uso de um driver JDBC&lt;/a&gt; como mecanismo de conexão
ao SQL Server e também &lt;a href=&quot;http://imasters.uol.com.br/artigo/10196/sql_server/acessando_o_sql_server_no_linux/&quot;&gt;o
uso do freeTDS&lt;/a&gt;  como alternativas para a
conexão de aplicações que rodam no Linux em um servidor SQL
Server. Além disso, existem outras abordagens como o uso de ODBC que
podem ser executadas na plataforma Unix sem problemas com unixODBC.&lt;/p&gt;


&lt;p&gt;O primeiro aspecto a ser estudado
quando se fala em SQL Server no Linux é a documentação do
fabricante. De acordo com a documentação oficial do SQL Server
fornecida pela Microsoft, ele pode ser executado apenas na plataforma
Windows e em diversos servidores (Windows 98, 2000, 2003 etc.)
dependendo da versão (7, 2000, 2005, 2008, 2008 R2) e da edição
(Standard, Developer, Enterprise, MSDE etc.). 
&lt;/p&gt;


&lt;p&gt;Posto isso, podemos avaliar a primeira
alternativa e a mais viável: o uso de virtualização. Nesta técnica
é preciso utilizar no Linux um software de virtualização como o
&lt;a href=&quot;http://www.xensource.com/&quot; class=&quot;ext&quot;&gt;Xen&lt;/a&gt;
ou &lt;a href=&quot;http://www.vmware.com/br/&quot; class=&quot;ext&quot;&gt;VMWare&lt;/a&gt;,
por exemplo, e instalar o Windows e o SQL Server dentro deste
servidor virtual. Depois desta instalação é preciso configurar a
rede e pode-se utilizar o SQL Server no Linux rodando sobre um
Windows virtualizado. Para casos onde é preciso apenas realizar
alguns testes é possível utilizar as versões de avaliação do
Windows e do SQL Server, que vão expirar em um período determinado
e que podem ser úteis para testes. Contudo, estas versões de
avaliação não requerem licença e geralmente têm algum tipo de
limitação nos recursos dos software, como quantidade de memória
máxima alocada, limite de uso de processadores, etc.&lt;/p&gt;


&lt;p&gt;A próxima técnica que pode permitir a
utilização do SQL Server no Linux de forma nativa é o uso do &lt;a href=&quot;http://www.winehq.org/&quot;&gt;WINE 
(Wine is not na emulator)&lt;/a&gt;. A idéia é que este
software crie todo o ambiente para executar aplicações Windows no
Linux sem emular todo o sistema operacional. 
&lt;/p&gt;


&lt;p&gt;O primeiro passo no estudo da emulação
de alguma versão do SQL Server no Linux me levou ao &lt;a href=&quot;http://appdb.winehq.org/&quot;&gt;repositório
de aplicações no site do WINE&lt;/a&gt;, onde podemos
verificar se é possível ou não rodar certa aplicação no WINE.
Pesquisando no site, encontrei &lt;a href=&quot;http://appdb.winehq.org/objectManager.php?sClass=application&amp;amp;iId=3610&quot;&gt;esta
tentantiva de execução do SQL Server no Linux&lt;/a&gt;,
postada em março de 2009, indicando que não seria possível
executar o SQL Server MSDE SP3 no WINE. A descrição do erro diz
respeito a um problema com a instalação do serviço. Resolvi
arregaçar as mangas e fazer eu mesmo um teste com o WINE.&lt;/p&gt;


&lt;p&gt;Depois de instalar o &lt;a href=&quot;http://www.vivaolinux.com.br/artigo/Instalando-e-configurando-o-Wine/&quot;&gt;WINE&lt;/a&gt; 
em um Linux Ubutu 8.04, chamado ubutu01, eu peguei os arquivos
binários, os arquivos de dados e as entradas do registro (o &lt;em&gt;registry&lt;/em&gt;
do Windows) de uma instância padrão do SQL Server MSDE SP3
instalado previamente em um Windows XP e joguei tudo no Linux que
continha o WINE, junto com a aplicação das chaves no registro. Além
disso, também configurei o WINE utilizando o poderoso &lt;a href=&quot;http://von-thadden.de/Joachim/WineTools/&quot; class=&quot;ext&quot;&gt;WineTools&lt;/a&gt;
para instalar os componentes necessários no Linux: DCOM, MDAC, IE6,
.NET framework, Windows Commom Controls e outros. Tudo ocorreu sem
problemas até este ponto.&lt;/p&gt;


&lt;p&gt;Apesar de não seguir a instalação do
produto como o recomendado, eu iniciei o serviço do SQL Server MSDE
no modo console utilizando o arquivo sqlservr.exe com o parâmetro -c
dentre outros. Contudo, infelizmente não consegui executar o serviço
com sucesso, como visto na Figura 1:&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;Figura 1. tentativa de executar o sql server 2000 msde pelo wine&quot; src=&quot;http://conteudo.imasters.uol.com.br/15795/Figura1.png&quot; /&gt;&lt;/p&gt;


&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;Figura 1: Tentativa de executar o SQL Server 2000
MSDE pelo WINE&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;Pelo log de erros do SQL Server,
mostrado na Figura 1, podemos ver dois erros: um problema de
autenticação do NTLMSP, responsável pela comunicação e
autenticação com o Windows, e um com a biblioteca de rede SSNETLIB,
que foi o motivo pelo qual o serviço não iniciou completamente.
Pesquisando um pouco, descobri que a biblioteca &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/aa174500%28SQL.80%29.aspx&quot; class=&quot;ext&quot;&gt;SSNETLIB.dll&lt;/a&gt; é um dos componentes do SQL Server que fazem parte das bibliotecas
de rede do servidor e que fazem chamadas diretas à API do sistema
operacional Windows Sockets 2, entre outras. Tentei de tudo:
registrar a DLL, instalar outras versões do SQL Server, modificar as
bibliotecas de redes pelas chaves do registro, mas não consegui
passar deste ponto. 
&lt;/p&gt;


&lt;p&gt;O curioso é que mesmo com iniciativas
da Microsoft para interoperabilidade, como a criação de &lt;a href=&quot;http://www.microsoft.com/latam/presspass/brasil/2009/maio/interop.mspx&quot;&gt;vários
laboratórios de interoperabilidades&lt;/a&gt;
específicos para isso, parece que não há nenhum interesse na
portabilidade do SQL Server. Há, inclusive, até &lt;a href=&quot;http://www.sqlservercentral.com/articles/The+Lighter+Side/sqlserveronlinux/1814/&quot;&gt;piadinhas
de primeiro de abril&lt;/a&gt; falando sobre uma suposta
versão do SQL Server para Linux utilizando a biblioteca Mono.&lt;/p&gt;


&lt;p&gt;Além disso a comunidade de código
livre também não apresentou muito interesse nesta linha de
interoperabilidade, apesar de já existirem casos onde diversos jogos
e softwares do Office são executados no WINE. Caso alguém
se interesse por continuar o esforço, basta colocar um comentário
no final do artigo. 
&lt;/p&gt;


&lt;p&gt;Mas o estudo não acaba aqui. Vou
comentar algumas outras abordagens que talvez possam ser interessantes
para aqueles que desejam, de alguma, forma migrar do SQL Server para
outro banco de dados. Algumas destas abordagens requerem modificação
na aplicação no banco de dados utilizado e talvez possam não ser
adequadas ao cenário descrito no começo do artigo.&lt;/p&gt;


&lt;p&gt;Uma das alternativas interessantes é a
utilização de um banco de dados que possa emular o comportamento do
SQL Server de forma adequada, mais especificamente o comportamento da
&lt;a href=&quot;http://en.wikipedia.org/wiki/Transact-SQL&quot;&gt;linguagem
T-SQL&lt;/a&gt;, o dialeto do SQL utilizado SQL Server.
Neste ponto podemos pensar no Access e também na utilização do
arquivo .mdf de dados do SQL Server diretamente acoplado à aplicação
no que é conhecido por &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/bb264564%28SQL.90%29.aspx&quot;&gt;SQL
Server 2005 Express Edition Edition User Instances&lt;/a&gt; 
ou pelo acrônimo RANU (Run As Normal User). Para acoplar
diretamente a instalação do SQL Server a um software, eu recomendo
este &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/bb264562%28SQL.90%29.aspx&quot;&gt;link
que explica os detalhes deste acoplamento&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;Uma outra abordagem interessante é a
utilização de outro banco de dados que possua alguma
compatibilidade com o T-SQL. Existem diversas alternativas que podem
ser mais adequadas, porém esperava encontrar algo como o &lt;a href=&quot;http://www.janus-software.com/fb_fyracle.html&quot; class=&quot;ext&quot;&gt;Fyracle&lt;/a&gt;,
que é o uso do Firebird com um módulo próprio para compatibilidade
com o Oracle. O mais próximo disso que encontrei foi o
&lt;a href=&quot;http://www.firebirdsql.org/dotnetfirebird/&quot; class=&quot;ext&quot;&gt;DotNetFirebird&lt;/a&gt;
que possui muitas similaridades com o Access e o MSDE. Outros bancos
acopláveis que talvez possuam uma compatibilidade maior com o SQL
Server podem ser o &lt;a href=&quot;http://www.sqlite.org/&quot; class=&quot;ext&quot;&gt;SQLLite&lt;/a&gt;
e o &lt;a href=&quot;http://www.codeplex.com/sharphsql&quot; class=&quot;ext&quot;&gt;Sharphsql&lt;/a&gt;.&lt;/p&gt;


&lt;p&gt;Outro caminho interessante seria a
conversão em tempo real das instruções SQL enviadas pela aplicação
para um outro banco de dados. Algo como o MySQL Proxy, que já
discuti em &lt;a href=&quot;http://imasters.uol.com.br/artigo/11161/mysql/balanceamento_de_carga_no_mysql_parte_1/&quot;&gt;um
artigo anterior&lt;/a&gt;, e que possui a capacidade de
captar, interpretar e modificar em tempo real as instruções
enviadas pela aplicação antes delas chegarem ao banco de dados.
Contudo, não encontrei nenhum software ou projeto que não precise
modificar o código fonte e que faça esta tradução de instruções
em tempo real. Aqui temos uma boa oportunidade para um novo projeto
que talvez tenha algum mercado. Alguém se candidata?&lt;/p&gt;


&lt;p&gt;Se observarmos o ponto de vista de
conversão, podemos encontrar uma infinidade de ferramentas,
aplicações, middlewares e softwares que permitem a conversão de um
banco de dados para outro, com destaque especial para o MySQL e o
PostgreSQL, considerados os principais bancos de dados de código
livre. Estas ferramentas transformam os objetos e os dados de um banco
para outro de forma &lt;em&gt;off-line&lt;/em&gt;, ou seja, é preciso converter
e trocar um banco de dados por outro. Esta opção é recomendada, mas
aqui vou destacar algumas ferramentas que podem auxiliar apenas na
migração das instruções.&lt;/p&gt;


&lt;p&gt;Uma das várias ferramentas gráficas
existentes que podem auxiliar na conversão das instruções entre
diferentes bancos de dados é o &lt;a href=&quot;http://www.swissql.com/products/sql-translator/sql-converter.html&quot;&gt;SwissSQL
Console&lt;/a&gt;, cuja interface é mostrada na Figura
2:&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;Figura 2. interface gráfica do swisssql console.&quot; src=&quot;http://conteudo.imasters.uol.com.br/15795/Figura2.png&quot; /&gt;&lt;/p&gt;


&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;Figura 2: Interface gráfica do
SwissSQL Console&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;Notem que a interface da Figura 2
apresenta dois painéis para a digitação das instruções e que no
segundo painel, mais abaixo, existem diversas abas com os nomes de
alguns bancos de dados. Na parte inferior é mostrado o resultado
também com as diversas abas para os bancos de dados. Esta ferramenta
tem o potencial de auxiliar muito os desenvolvedores que desejam
converter instruções, seja para mudar o banco de dados ou para
criar uma aplicação que suporte diferentes bancos de dados
(multi-banco), assim como um potencial enorme para ensinar a novos
desenvolvedores a diferenças entre os dialetos da linguagem SQL.&lt;/p&gt;


&lt;p&gt;Uma alternativa ao SwissSQL Console de
código livre e voltada para o Linux é o conjunto de scripts chamado
&lt;a href=&quot;http://search.cpan.org/~ribasushi/SQL-Translator-0.11003/lib/SQL/Translator/Manual.pod&quot;&gt;SQL
Translator&lt;/a&gt;. Esta ferramenta permite converter
todos os comandos de um arquivo .sql diretamente pela linha de
comando, além de converter o esquema do banco de dados e gerar
código automaticamente, entre outras funcionalidades.&lt;/p&gt;


&lt;p&gt;Do ponto de vista de aplicação, é
comum encontrar uma camada a mais de software que encapsula os
detalhes da instrução SQL e da interação da aplicação com o
banco de dados. Existem diversos mecanismos, APIs, frameworks,
aplicações, bibliotecas e outros recursos que separam o
desenvolvedor da instrução que é enviada para o banco de dados
como objetivo de criar aplicações que não dependam diretamente de
um banco de dados específico. Esta prática é muito comum,
principalmente quando se fala em aplicações web que utilizem
mecanismo de mapeamento e persistência de objetos e que muitas vezes
nem utilizam um banco de dados propriamente dito. Já existe,
inclusive, alguns design patterns que facilitam a implementação
deste tipo de camada de software.&lt;/p&gt;


&lt;p&gt;Sem entrar em uma discussão mais
profunda do uso de camadas de software para separar o acesso aos
dados, eu cito o &lt;a href=&quot;http://www.swissql.com/products/sqlone-apijava/sqlone-apijava.html&quot;&gt;SwissSQL
API para Java&lt;/a&gt; como uma API que permite este
tipo de programação multi-banco. Porém, existem várias soluções
que seguem esta linha de raciocínio.&lt;/p&gt;


&lt;p&gt;Com isso termino aqui este pequeno
estudo que mostrou algumas abordagens e idéias para a integração
do SQL Server no Linux. Apesar de existirem diversas alternativas,
vale a pena lembrar que os fatores técnicos são apenas um dos
aspectos a serem considerados, pois deve-se levar em conta questões
como proficiência no banco de dados, acordo de licença, política,
idealismo, filosofia, regras e outros fatores que acabam norteando as
escolhas das ferramentas e recursos técnicos utilizados no que diz
respeito a banco de dados.&lt;/p&gt;


&lt;p&gt; Um grande abraço, pessoal, e até
a próxima. 
&lt;/p&gt;

</description>
            <author>pichiliani@gmail.com (Mauro Pichiliani)</author>
            <pubDate>Mon, 08 Feb 2010 11:15:00 +0100</pubDate>
            <guid>http://imasters.uol.com.br/artigo/15795</guid>
        </item>
        <item>
            <title>Usando dados espaciais no SQL Server 2008 - Parte 02</title>
            <link>http://imasters.uol.com.br/artigo/15748/sql_server/usando_dados_espaciais_no_sql_server_2008_parte_02/</link>
            <description>&lt;p&gt;No
&lt;a href=&quot;http://imasters.uol.com.br/artigo/15684/sql_server/usando_dados_espaciais_no_sql_server_2008_parte_01/&quot;&gt;artigo anterior&lt;/a&gt; falamos da importância dos dados espaciais em
nossas aplicações, porque e quando devemos utilizar, o que é o
plano geométrico e suas dimensões, qual diferença dos tipos de
dados geometry
e geography
os quais são novos tipos de dados que estão presente no SQL Server
2008 e por último vimos quais são os tipos
específicos para cada tipo de dados existente.&lt;/p&gt;

&lt;p&gt;Ao
termos conhecido as diferenças dentre os tipos de dados geometry e
geography o que nos resta neste momento é conhecermos quais são os
diferentes formatos que podem ser utilizados para representar os
dados espaciais, existe exatamente três:&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;
1.     
&lt;a href=&quot;http://edndoc.esri.com/arcsde/9.0/general_topics/wkb_representation.htm&quot;&gt;The
Well-Known-Binary (WKB) format&lt;/a&gt;
&lt;/li&gt;

&lt;li&gt;
2.     
&lt;a href=&quot;http://en.wikipedia.org/wiki/Well-known_text&quot;&gt;The
Well-Known-Text (WKT) format&lt;/a&gt;
&lt;/li&gt;

&lt;li&gt;
3.     
&lt;a href=&quot;http://en.wikipedia.org/wiki/Geography_Markup_Language&quot;&gt;The
Geography Markup Language (GML) data format.&lt;/a&gt;&lt;/li&gt;

&lt;/ol&gt;&lt;p&gt;Todos
os formatos mostrado acima foram publicados pela Open Geospatial
Consortium (OGC), o qual é lider dos padrões geoespaciais e serviços
baseados em localizações.&lt;/p&gt;

&lt;ol&gt;&lt;li&gt;
1.     
&lt;a href=&quot;http://edndoc.esri.com/arcsde/9.0/general_topics/wkb_representation.htm&quot;&gt;The
Well-Known-Binary (WKB) format&lt;/a&gt;
: Este é o formato binário pelo qual representa a instância do
tipo de dados geography, é um dos formatos  preferidos serializados
das aplicações que necessitam armazenar informações geoespacial
em compactação.&lt;/li&gt;

&lt;li&gt;
2.     
&lt;a href=&quot;http://en.wikipedia.org/wiki/Well-known_text&quot;&gt;The
Well-Known-Text (WKT) format&lt;/a&gt;:
Este formato é compacto, ou melhor nos garante uma leitura fácil,
isto porque é um formato amigável e o mais comum para se usar em
consultas geoespaciais.&lt;/li&gt;

&lt;li&gt;
3.     
&lt;a href=&quot;http://en.wikipedia.org/wiki/Geography_Markup_Language&quot;&gt;The
Geography Markup Language (GML) data format:&lt;/a&gt;
Este formato representa o padrão (XML), o qual é a melhor
alternativa para incluir informações geoespaciais em documentos
XML. Este formato é útil em aplicações onde é necessário
alterar informações geoespaciais por meios de XML Web Service.&lt;/li&gt;

&lt;/ol&gt;&lt;p&gt;Os
padrões, conforme foi ilustrado acima, nos oferecem vastas vantagens, o
que precisa ser analisado é: qual deles se comportaria de melhor
forma em nossas aplicações. Os dados espaciais também suportam
algumas funções, atualmente existem mais de 60 nos tipo de dados
geometry 
e geography.&lt;/p&gt;

&lt;h4&gt;STDifference
&lt;/h4&gt;

&lt;p&gt;Retorna uma nova instância
consistindo em pontos da instância base em que não contém pontos
da instância parâmetro&lt;/p&gt;

&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;-- Results: POLYGON ((10 10, 40 10, 40 26.666666666666668, 20 20, 26.666666666666668 40, 10 40, 10 10))&lt;br /&gt;&lt;br /&gt;DECLARE @g geometry&lt;br /&gt;&lt;br /&gt;='POLYGON((10 10, 40 10, 40 40, 10 40, 10 10))'&lt;br /&gt;&lt;br /&gt;DECLARE @h geometry&lt;br /&gt;&lt;br /&gt;='POLYGON((20 20, 50 30, 50 50, 30 50, 20 20))'&lt;br /&gt;&lt;br /&gt;SELECT @G.STDifference(@H).ToString(); &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15748/1.JPG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;Figura
1 - Function - STDifference&lt;/span&gt;&lt;/p&gt;

&lt;h4&gt;STIntersects&lt;/h4&gt;

&lt;p&gt;Retorna
verdadeiro se todos os valores geometric
possuírem mais de uma intersecção, por outro lado este método irá
retorna NULL
se todos SRID
da instância não possuírem o valor adequado ao SRID da outra
instância ou melhor. Este método tem por objetivo conter apenas os
pontos comuns entre a instância base e a instância parâmetro.&lt;/p&gt;

&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;-- Results: POLYGON ((30 30, 40 30, 40 40, 30 40, 30 30))&lt;br /&gt;&lt;br /&gt;DECLARE @g geometry&lt;br /&gt;&lt;br /&gt;='POLYGON((10 10, 40 10, 40 40, 10 40, 10 10))'&lt;br /&gt;&lt;br /&gt;DECLARE @h geometry&lt;br /&gt;&lt;br /&gt;='POLYGON((30 30, 50 30, 50 50, 30 50, 30 30))'&lt;br /&gt;&lt;br /&gt;SELECT @g.STIntersection(@h).ToString();&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15748/2.JPG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;Figura 2 - Function - STIntersects&lt;/span&gt;&lt;/p&gt;

&lt;h4&gt;STSymDifference&lt;/h4&gt;

&lt;p&gt;Retorna
uma nova instância contendo apenas os pontos únicos da instância
base e parâmetro  ( i.e., excluindo os pontos que retornam
STIntersection() ).&lt;/p&gt;

&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;-- Results: MULTIPOLYGON (((50 30, 50 50, 30 50, 40 40, 50 30)),&lt;br /&gt;&lt;br /&gt;((10 10, 40 10, 40 40, 10 40, 10 10)))&lt;br /&gt;DECLARE @g geometry&lt;br /&gt;='POLYGON((10 10, 40 10, 40 40, 10 40, 10 10))'&lt;br /&gt;&lt;br /&gt;DECLARE @h geometry&lt;br /&gt;&lt;br /&gt;='POLYGON((40 40, 50 30, 50 50, 30 50, 40 40))'&lt;br /&gt;&lt;br /&gt;SELECT @g.STSymDifference(@h).ToString();&lt;br /&gt;&lt;br /&gt;Figura 1.2 (Function - STSysmDifference)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15748/3.JPG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;Figura 3 - Fucntion - STSysmDifference&lt;/span&gt;&lt;/p&gt;

&lt;h4&gt;STUnion&lt;/h4&gt;

&lt;p&gt;Retorna
uma nova instância contendo todo os pontos aninhados da instância
base e parâmetro.&lt;/p&gt;

&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;-- Results: MULTIPOLYGON (((50 30, 50 50, 30 50, 40 40, 50 30)),&lt;br /&gt;&lt;br /&gt;((40 10, 40 40, 10 40, 20 20, 40 10)))&lt;br /&gt;&lt;br /&gt;DECLARE @g geometry&lt;br /&gt;&lt;br /&gt;='POLYGON((20 20, 40 10, 40 40, 10 40, 20 20))'&lt;br /&gt;&lt;br /&gt;DECLARE @h geometry&lt;br /&gt;&lt;br /&gt;='POLYGON((40 40, 50 30, 50 50, 30 50, 40 40))'&lt;br /&gt;&lt;br /&gt;SELECT @g.STUnion(@h).ToString();&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15748/4.JPG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;Figura 4 - Function - STUnion&lt;/span&gt;&lt;/p&gt;

&lt;h4&gt;Blended
Types&lt;/h4&gt;

&lt;p&gt;Este método descreve não
justamente os polígonos, no entanto é possível usar diferentes
tipos específicos em cada coleção ou em cada tipo de definido.&lt;/p&gt;

&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;-- Results: MULTILINESTRING ((40 40, 30 30), (25 25, 8 8))&lt;br /&gt;&lt;br /&gt;DECLARE @g geometry='LINESTRING(8 8, 40 40)'&lt;br /&gt;&lt;br /&gt;DECLARE @h geometry='POLYGON((25 25, 15 30, 30 30, 30 15, 25 25))'&lt;br /&gt;&lt;br /&gt;SELECT @g.STDifference(@h).ToString();&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15748/5.JPG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;Figura 4 - Function  Blended Types&lt;/span&gt;&lt;/p&gt;

&lt;h4&gt;STArea,
STLength&lt;/h4&gt;

&lt;p&gt;Retorna a soma de todas
as áreas calculadas na superfície definida pelo usuário do tipo
geometric.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;STArea()
retorna o tipo float indicando a área quadrangular da instância
&lt;/li&gt;

&lt;li&gt;STLength()
retorna o tipo float indicando o tamanho de unidade da instância (0
ou se a instância é um ponto ou se não possui tamanho)&lt;/li&gt;

&lt;/ul&gt;&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;-- Results: 400 | 121.28990204492&lt;br /&gt;DECLARE @g GEOMETRY='POLYGON((10 10, 30 50, 50 50, 10 10))'&lt;br /&gt;&lt;br /&gt;SELECT @g.STArea(), @g.STLength()&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15748/6.JPG&quot; /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;Figura 5 - Function  STArea, STLength&lt;/span&gt;&lt;/p&gt;

&lt;h4&gt;STCentroid&lt;/h4&gt;

&lt;p&gt;Este
é um dos métodos padrões OGC que retorna 'Point'
indicando o centro da forma quandrangular, se a instância adotada
não for Polygon ou MultiPolygon o valor NULL será retornado.&lt;/p&gt;

&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;-- Results: POINT (20 30)&lt;br /&gt;DECLARE @g GEOMETRY='POLYGON((10 10, 10 40, 40 40, 10 10))'&lt;br /&gt;SELECT @g.STCentroid().ToString()&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15748/7.JPG&quot; /&gt;&lt;br /&gt;&lt;span class=&quot;c4&quot;&gt;Figura 6 - Function - STCentroid&lt;/span&gt;&lt;/p&gt;

&lt;h4&gt;STWithin,
STContains&lt;/h4&gt;

&lt;p&gt;Dois métodos que
seguem também o padrão OGC retornando 1 ou 0 e indica se todos
'Points'
da instância existe totalmente ao lado de outra
instância.&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;STContains()
testa se o parâmetro instância
está do lado da instância base &lt;/li&gt;

&lt;li&gt;STWithin()
testa se a instância base está do
lado da instância parâmetro, retorna verdadeiro se dado o valor
geometric
estiver juntamente á outro, de outra forma, retorna falso. Este
método irá retorna NULL
se todos SRID da instância não possuir o valor adequado ao SRID da
outra instância.&lt;/li&gt;

&lt;/ul&gt;&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;-- Results: 1 0&lt;br /&gt;&lt;br /&gt;-- 0 1&lt;br /&gt;&lt;br /&gt;DECLARE @g geometry='POLYGON ((10 10, 13 30, 30 30, 30 15,10 10))'&lt;br /&gt;&lt;br /&gt;DECLARE @h geometry='LINESTRING (16 16, 16 24, 25 18)'&lt;br /&gt;&lt;br /&gt;SELECT @g.STContains(@h), @g.STWithin(@h)&lt;br /&gt;&lt;br /&gt;SELECT @h.STContains(@g), @h.STWithin(@g)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15748/8.JPG&quot; /&gt;&lt;br /&gt;&lt;span class=&quot;c4&quot;&gt;Figura 7 - Function - STContains&lt;/span&gt;&lt;/p&gt;

&lt;h4&gt;STPointOnSurface&lt;/h4&gt;

&lt;p&gt;Este
método irá retornar de certa forma um ponto aleatório que nos
garante a localização dentro da instância base.&lt;/p&gt;

&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;-- Results: POINT (23 25)&lt;br /&gt;DECLARE @g geometry='POLYGON((10 10, 14 15, 50 12, 45 30,10 30, 10 10))'&lt;br /&gt;&lt;br /&gt;SELECT @g.STPointOnSurface().ToString()&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15748/9.JPG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;Figura 8 - Function - STPointOnSurface&lt;/span&gt;&lt;/p&gt;

&lt;h4&gt;STGeometryType
&lt;/h4&gt;

&lt;p&gt;Retorna o tipo Open Geospatial Consortium (OGC) o qual representa
a instância geometry.&lt;/p&gt;

&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;-- Results: Polygon&lt;br /&gt;&lt;br /&gt;DECLARE @g geometry;&lt;br /&gt;&lt;br /&gt;SET @g =geometry::STGeomFromText('POLYGON((0 0, 3 0, 3 3, 0 3, 0 0))', 0);&lt;br /&gt;&lt;br /&gt;SELECT @g.STGeometryType();&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;STGeomFromText&lt;/h4&gt;

&lt;p&gt;Retorna
uma instância geometry
da representação Open Geospatial Consortium (OGC) Well-Known
Text(WKT) representando assim o argumento de elevações (Z) e
medidas (M).&lt;/p&gt;

&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;-- Results: LINESTRING (100 100, 20 180, 180 180)&lt;br /&gt;&lt;br /&gt;DECLARE @g geometry;&lt;br /&gt;&lt;br /&gt;SET @g =geometry::ST&lt;br /&gt;&lt;br /&gt;GeomFromText('LINESTRING (100 100, 20 180, 180 180)', 0);&lt;br /&gt;&lt;br /&gt;SELECT @g.ToString();&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;STTouches
&lt;/h4&gt;

&lt;p&gt;Retorna verdadeiro se dado o valor geometric
faz parte de outro; de outra forma, retorna falso. Este método
retorna null se todos SRID (Spatial
Reference Identifier) da instância
não possuírem o valor adequado SRID da outra instância.&lt;/p&gt;

&lt;h4&gt;STDistance&lt;/h4&gt;

&lt;p&gt;Retorna
a distância entre os valores do tipo específico Point
geometric.
Este método irá retorna null se SRID (Spatial
Reference Identifier) da instância
não possuir o valor adequado ao SRID da outra instância.&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15748/10.JPG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;Figura
1.9  - Geography Methods&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Aos
analisarmos algumas funções e formatos existentes nos dados
espaciais, o que nos resta agora é apenas aplicá-los. Os tipos
específicos podem ser usados em definições de tabela e tipos
variáveis. Dependendo de sua manipulação dos dados espaciais, sendo
plano ou elíptico, pode-se usar os  dois tipo de dados, geometry e
geography.&lt;/p&gt;

&lt;h4&gt;Exemplo
prático:&lt;/h4&gt;

&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;USEmaster&lt;br /&gt;&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;IFEXISTS(SELECT Name FROMsys.databasesWHERE name =N'SpatialDatabase')&lt;br /&gt;&lt;br /&gt;DROPDATABASE SpatialDatabase&lt;br /&gt;&lt;br /&gt;CREATEDATABASE SpatialDatabase&lt;br /&gt;&lt;br /&gt;ONPRIMARY&lt;br /&gt;&lt;br /&gt;(NAME = SpatialDatabase_data,&lt;br /&gt;&lt;br /&gt;FILENAME='C:&amp;#92;DatabaseSpatial&amp;#92;SpatialDatabase.mdf'),&lt;br /&gt;&lt;br /&gt;FILEGROUP SpatialGroup1&lt;br /&gt;&lt;br /&gt;(NAME = SpatialGroup2,&lt;br /&gt;&lt;br /&gt;FILENAME='C:&amp;#92;DatabaseSpatial&amp;#92;SpatialDatabase.ndf')&lt;br /&gt;&lt;br /&gt;LOGON&lt;br /&gt;&lt;br /&gt;(NAME = Spatial_log,&lt;br /&gt;&lt;br /&gt;FILENAME='C:&amp;#92;DatabaseSpatial&amp;#92;SpatialDatabase.ldf')&lt;br /&gt;&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;USE SpatialDatabase;&lt;br /&gt;&lt;br /&gt;CREATETABLE #GeometrySpatial&lt;br /&gt;&lt;br /&gt;(&lt;br /&gt;&lt;br /&gt;LocationID INTPRIMARYKEYCLUSTEREDNOTNULL,&lt;br /&gt;&lt;br /&gt;LocationName NVARCHAR(30),&lt;br /&gt;&lt;br /&gt;Position GEOGRAPHY&lt;br /&gt;&lt;br /&gt;);&lt;br /&gt;&lt;br /&gt;INSERTINTO #GeometrySpatial(LocationID,LocationName,Position)&lt;br /&gt;&lt;br /&gt;VALUES (1,'Nova York',geography::Parse('POLYGON((&lt;br /&gt;&lt;br /&gt;-75.17031 39.95601, -75.16786 39.95778, -75.17921 39.96874,&lt;br /&gt;&lt;br /&gt;-75.18441 39.96512, -75.17031 39.95601 ))'))&lt;br /&gt;&lt;br /&gt;INSERTINTO #GeometrySpatial(LocationID,LocationName,Position)&lt;br /&gt;&lt;br /&gt;VALUES (2,'Chicago',geography::Parse('POLYGON((&lt;br /&gt;&lt;br /&gt;-75.17031 39.95601, -75.16786 39.95778, -75.18870 39.97789, -75.18521 39.99237,&lt;br /&gt;&lt;br /&gt;-75.18603 40.00677, -75.19922 40.01136, -75.21746 40.03142, -75.22534 40.02586,&lt;br /&gt;&lt;br /&gt;-75.21052 40.01430, -75.19192 40.00634, -75.19248 39.99570, -75.20526 39.98374,&lt;br /&gt;&lt;br /&gt;-75.19437 39.97704, -75.19087 39.96920, -75.17031 39.95601))'))&lt;br /&gt;&lt;br /&gt;INSERTINTO #GeometrySpatial(LocationID,LocationName,Position)&lt;br /&gt;&lt;br /&gt;VALUES (3,'Miami',geography::Parse('POLYGON((&lt;br /&gt;&lt;br /&gt;-75.22280 40.02387, -75.21442 40.02810, -75.21746 40.03142,&lt;br /&gt;&lt;br /&gt;-75.22534 40.02586, -75.22280 40.02387))'))&lt;br /&gt;&lt;br /&gt;-- Trazendo os valores de forma compreensivel.&lt;br /&gt;&lt;br /&gt;SELECT&lt;br /&gt;&lt;br /&gt;LocationID,LocationName,Position.ToString()as WKT,&lt;br /&gt;&lt;br /&gt;Position.STLength()as longitude&lt;br /&gt;&lt;br /&gt;FROM #GeometrySpatial&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15748/11.JPG&quot; /&gt; &lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;Figura 10 - Results  measures and longitude&lt;/span&gt;&lt;/p&gt;

&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt; -- Visualizando o Spatial Results&lt;br /&gt;SELECT * FROM #GeometrySpatial&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15748/13.JPG&quot; /&gt; &lt;/p&gt;

&lt;p&gt;Figura 11 - Spatial Results - Map&lt;/p&gt;

&lt;p&gt;OBS:
Se por acaso quisermos obter a lista de todos SRIDs suportados pelo
SQL Server, podemos executar a instrução:&lt;/p&gt;

&lt;p&gt;SELECT
* FROM
sys.spatial_reference_systems&lt;/p&gt;

&lt;p&gt;Para mais informações relacionado aos SRIDs, visite:
&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/bb964707.aspx&quot; class=&quot;ext&quot;&gt;http://msdn.microsoft.com/en-us/library/bb964707.aspx&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15748/14.JPG&quot; /&gt;&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/bb964707.aspx&quot; class=&quot;ext&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;Figura
12 - Results - SRIDs&lt;/span&gt;&lt;/p&gt;

&lt;h4&gt;Conclusão&lt;/h4&gt;

&lt;p&gt;Ótimo,
agora que conhecemos o que são os dados espacias, quando devemos
utilizar, quais são as diferença dentre os tipos de dados geometry
e geography, quais são os formatos e tipos existentes suportados
pelo SQL Server e o exemplo prático, o que resta neste momento é
apenas implementá-los em nossa aplicação e, assim, termos a
convicção de que possuímos uma aplicação que tem ótimas
vantagens quando o assunto se relata em buscas (mundialmente).&lt;/p&gt;

</description>
            <author>lucassouzace@hotmail.com (Lucas Souza)</author>
            <pubDate>Mon, 01 Feb 2010 09:30:00 +0100</pubDate>
            <guid>http://imasters.uol.com.br/artigo/15748</guid>
        </item>
        <item>
            <title>Usando dados espaciais no SQL Server 2008 - Parte 01</title>
            <link>http://imasters.uol.com.br/artigo/15684/sql_server/usando_dados_espaciais_no_sql_server_2008_parte_01/</link>
            <description>&lt;p&gt;Além
de nos fornecer diversas opções relacionadas à integração, relatório e replicação de dados, políticas e manutenção de planos, o SQL Server agora também nos dá acesso a dados espaciais, na versão 2008. Os dados espaciais são
implementados por 'CLR Types' internos no banco de dados, e têm por objetivo armazenar informações, representando posições e estruturas geométrica no SQL Server. Com
isso  em mente, podemos agora desenvolver aplicações que nos concedam
distância, localização e espaço em um certo ponto, localizado em
uma certa circunstância; podendo, além disso, virem até de aplicações
que utilizem GPS.&lt;/p&gt;


&lt;h4&gt;Por que
e quando devemos utilizar os Dados Espaciais?&lt;/h4&gt;


&lt;p&gt;Quando temos aplicações que disponibilizam produtos
mundialmente, alguns dos grandes aspectos que podem influenciar ao
utilizar os dados espaciais são:&lt;/p&gt;


&lt;p&gt;
01. Onde o produto será entregue?&lt;/p&gt;


&lt;p&gt;
02. Onde o produto se encontra neste momento?&lt;/p&gt;


&lt;p&gt;
03. Por quais cidades já passou?&lt;/p&gt;


&lt;p&gt;Quando
precisamos trabalhar com os dados espaciais, temos que ter em mente
alguns requisitos, por exemplo: 
&lt;/p&gt;


&lt;p&gt;
01.   Os tipos de dados de espaciais podem ser usados para além
de coordenadas, consistindo assim em mistura de pontos, linhas, um
único segmento ou multi-segmento.&lt;/p&gt;


&lt;p&gt;
02.  Habilidade de executar grandes massas de operações de
dados geoespaciais, por exemplo: aplicações que necessitam calcular
a área de um ponto complexo a outro.&lt;/p&gt;


&lt;p&gt;
03.  Armazenar coordenadas espaciais diretamente no banco
de dados, ou melhor, armazenar informações de uma localização à
outra.&lt;/p&gt;


&lt;p&gt;Relembro de há alguns dias estive estudando geometria e o plano geométrico pode ser descrito por duas dimensões criadas no espaço,
dimensões estas que podem ser descritas usando as posições em uma
ordem de números indicando a posição de cada uma, ou melhor 'o
verdadeiro X e Y'.&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15684/1.JPG&quot; /&gt;&lt;/p&gt;


&lt;p&gt;A figura acima
representa um plano que possui dois pontos, identificando as
coordenadas X (esquerdo e direito) e Y (acima e abaixo). Neste caso, com esses valores conforme mostra a imagem, podemos
identificar dois pontos no espaço dimensional.&lt;/p&gt;


&lt;h4&gt;Qual
diferença entre Geometry e Geopraphy?&lt;/h4&gt;


&lt;p&gt;Atualmente o SQL Server 2008 suporta estes dois tipos de dados, porém eles possuem algumas diferenças entre si. Vale lembrar que,
apesar de ambos fazerem parte de dados espaciais, cada um tem por
objetivo uma determinada forma.&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Geometry&lt;/strong&gt;: É
um tipo de dados .NET common languague runtime (CLR),  tem por
objetivo armazenar dados baseados em pontos nas dimensões(X,Y) e
opcionalmente nas coordenadas (Z) em um único modelo traçado. Essas
dimensões podem ser conhecidas através do 'plano geométrico',
representando, assim, os dados em um sistema de coordenadas
Euclidean (flat). Para mais informações a respeito desse tipo de dados, visite
&lt;a href=&quot;http://msdn.microsoft.com/en-us/library/bb933973.aspx&quot;&gt;geometry
Data Type Method Reference.&lt;/a&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Geometry:
Relaciona-se a um tipo de dados com mapeamento em um plano com duas
dimensões (sistema de coordenadas x e y)&lt;/strong&gt;&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Geopraphy&lt;/strong&gt;: Pode
ser visto também como outro tipo de dados .NET common language
runtime (CLR), os dados geográficos têm por objetivo armazenar
linhas, pontos, círculos,  polígonos e uma coleção de cada um
desses, usando um modelo oposto ao 'round earth', o
'flat earth'; utilizando as coordenadas X e Y, o tipo de dados
geometry utiliza as combinações de latitude/longitude (GPS)
representando um único ponto. Para mais informações sobre esse tipo de dados, visite &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/bb933973.aspx&quot;&gt;geometry
Data Type Method Reference.&lt;/a&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Geography:
Relaciona-se a um tipo de dados que armazenam informações em
relação à superfície terrestre.&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;&lt;em&gt;Obs: Vale lembrar
que os dois tipos de dados armazenam linhas,
pontos, polígonos, círculos. A diferença  é que o tipo de dados
geometry
utiliza o plano 2d e o tipo de dados geopraphy
utiliza o plano 3d.&lt;/em&gt;&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15684/2.JPG&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Os
dados geometry e geography são abstraídos por tipos específicos,
basicamente eles possuem os mesmos modos, a diferença entre eles é
apenas se a criação especificada é direcionada para um plano ou
espaço.&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;
Polygon&lt;/li&gt;

&lt;li&gt;
MultiPolygon&lt;/li&gt;

&lt;li&gt;
LineString&lt;/li&gt;

&lt;li&gt;
MultiLineString&lt;/li&gt;

&lt;li&gt;
Point&lt;/li&gt;

&lt;li&gt;
MultiPoint&lt;/li&gt;

&lt;li&gt;
GeometryCollection&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Cada
tipo corresponde a uma coleção. Podemos armazenar um único ponto
utilizando Point; se por acaso for útil armazenar dois pontos, podemos utilizar
MultiPoint, que nos provê uma coleção de agregação dos tipos específicos. O SQL Server possui também uma hierarquia que pode ser vista na imagem abaixo:&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15684/3.JPG&quot; /&gt;&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Polygon:&lt;/strong&gt; Pode
ser descrito como um modelo fechado, por exemplo: bola. O Polygon
pode consistir em uma bola onde pode conter zero ou mais lados. Onde o
Polygon foi construído nunca terá intersecção.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15684/4.JPG&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;MultiPolygon:&lt;/strong&gt; Esse
tipo representa uma coleção de polígonos válidos, neste caso se
um polígono dentro da coleção estiver inválido,
a coleção estará completamente inválida. Onde o MultiPolygon foi construído nunca terá intersecção.&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;LineString:&lt;/strong&gt;
LineString é um objeto de uma dimensão que consiste em
dois ou mais pontos identificados com segmentos em linhas. As
linhas não possuem dimensões correta, no entanto pode ser simples,
complexa e fechada.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15684/5.JPG&quot; /&gt;&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;&lt;strong&gt;MultiLineString:
&lt;/strong&gt;Este tipo representa a coleção dos objetos LineString, similar
ao objeto MultiPoint, no entanto pode-se também fornecer expressões
WKT sendo identificado por todos LineStrings na coleção.
MultiLineString podem ser simples, intersecção ou fechado.&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;Point:&lt;/strong&gt; Representa
uma única dimensão, sendo referenciado e identificado pelo par.
Pode-se utilizar o Point em uma posição para identificar uma
localização na superfície.&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;MultiPoint:&lt;/strong&gt;
Representa a coleção de zero ou mais pontos, geralmente é
utilizado para identificar a localização na superfície.&lt;/li&gt;

&lt;li&gt;&lt;strong&gt;GeometryCollection:&lt;/strong&gt;
Os tipos mostrados acima são específicos para cada um. O
tipo GeometryCollection pode armazenar vários outros tipos de objeto
em apenas um só. Por exemplo, podemos armazenar tanto LineString
como Point em um único GeometryCollection.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Na próxima semana continuaremos o assunto, falando dos diferentes formatos que podem
ser utilizados para representar os dados espaciais. Até lá. &lt;/p&gt;

</description>
            <author>lucassouzace@hotmail.com (Lucas Souza)</author>
            <pubDate>Tue, 26 Jan 2010 10:00:00 +0100</pubDate>
            <guid>http://imasters.uol.com.br/artigo/15684</guid>
        </item>
        <item>
            <title>Como obter uma organização robusta no SQL Server 2008</title>
            <link>http://imasters.uol.com.br/artigo/15605/sql_server/como_obter_uma_organizacao_robusta_no_sql_server_2008/</link>
            <description>&lt;p&gt;Atualmente um dos maiores
objetivos de empresas é possuir uma plataforma ao mesmo tempo organizada e robusta. Sendo assim, o SQL Server 2008 nos trouxe o
framework Policy Management, também conhecido como Declarative Management Framework, que tem por objetivo manter os dados nas regiões pré-definidas pelo
usuário.&lt;/p&gt;


&lt;h4&gt;Componentes do Policy Management&lt;/h4&gt;


&lt;p&gt;Existem
cinco componentes fundamentais que englobam o framework Policy
Managemen. Estes componentes podem ser usados para
criação e verificação de condições. São eles:&lt;/p&gt;


&lt;ol&gt;&lt;li&gt;Facets
(Facetas)&lt;/li&gt;

&lt;li&gt;Conditions
(Condições)&lt;/li&gt;

&lt;li&gt;Policies
(Políticas)&lt;/li&gt;

&lt;li&gt;Policy
targets (Política alvo)&lt;/li&gt;

&lt;li&gt;Policy
categories (Categorias de Política)&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;01. Facets&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;As facetas
podem ser descritas como objetos padrões que automaticamente estão
disponíveis para o uso após a instalação do aplicativo SQL Server
2008. Neste caso, as facetas definem o tipo de objeto  ou opção
assinalada para: Banco de dados, Indexes, Endpoint, entre outros.
Existem aproximadamente 74 facetas disponíveis, são elas implementadas
por .NET assemblies possuindo suas propriedades únicas para cada
uma.&lt;/p&gt;


&lt;p&gt;Todos os
objetos do framework Policy Management estão armazenados no
banco de dados msdb, e as facetas podem ser ilustradas de duas formas:
uma delas utilizando o comando dbo.syspolicy_management_facets, ou
então, utilizando o SQL Server Management Studio (SSMS).&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;02. Conditions&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;As condições
têm por objetivo irem mais além do que as facetas oferecem, ou seja,
podemos definir expressões Transact-SQL avançadas utilizando
operadores =, &amp;lt;&amp;gt;, LIKE, NOT LIKE, IN ou NOT IN. Por exemplo, a
cláusula WHERE nos garante a filtração dos dados para chegarmos a
um determinado ponto utilizando menos recurso de processador, as
condições são similares, todavia as condições são verificadas
em um único passo.&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;03. Policy
Targets&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;A política alvo tem por objetivo principal a
efetuação de inúmeras condições as quais serão executadas em
instâncias remotas, por exemplo: nosso servidor está localizado em
Fortaleza, temos 2 Filiais atualmente localizadas em São Paulo e Rio
de Janeiro, podemos aplicar as políticas padrões do servidor
central (Fortaleza) para ambos as filiais citadas acima.&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;04. Policies&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;As
políticas estão assinaladas a uma única condição. Os objetivos
principais são verificar uma determinada circunstância e reforçar
uma determinada cláusula.
As políticas podem ser executadas das seguintes formas:&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;On schedule  - a execução é feita por um agendamento definido pelo usuário, neste
caso é utilizado um job através do SQL Server Agent.&lt;/li&gt;

&lt;li&gt;On demand - disponibiliza a política quando é feita a execução pelo usuário.&lt;/li&gt;

&lt;li&gt;On change, prevent 
- efetua a criação de um gatilho data definition language (DDL)
impedindo que ocorra alguma mudança inesperada.&lt;/li&gt;

&lt;li&gt;On change, log only - se por acaso ocorrer alguma mudança, automaticamente é criado um
evento de notificação.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;05. Policy
Categories&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;As categorias de
política podem ser usadas para um ou mais grupo de uma única
unidade, a categoria padrão se não assinalada será DEFAULT. Para
verificar ou forçar uma determinada política é necessária a
criação de uma assinatura para uma ou mais políticas.&lt;/p&gt;


&lt;h4&gt;

Posso
importar ou exportar políticas?&lt;/h4&gt;


&lt;p&gt;Sim,
é possível fazer isso, as políticas e as condições podem ser
exportadas para arquivos formados e, além disso, pode-se também
importar através deles. O SQL Server possui 53 políticas as quais
estão localizadas no diretório Microsoft SQL
Server&amp;#92;100&amp;#92;Tools&amp;#92;Policies, dentre as existentes podemos
categoriza-las como:&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;50 Políticas direcionadas a Engine do Banco
de Dados;&lt;/li&gt;

&lt;li&gt;2 Políticas direcionadas ao Reporting Services;&lt;/li&gt;

&lt;li&gt;1
Política direcionada ao Analysis Services.&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;Como
importar e exportar as políticas&lt;/h4&gt;


&lt;p&gt;Podemos
importar/exportar as políticas utilizando a janela Object Explorer a
qual localiza-se na parte interior do SQL Server Management Studio
(SSMS). Clique com o botão direito em Policies &amp;gt; Import
Policy. Para exportar as políticas devidamente, é necessário
ter pelo o menos uma, sendo assim crie a sua política, em seguida
clique com o botão direito encima e Export Policy, a figura abaixo
ilustra de forma clara, veja:&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15605/1.jpg&quot; /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;c4&quot;&gt;Figura
1.0 - Object Explorer  Policies&lt;/span&gt;&lt;/p&gt;


&lt;h4&gt;Exemplo
Prático&lt;/h4&gt;


&lt;p&gt;Este
exemplo tem por objetivo forçar a integridade dos dados, ou seja,
todas as tabelas em nosso banco de dados atual terão que iniciar com
o prefixo tbl_. Caso a criação de alguma tabela se iniciar fora
desse padrão, a aplicação não será sucedida, ou melhor, irá
sofrer um rollback, caso contrário, aplicação irá sofrer um
committed e assim será bem sucedida a transação pela qual está
dentro do padrão adotado pela empresa.&lt;/p&gt;


&lt;p&gt;1º)
Clique em Management,
em seguida Policy
Management, após isto
clique com o botão direito em Conditions
&amp;gt;
New Condition.&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15605/2.jpg&quot; /&gt;&lt;/p&gt;


&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;Figura
1.1 - Object Explorer  Conditions&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;2º)
Defina as seguintes características: Name: PM_Table (ou
desejado), Facet: Multipart Name, abaixo do campo Field
selecione @Name, em Operator selecione LIKE,
e por último emValue s digite 'tbl_%', e por fim
clique em OK.&lt;/p&gt;


&lt;p&gt;Definição: Essa janela tem por
objetivo definirmos qual o padrão que a política irá adotar, neste
caso irá se chamar: PM_Table, a faceta por nome Multipart Name, e
por último foi definido que a variável @Name, ou seja, o nome de
cada tabela terá que se iniciar com o prefixo tbl_.&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15605/3.jpg&quot; /&gt;&lt;/p&gt;


&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;Figura
1.2 (Conditions)&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;NOTA: Veja que após
termos clicado em Ok foi exibida a nossa condição na raiz Conditions,
a imagem abaixo ilustra de forma clara.&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15605/4.jpg&quot; /&gt;&lt;br /&gt;&lt;span class=&quot;c4&quot;&gt;Figura
1.3 - Conditions  PM_Table&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;3º) Estando posto ao
nó Policy Management, clique com o botão direito em
Policies, em seguida New Policy.&lt;/p&gt;


&lt;p&gt;4º) Defina as
seguintes propriedades, Name: PM_TableComplete, habilite a
opção Enabled, em Check Condition selecione a condição
conforme criamos nos passos anteriores, marque a opção Table,
em Evaluation Mode selecione On Change: prevent, clique na aba
Description, em seguida defina uma descrição conforme pode
nos ajudar futuramente, e por último clique em OK.&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15605/5.jpg&quot; /&gt;&lt;/p&gt;


&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;Figura
1.4 - New Policy&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15605/6.jpg&quot; /&gt;&lt;/p&gt;


&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;Figura
1.5 - New Policy - Description&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;Ótimo, nossa política
está completamente finalizada, se por acaso algum usuário tentar
efetuar alguma instrução Transact-SQL juntamente com a cláusula
CREATE TABLE sem adicionar os seguintes parâmetros: tbl_, a aplicação
irá sofrer um rollback, veja o exemplo abaixo:&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15605/7.jpg&quot; /&gt;&lt;/p&gt;


&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;Figura
1.6 - Messages&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;Como pode ser visto,
aplicação não foi bem sucedida, isto devido a nossa política
conforme criamos nos passos anteriores, veja que com este framework
temos uma grande facilidade em definirmos um certo padrão e utilizá-lo de uma forma fácil/clara.&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Conclusão&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Este artigo teve por
objetivo a exibição de um novo framework do SQL Server 2008, Policy
Management, que tem por objetivo forçar a integridade dos
dados em uma aplicação comercial. Mostramos quais
são os componentes que englobam este framework, como devemos
exportar/importar políticas, e por último vimos um exemplo prático
de como aplicarmos uma determinada política em um ambiente real.&lt;/p&gt;


&lt;p&gt;&lt;br /&gt;[]´s&lt;/p&gt;

</description>
            <author>lucassouzace@hotmail.com (Lucas Souza)</author>
            <pubDate>Wed, 20 Jan 2010 11:00:00 +0100</pubDate>
            <guid>http://imasters.uol.com.br/artigo/15605</guid>
        </item>
        <item>
            <title>ASP .NET - Preenchendo um DropDownList com LINQ to SQL</title>
            <link>http://imasters.uol.com.br/artigo/15478/aspnet/asp_net_preenchendo_um_dropdownlist_com_linq_to_sql/</link>
            <description>&lt;p&gt;A partir da versão 3.5 da
plataforma .NET o Visual Studio trouxe integrado o LINQ como uma
nova ferramenta para trabalhar com a camada de dados em
aplicações que usam a linguagem VB.NET ou C#. Antes do LINQ,
geralmente era criada uma camada de acesso a dados com a
utilização dos objetos SqlConnection, SqlCommand, DataReader,
DataSet, etc para manipular os dados. &lt;/p&gt;


&lt;p&gt;Com o advento do LINQ e do LINQ to
SQL não precisamos nos preocupar com detalhes relacionados aos
objetos SqlConnection, SqlCommand, DataReader e DataSet, pois
podemos usar os objetos de negócios gerados pelo LINQ e a camada
de acesso a dados.&lt;/p&gt;


&lt;p&gt;Por ser uma tecnologia recente, ainda
estamos aprendendo a melhor forma de usar o recurso oferecido
pelo LINQ e o LINQ to SQL em nossas aplicações, e este artigo
tem o objetivo de mostrar como você pode preencher um controle dropdownlist em uma aplicação ASP .NET usando o LINQ to SQL.&lt;/p&gt;


&lt;p&gt;Portanto eu vou mostrar duas das formas
mais usadas de preencher o controle Dropdownlist em um web site
ASP .NET. Usaremos o Visual Web Developer 2008 Express
Edition e o SQL Server 2005 Express Edition e o banco de dados
Northwind.mdf.&lt;/p&gt;


&lt;p&gt;Vamos lá.&lt;/p&gt;


&lt;p&gt;Abra o Visual Web Developer 
2008 Express e crie
um novo web site no menu File-&amp;gt;New Web Site, usando o template ASP .NET Web Site e a linguagem Visual Basic, com o nome
DropDownList_LINQ;&lt;/p&gt;


&lt;p&gt;Selecione o projeto e clique com o
botão direito do mouse sobre o nome e selecione Add -&amp;gt; New
Item;&lt;/p&gt;


&lt;p&gt;A seguir selecione o template LINQ
to SQL Classes e informe o nome Northwind.dbml, pois vamos usar o
banco de dados Northwind.mdf;&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15478/1.gif&quot; /&gt;&lt;/p&gt;


&lt;p&gt;O arquivo .dbml será copiado para a
pasta AppCode e o assistente LINQ será aberto.&lt;/p&gt;


&lt;p&gt;A seguir abra a janela DataBase
Explorer e selecione a conexão com o banco de dados Northwind.mdf (se ela não existir você deverá criá-la).
Expanda os objetos Table e arraste e solte a tabela &lt;strong&gt;Products&lt;/strong&gt;
no descrito LINQ para gerar o mapeamento OR/M e criar a entidade &lt;strong&gt;Product&lt;/strong&gt;
e contexto &lt;strong&gt;NorthwindDataContext&lt;/strong&gt; para gerenciar o
relacionamento entre a entidade e a tabela usada no exemplo
conforme a figura abaixo:&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15478/2.gif&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Com isso já temos o ambiente
preparado para acessar os dados a partir do LINQ to SQL.&lt;/p&gt;


&lt;h4&gt;&lt;strong&gt;1.
Preenchendo um dropdownlist com o LinqDataSource&lt;/strong&gt;&lt;/h4&gt;


&lt;p&gt;Usar o controle para preencher o
controle dropdownlist é uma das maneiras mais simples.&lt;/p&gt;


&lt;p&gt;Selecione e abra a página
Default.aspx e inclua uma tabela a partir do menu Table-&amp;gt;
Insert Table;&lt;/p&gt;


&lt;p&gt;A seguir inclua um controle
DropDownList na página e a partir da guia Data arraste e solte
um controle LinqDataSource conforme o leiaute abaixo:&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15478/3.gif&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Vamos configurar o componente
LinqDataSource. Selecione o componente e clique em Configure DataSource;&lt;/p&gt;


&lt;p&gt;Na janela do assistente você verá
o contexto NorthwindDataContext sendo exibido, clique em Next&amp;gt;;&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15478/4.gif&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Na próxima janela selecione a
tabela Products e marque os campos que vamos usar: ProductID e
ProductName e clique em Finish;&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15478/5.gif&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Agora selecione o controle
Dropdownlist e defina as seguintes propriedades:&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;ID = ddl1 - o nome do controle;&lt;/li&gt;

&lt;li&gt;DataSourceID - LinqDataSource1
        - a fonte de dados do controle;&lt;/li&gt;

&lt;li&gt;DataTextField - ProductName - o
        campo que será exibido no controle;&lt;/li&gt;

&lt;li&gt;DataValueField - ProductID - o
        valor que será tratado quando um item for selecionado no
        controle;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15478/6.gif&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Executando o web site iremos obter:&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15478/7.gif&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Esta é a forma mais fácil de fazer
a vinculação com o dropdownlist, não usa nenhum código e é
fácil de entender e de manter, mas nem sempre o que é o mais
fácil é o mais correto, e dependendo da arquitetura do seu
projeto, efetuar a vinculação direta usando o controle
LinqDataSource com a fonte de dados viola a hierarquia de uma
arquitetura em camadas e não lhe oferece nenhum objeto com o
qual você possa trabalhar usando padrões de projeto.&lt;/p&gt;


&lt;h4&gt;&lt;strong&gt;2.
Preenchendo um dropdownlist com vinculação a objetos LINQ&lt;/strong&gt;&lt;/h4&gt;


&lt;p&gt;Vamos agora mostrar uma outra forma
de preencher o dropdownlist usando objetos LINQ através da
vinculação direta.&lt;/p&gt;


&lt;p&gt;Inclua um novo controle DropDowList
na página e defina as seguintes propriedades:&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;ID = ddl2 - o nome do controle;&lt;/li&gt;

&lt;li&gt;DataTextField - ProductName - o
        campo que será exibido no controle;&lt;/li&gt;

&lt;li&gt;DataValueField - ProductID - o
        valor que será tratado quando um item for selecionado no
        controle;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Agora no evento Load da página
inclua o código abaixo:&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load&lt;br /&gt;        If Not IsPostBack Then&lt;br /&gt;            ' Vinculando diretamente com a entidade LINQ&lt;br /&gt;            ddl2.DataSource = GetProdutos()&lt;br /&gt;            ddl2.DataBind()&lt;br /&gt;        End If&lt;br /&gt;    End Sub&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Agora defina o método estático &lt;strong&gt;GetProdutos&lt;/strong&gt;
conforme abaixo:&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;  Public Shared Function GetProdutos() As List(Of Product)&lt;br /&gt;        'Usamos o contexto criado pelo LINQ to SQL&lt;br /&gt;        Using contexto As New NorthwindDataContext()&lt;br /&gt;            Return (contexto.Products.ToList())&lt;br /&gt;        End Using&lt;br /&gt;    End Function&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Usando este método tivemos que
escrever um pouco de código. O ideal seria ter criado uma camada
separada para definir os objetos LINQ através da criação de um
novo projeto do tipo Class Library e em seguida referenciar o
projeto ao nosso web site.&lt;/p&gt;


&lt;p&gt;A função GetProdutos() acessa
através do contexto criado pelo LINQ a tabela Products; chamamos
o método ToList() para retornar uma coleção de objetos.&lt;/p&gt;


&lt;p&gt;Executando o web site teremos o
seguinte resultado:&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15478/8.gif&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Neste método temos um controle
maior sobre como os dados podem ser exibidos, pois podemos alterar
o método GetProdutos() incluindo algum processamento prévio.
Além disso, dessa forma podemos trabalhar com uma arquitetura em
camadas. O problema é que estamos fazendo a vinculação
diretamente com os objetos LINQ e o nosso código não fica tão
fácil de ser reusado.&lt;/p&gt;


&lt;p&gt;Existe ainda uma outra forma de
fazer esta vinculação através da definição de uma interface
para os tipos que estamos tratando e da implementação desta
interface e da utilização de Reflection, mas abordarei este
método em outro artigo.&lt;/p&gt;


&lt;p&gt;Aguarde mais artigos sobre ADO .NET.&lt;/p&gt;

</description>
            <author>macoratti@yahoo.com (José Carlos Macoratti)</author>
            <pubDate>Thu, 07 Jan 2010 11:00:00 +0100</pubDate>
            <guid>http://imasters.uol.com.br/artigo/15478</guid>
        </item>
        <item>
            <title>Raspagem de dados</title>
            <link>http://imasters.uol.com.br/artigo/15447/bancodedados/raspagem_de_dados/</link>
            <description>&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15447/Figura1_raspagem_dados.jpg&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Olá, pessoal. Na coluna desta
semana vou comentar sobre uma idéia que já é antiga, mas vem ganhando alguma
atenção recentemente: a raspagem de dados. Comentarei sobre o que se trata,
alguns detalhes, um exemplo e também apresentarei outras questões relevantes.&lt;/p&gt;


&lt;p&gt;O conceito de raspagem de dados,
ou data scrapping, não é novo. De
acordo com a página americana da Wikipédia, desde os primórdios da informática
já havia a necessidade e soluções para se obter dados a partir da tela do
computador, ou seja, obter os dados que estão em telas, relatórios e outros
formatos que não são convencionais.&lt;/p&gt;


&lt;p&gt;Com o surgimento da internet, cada vez mais dados
são publicados on-line. Porém a maioria destes dados não é pronta para ser
consumida, sob o ponto de vista de um banco de dados, e ser transformada em
informação relevante. Isso quer dizer que é preciso, de alguma maneira, obter
os dados publicados nos seus mais variados formatos, limpá-los e armazená-los
em um formato que possa ser facilmente manipulado.&lt;/p&gt;


&lt;p&gt;Na área de banco de dados já existe uma sigla
que resume este processo: ETL. Um processo ETL (Extract Transform Load  - Extração Transformação Carga) é adotado
periodicamente ou esporadicamente e sua função é a extração de dados de
diversos sistemas, a transformação desses dados conforme regras de negócios e,
por fim, a transferência dos dados para um local de armazenamento. Geralmente
utiliza-se ferramentas ETL que possuem recursos para a manipulação de diversos
formatos de dados, como arquivos texto, PDF, HTML, XML, dados relacionais, DBF,
etc. durante o processo ETL. Além disso, as ferramentas específicas para se implementar
o processo ETL contam com mecanismos para modificar, complementar, consolidar,
acrescentar, transformar, apagar e muitas outras formas de manipular dados
antes de enviá-los ao seu destino final.&lt;/p&gt;


&lt;p&gt;Devido à peculiaridade, à variação
de formato e até à forma de disponibilizar os dados na internet, as ferramentas
que fornecem suporte para implementar ETL não são capazes de obter e manipular
os dados on-line muito bem. Devido a isso, muitas pessoas começaram a chamar o
processo de obtenção e manipulação de dados on-line de raspagem de dados e
criar ferramentas para auxiliar a obtenção de dados. Já existem até livros que
abordam este assunto focando em como implementar a raspagem de dados em
linguagens específicas. Faz muito sentido pensar em ETL em um ambiente
corporativo, pois algumas pesquisas recentes indicam que aproximadamente 80% do
total de informação corporativa são armazenados em dados não estruturados.&lt;/p&gt;


&lt;p&gt;Lembro-me de que uma das primeiras vezes em que ouvi o
termo raspagem de dados foi durante a conferência Transparência Hack Day,
realizada em 2009. Neste evento diversos projetos foram apresentados para obter
dados visando ao ativismo digital, em especial para a área de fiscalização, ao acompanhamento
político e também à obtenção de notícia e fotos. Este projetos sugerem que
a obtenção de dados públicos pode facilitar e tornar transparente o
acompanhamento de ações políticas, destino de recursos, causas e efeitos de
atitudes, conscientização e outros fins. Contudo, é preciso separar a parte
técnica envolvida na obtenção e na manipulação dos dados da parte que apresenta,
interpreta e faz uso dos dados.&lt;/p&gt;


&lt;p&gt;Focando na parte técnica, geralmente as
ferramentas de raspagem de dados se baseiam na premissa de que o dado está dentro
de uma página HTML disponibilizada em um site. Neste cenário a raspagem de
dados se resume na obtenção e na manipulação destes dados, geralmente por meio de
um script que acessa o código fonte da(s) página(s) HTML, faz uma cópia local deste
conteúdo, aplica alguma regra para a obtenção do dados em si (geralmente
através de uma expressão regular ou instruções SQL) e joga os dados em uma
tabela de um banco de dados. Apesar de existirem outras fontes de dados OnLine,
como arquivos PDF, imagens, animações flash, podcasts, planilhas on-line e
outros, o princípio das ferramentas que fazem raspagem de dados segue o mesmo
conceito básico.&lt;/p&gt;


&lt;p&gt;O uso de scripts é feito devido à facilidade
e à quantidade de recursos que podem ser empregados na raspagem de dados.
Geralmente este script contém uma chamada para uma ferramenta ou instrução que faz
uma requisição web por meio de um POST ou GET, que são comandos do protocolo
HTTP. Depois de obter a página, o script geralmente salva o seu conteúdo em um
arquivo no disco ou faz um parser no
mesmo, ou seja, lê o código da página e obtém o dado que é relevante. Devido à variedade
de formatos, layouts, designs e outros fatores visuais encontrados nas páginas
da web, geralmente os scritps de raspagem de dados são customizados para uma
determinada fonte.&lt;/p&gt;


&lt;p&gt;Como exemplo, posso citar o projeto Legisdados, um
projeto para agregar software de extração de dados do Legislativo Brasileiro. A
iniciativa partiu dos projetos CongressoAberto e ParlamentoAberto que, como
fica claro pelos nomes, têm muito em comum. Para evitar repetição de esforços e
desacoplar extração de dados de aplicações escritas usando esses dados, o
projeto agrega scripts de raspagem de dados relativos ao poder
Legislativo. A idéia é aumentar a colaboração entre os dois projetos e
incentivar o surgimento de novas aplicações.&lt;/p&gt;


&lt;p&gt;Atualmente já existem algumas ferramentas e
produtos além de projetos independentes para a raspagem de dados. Destaco o
Yahoo! Query Language (YQL), uma excelente linguagem que pode ser utilizada em
conjunto com a plataforma Yahoo! Pipes. Além disso, existem diversas outras
ferramentas que permitem a raspagem de dados, como apresentado no link na parte final do artigo. &lt;/p&gt;


&lt;p&gt;Uma rápida pesquisa por
ferramentas de screen scraper em qualquer buscador vai retornar uma quantidade
considerável de ferramentas para a raspagem de dados. Porém, por mais adequada
que seja a ferramenta, sempre é preciso algum nível de customização ou, na
maioria das vezes, algum esforço de programação. &lt;/p&gt;


&lt;p&gt;Para quem acompanha as minhas colunas
aqui no iMasters pode ter percebido que eu já escrevi uma coluna onde acabei
precisando criar um pequeno script para a raspagem de dados. A coluna em
questão chama-se &lt;a href=&quot;http://imasters.uol.com.br/artigo/11354/webmarketing/estatisticas_do_canal_cparty/&quot;&gt;Estatísticas do canal #cparty&lt;/a&gt;, onde tive que obter os dados
do canal #cparty fornecido pelo &lt;a href=&quot;http://live.blogblogs.com.br/&quot;&gt;Livestream
do BlogBlogs&lt;/a&gt; e que poderia ser atualizado por vários meios. Na sequência
apresentarei os 6 passos que utilizei para obter os dados que estavam separados
em várias páginas HTML. Destaco que estes 6 passos podem ser utilizados como um
pequeno guia para quem está pensando em montar um script ou utilizar uma ferramenta
de raspagem de dados.&lt;/p&gt;


&lt;h4&gt;1. Entenda a fonte de dados&lt;/h4&gt;


&lt;p&gt;Antes de obter o dado on-line é
preciso conhecer muito bem a fonte de dados. Isso quer dizer que é preciso
navegar pela página, verificar se as informações são paginadas, colocadas em
uma tabela, frame, área dinâmica ou controle, se requerem um login, se são disponibilizadas
em um arquivo, possuem acesso via API, seguem algum padrão de apresentação e
organização e outros detalhes relevantes. No exemplo da #cparty eu estudei a
página e vi que apenas certa quantidade de dados eram apresentados na página do
Livestream. Como eu queria todos os dados de certo período, eu tive que entender
o parâmetro da URL utilizada na parte de arquivos. Isso quer dizer que a cada requisição
do tipo GET eu precisava passar um parâmetro da página que gostaria de
consultar. &lt;/p&gt;


&lt;p&gt;Este tipo de atitude é comum.
Fazer raspagem de dados com certeza vai acabar envolvendo a manipulação de
parâmetros de uma URL, principalmente quando não se deseja obter todos os dados
de um vez só, ou seja, são necessários filtros. Novamente, aqui não há regra:
cada site da web possui um jeito de apresentar informações, porém para evitar a
sobrecarga geralmente algum tipo de paginação é feita, que pode apresentar a
forma de links com o número da página, controles do tipo combo-box ou outros.
Em alguns casos é preciso até fornecer parâmetros de pesquisa em um controle e
forçar um POST para se chegar os dados ao invés de simplesmente fazer uma
requisição do tipo GET.&lt;/p&gt;


&lt;p&gt;Há casos mais complexos onde é
preciso realizar um login antes ou até elaborar alguma maneira de se ler um
dado que não está armazenado em texto, como dados colocados dentro de uma
figura, animações flash ou arquivos PDFs. Nestas situações é preciso primeiro
entender o que é necessário para se obter o dados para só depois pensar na tecnologia
necessária para se extrair o dado e transformá-lo no formato adequado.&lt;/p&gt;


&lt;h4&gt;2. Planejar como será feita a
obtenção bruta dos dados&lt;/h4&gt;


&lt;p&gt;Uma vez que é já se conhecem os detalhes
da fonte de dados é hora de planejar como será planejada a obtenção dos dados.
Isso quer dizer é preciso relacionar ferramentas, ambiente, plataforma, link
de internet e outros recursos tecnológicos necessários para se obter os dados.
Aqui também não há uma regra geral, pois cada situação pode requerer uma
ferramenta específica. Por exemplo, se o dado está armazenado em uma imagem
pode ser preciso utilizar uma biblioteca OCR ou algo similar para se obter o
dado. Outra situação envolve animações em flash, estruturas de dados em JavaScript,
aplicativos em Java, silverlight ou algo assim. Obter dados que estão dentro
destas tecnologias pode ser complexo, porém não impossível.&lt;/p&gt;


&lt;p&gt;No exemplo eu decidi utilizar um
script em SQL por conveniência, uma vez que tenho experiência nesta linguagem.
Planejei montar um script que faria uma chamada a um executável no console com
o objetivo de obter a página em um arquivo texto. Esta chamada foi colocada
dentro de um loop onde criei dinamicamente o número da página na URL que seria
passada como parâmetro para o aplicativo de código livre wget.exe para
plataforma Windows. Este pequeno programa wget.exe recebe como parâmetro a URL
da página e o arquivo texto onde será armazenado o código HTML. Após o loop eu
joguei todas as páginas em um único arquivo HTML. &lt;/p&gt;


&lt;p&gt;Depois de capturar os dados eu teria um arquivo texto grande com todo o
conteúdo das páginas. Apesar de conter os dados que eu queria, o arquivo também
tinha muitas tags HTML que não me interessavam e que foram removidas em um
passo posterior.&lt;/p&gt;


&lt;h4&gt;3. Programar a captura dos dados&lt;/h4&gt;


&lt;p&gt;Uma vez que o planejamento estava
pronto, bastou programar o script que montava o loop e fazer a chamada ao arquivo
wget.exe. Neste ponto me deparei com alguns detalhes de implementação, como a
necessidade de criar um arquivo .bat para poder chamar o arquivo wget.exe.&lt;/p&gt;


&lt;p&gt;Geralmente a implementação da
raspagem de dados envolve muitos detalhes específicos, como a utilização de
bibliotecas no script, a criação de nomeclaturas para os arquivos, a alocação
de espaço em disco e outros detalhes técnicos. Vale a pena lembrar também que
neste passo de programação é preciso efetuar testes e também considerar
questões de desempenho e acesso aos dados. Algumas páginas da Web podem perceber
que estão recebendo muitas requisições simultâneas de um mesmo endereço IP e
bloquear o acesso. Para isso, faça testes que possam ajudar a identificar
problemas quando o script da raspagem for executado.&lt;/p&gt;


&lt;h4&gt;4. Verificar os dados capturados e
realizar a limpeza&lt;/h4&gt;


&lt;p&gt;Após programar e executar o
script que faz a raspagem de dados é preciso verificar se tudo ocorreu bem e se
os dados brutos realmente foram capturados. Aqui não é preciso realizar uma
verificação completa, e sim apenas checar se o processo automático realmente
capturou os dados. &lt;/p&gt;


&lt;p&gt;Na maioria das situações a
raspagem de dados não trará os dados já prontos e no formato adequado, a não
ser que isso seja planejado desde o começo by
design. Isso quer dizer que geralmente muito do que foi capturado precisa ser
limpo e transformado em informação que realmente pode ser manipulada da maneira
desejada.&lt;/p&gt;


&lt;p&gt;Aqui temos algumas opções que
envolvem trabalhar diretamente no que foi capturado ou jogar tudo em um local
que forneça recursos para manipulação dados. Os bancos de dados atuais
trabalham muito bem com a linguagem SQL, que é feita sob medida para tornar
fácil a manipulação de dados. Porém existem diversas outras ferramentas que
permitem o uso de expressões regulares para limpar os dados, em especial a
linguagem Perl e vários comandos do shell do Unix, que também conta com o conveniente
mecanismo de redirecionamento de dados conhecido como pipes.&lt;/p&gt;


&lt;p&gt;Na maioria das vezes é preciso
eliminar dados irrelevantes que vieram junto com os dados desejados. No exemplo
da #cparty todo o HTML da página que não continha o que foi escrito no Twitter
deveria ser eliminado. Optei por carregar todo o arquivo com os dados
capturados em uma tabela com uma única coluna contendo caracteres sem limites.
A separação em linhas foi feita de acordo com os caracteres separadores de
final de linhas (CR+LF). Aliás, quem trabalha com raspagem de dados em texto
deve estar preparado para conhecer bem certos caracteres especiais,
especialmente os caracteres de controle.&lt;/p&gt;


&lt;p&gt;Já com os dados carregados em uma
só tabela eu complementei o script com diversas instruções UPDATE e DELETE que
eliminaram as linhas que eu não queria. Além disso, pude utilizar a instrução
SELECT para prototipar as chamadas às funções que manipulam strings (SUBSTRING,
REPLACE, etc) e montar as colunas com os dados no formato que eu precisava.&lt;/p&gt;


&lt;h4&gt;5. Carregar no banco de dados e
adequar os dados ao formato modelado&lt;/h4&gt;


&lt;p&gt;Uma vez que já se tenha realizado
a limpeza nos dados e outras manipulações, recomenda-se colocar os dados em um
formato que seja adequado. Este passo muitas vezes requer a modelagem de uma ou
mais tabelas em um banco de dados visando à integração com um sistema ou com
uma ferramenta que possa fazer a visualização de dados de forma gráfica. Além
disso, as ferramentas de relatórios ou de mineração de dados podem exigir um
formato proprietário que pode ser facilmente gerado através de ferramentas de
importação e exportação dos banco de dados.&lt;/p&gt;


&lt;p&gt;Destaco que a modelagem é uma
tarefa muito importante, principalmente quando se fala em integração com dados
já existentes que estão separados em várias tabelas. Aqui é preciso tomar
cuidado na ordem de inserção, pois relacionamentos de chave primária e chave
estrangeira forçam a integridade relacional do banco de dados e requerem uma
ordem específica de inserção, deleção e atualização dos dados.&lt;/p&gt;


&lt;p&gt;No exemplo da #cparty foi fácil
criar uma tabela a partir da instrução SELECT que já separava corretamente as
colunas da tabela. Fiz isso utilizando a cláusula INTO da instrução SELECT,
pois no SQL Server esta cláusula permite a criação de uma tabela a partir do
que a instrução SELECT retornar. Porém tive também que adequar os dados obtidos
ao formato dos tipos de dados do SQL Server, o que deu certo trabalho. Foi especialmente
complicado adequar a data da mensagem devido ao formato necessário para a
inserção desta data no tipo de dados DATETIME do SQL Server.&lt;/p&gt;


&lt;p&gt;Quanto todos os dados já estavam
armazenados na tabela fiz questão de criar índices e também uma chave primária
artificial para que a manipulação apresentasse uma performance razoável. Como
utilizei o site ManyEyes para realizar algumas visualizações, tive que exportar
os dados para outro arquivo texto já com as palavras separadas por cada dia do
evento.&lt;/p&gt;


&lt;h4&gt;6. Deixar a solução pronta para
ser utilizada novamente&lt;/h4&gt;


&lt;p&gt;Algumas raspagens de dados precisam
ser periódicas, ou seja, devem ser executadas com alguma freqüência. Nestes
casos vale a pena incrementar o script e montá-lo de forma que possa ser
chamado várias vezes e em vários horários automaticamente.&lt;/p&gt;


&lt;p&gt;Isso implica em planejamento de
armazenamento, expurgo de dados, horário de execução e recursos adicionais como
backup, garantia de largura de banda e backup. A forma de implementar a
reutilização do script varia de plataforma para plataforma, porém a maioria
dos bancos de dados e o próprio sistema operacional contêm ferramentas,
funcionalidades e maneiras de se criar tarefas agendadas, cujo resultado de
execução pode ser acompanhado posteriormente.&lt;/p&gt;


&lt;p&gt;No exemplo da #cparty não houve a
necessidade de executar o script de raspagem de dados novamente, pois o que eu
queria era obter apenas a quantidade de dados de um período fechado. Porém nem
todas as soluções seguem esta idéia e podem precisar de extração de dados
periódica.&lt;/p&gt;


&lt;p&gt;Para terminar, destaco a questão
do uso e da disponibilização dos dados. Ao contrário do que muitos podem pensar,
nem tudo que é colocado na internet é público e pode ser utilizado como bem
entender. Existem muitos dados que são publicados na internet, independente da
sua origem/site, que estão protegidos por copyright ou outro modelo de licença.
Infelizmente nem sempre é possível obter informações sobre a licença do dados,
porém, em caso de dúvida, é preciso adotar uma postura ética e perguntar para
quem está disponibilizando a informação e como ela pode ser utilizada. &lt;/p&gt;


&lt;p&gt;Um grande abraço e até a próxima,
pessoal! &lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Referências:&lt;/strong&gt;&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;Página da Wikipédia sobre
raspagem de dados: &lt;a href=&quot;http://en.wikipedia.org/wiki/Screen-scraping#Screen_scraping&quot; class=&quot;ext&quot;&gt;http://en.wikipedia.org/wiki/Screen-scraping#Screen_scraping&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;Projeto Legisdados: &lt;a href=&quot;http://github.com/legisdados/legisdados&quot; class=&quot;ext&quot;&gt;http://github.com/legisdados/legisdados&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;Projetos do Transparência
HackDay: &lt;a href=&quot;http://blog.esfera.mobi/balanco-thackday-1/&quot; class=&quot;ext&quot;&gt;http://blog.esfera.mobi/balanco-thackday-1/&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;Linguagem YQL (Yahoo! Query Language): &lt;a href=&quot;http://developer.yahoo.com/yql/&quot; class=&quot;ext&quot;&gt;http://developer.yahoo.com/yql/&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;Plataforma Yahoo! Pipes: &lt;a href=&quot;http://pipes.yahoo.com/pipes/&quot; class=&quot;ext&quot;&gt;http://pipes.yahoo.com/pipes/&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;Livro Data Scraping Information from the Web
with ASP.NET: &lt;a href=&quot;http://www.amazon.com/Data-Scraping-Information-Web-ASP-NET/dp/B00095M8P6&quot; class=&quot;ext&quot;&gt;http://www.amazon.com/Data-Scraping-Information-Web-ASP-NET/dp/B00095M8P6&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;Ferramentas para Data Scrapping: &lt;a href=&quot;http://www.sharewareconnection.com/software.php?list=Pdf+Screen+Scraper&quot; class=&quot;ext&quot;&gt;http://www.sharewareconnection.com/software.php?list=Pdf+Screen+Scraper&lt;/a&gt;&lt;/li&gt;

&lt;/ul&gt;</description>
            <author>pichiliani@gmail.com (Mauro Pichiliani)</author>
            <pubDate>Wed, 06 Jan 2010 11:00:00 +0100</pubDate>
            <guid>http://imasters.uol.com.br/artigo/15447</guid>
        </item>
        <item>
            <title>Performance Data Warehouse</title>
            <link>http://imasters.uol.com.br/artigo/15405/sql_server/performance_data_warehouse/</link>
            <description>&lt;p&gt;O Performance Data Warehouse, que também é conhecido como Performance
Studio, é um recurso implementado no SQL Server 2008 que permite a
coleta de informações para estudo e análise de performance.&lt;/p&gt;


&lt;p&gt;Esse novo recurso é baseado em uma feature chamada de Data Collector e está sobre dois pilares:&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;SSIS (SQL Server Integration Services)&lt;/li&gt;

&lt;li&gt;SQL Server Agent&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Toda a coleta de dados feita é armazenada em um database previamente
configurado, e a coleta pode ser feita através dos tipos de coletores
abaixo:&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;T-SQL Query&lt;/li&gt;

&lt;li&gt;SQL Trace&lt;/li&gt;

&lt;li&gt;Performance Counter&lt;/li&gt;

&lt;li&gt;Query Activity&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;O Data Collector gera dinamicamente os resultados gravando em
tabelas de output. Quando os resultados forem gerados a partir de query
devemos ter o cuidado de não incluir na consulta campos com nomes como
snapshot_time, snapshot_id ou database_name. Isso porque são nomes
reservados do Data Collector.&lt;/p&gt;


&lt;p&gt;Outra consideração é que campos do tipo XML, Text, Image, nText não serão incluídos no retorno e não serão coletados.&lt;/p&gt;


&lt;p&gt;As DMVs para consultar o Query Activity Collector são:
&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;&lt;p&gt;Sys.dm_exec_requests&lt;/p&gt;

&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Sys.dm_exec_sessions&lt;/p&gt;

&lt;/li&gt;

&lt;li&gt;Sys.dm_exec_query_stats&lt;/li&gt;

&lt;/ul&gt;

&lt;ul&gt;&lt;li&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Para utilizarmos esse recurso precisamos seguir os passos que eu vou descrever abaixo:&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;Habilitar o Data Collection&lt;/li&gt;

&lt;li&gt;Configurar o banco de dados que gravará a coleta&lt;/li&gt;

&lt;li&gt;E analisar os dados&lt;/li&gt;

&lt;li&gt;Vejamos isso na prática, é muito fácil.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Configurando o Data Collector&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;No Manegement Studio, abra o Object Explorer e expanda a guia
Management. Lá haverá um item chamado Data Collection. Para habilitar,
basta clicar com o botão direito e clicar na opção Enable Data
Collection.&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15405/Imagem01.jpg&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Acesse o Data Collection novamente com o botão direito do mouse e
clique na opção Configure Management Data Warehouse, isso abrirá um
wizard. Basta avançar na tela de Welcome e na próxima tela marcar
Create or upgrade a management data warehouse e clique em avançar.&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15405/Imagem02.jpg&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Na próxima tela iremos definir os dados de gravação da coleta, no
caso, o database. Para isso, clique em New, isso fará com que abra a tela
de New Database. Crie um banco de dados chamado coleta, clique em OK, e
voltaremos ao Wizard do Data Collection, e é só avançar para
continuarmos.&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15405/Imagem03.jpg&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Map Logins and Users é onde selecionaremos os usuários e os logins
que serão usados para coleta. Nesse caso, selecione o usuário que
corresponde ao SQL Server Service e a role mdw_admin. Após configurar
essa etapa, basta avançar e concluir.&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15405/Imagem04.jpg&quot; /&gt;&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Configurando o Management Data Warehouse&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Agora que temos o Data Collector configurado, vamos configurar o
Management Data Warehouse. Clique com o botão direito sobre o item Data
Collection e selecione a opção Configure Management Data Warehouse. Selecione Set up data collection e clique em avançar.&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15405/Imagem05.jpg&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Na próxima tela, vamos deixar as opções padrão e avançar. Ao fazer
isso, abrirá a tela de autenticação do SQL Server, confirme as
credenciais e clique em finalizar.&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Agendando a coleta&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;As coletas não precisam ser feitas sempre? Ou precisam? Essas
perguntas são respondidas apenas com estudo de caso da situação. Algumas
vezes, na maior parte dos casos, vamos coletar apenas em períodos em que
uma determinada situação ocorre.&lt;/p&gt;


&lt;p&gt;Para realizarmos o agendamento de um item, como exemplo a utilização
de disco, vamos expandir o item System Data Collection Sets e clicar
sobre Disk Usage com o botão direito e selecionar propriedades.&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15405/Imagem06.jpg&quot; /&gt;&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15405/Imagem07.jpg&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Na tela de propriedades, sobre a opção Data collection and upload,
temos o Schedule, clique em New. Isso abrirá a tela de New Job Schedule
e é só preencher o nome e o período do agendamento. Veja a imagem
abaixo - o agendamento foi feito para acontecer todas as segundas-feiras, quartas-feiras, sextas-feiras e sábados às 13 horas.&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15405/Imagem08.jpg&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Importante lembrar que o agendamento só irá funcionar se o serviço do SQL Server Agent for inicializado.&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Relatório&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Ok, temos um coletor de informações que está programado para
acontecer em determinado período, mas como vamos visualizar essas
informações?&lt;/p&gt;


&lt;p&gt;Simples: na opção Data Collection clicamos com o botão direito do
mouse e sobre o item Reports, abrirá o item Management Data Warehouse e
nessa opção encontraremos o item Disk Usage Summary.&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15405/Imagem09.jpg&quot; /&gt;&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/15405/Imagem10.jpg&quot; /&gt;&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Conclusão&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;O SQL Server 2008 traz novas ferramentas para análise e monitoramento de ajustes de performance. Esse tipo de monitoramento em versões anteriores era apenas feito através de ferramentas de terceiros.&lt;/p&gt;


&lt;p&gt;Espero que esse artigo ajude a iniciar boas análises e grandes descobertas.&lt;/p&gt;

</description>
            <author>rodrigo@crespi.pro.br (Rodrigo Crespi)</author>
            <pubDate>Tue, 05 Jan 2010 11:00:00 +0100</pubDate>
            <guid>http://imasters.uol.com.br/artigo/15405</guid>
        </item>
        <item>
            <title>Dicas para uma boa performance em seus códigos T-SQL</title>
            <link>http://imasters.uol.com.br/artigo/14745/sql_server/dicas_para_uma_boa_performance_em_seus_codigos_t-sql/</link>
            <description>&lt;p&gt;Melhorar a performance nos bancos de dados é sempre algo desejado. Na hora de fazer isso, surgem várias dúvidas de como fazer da melhor forma. Talvez uma das perguntas mais comuns em fóruns seja como melhorar a performance de uma consulta que está muito lenta. Situação que traz uma resposta totalmente aberta: depende! E depende mesmo, de vários fatores. A lista a seguir traz apenas 10 dicas que podem fazer a diferença em seus scripts T-SQL. Espero que ela o ajude a encontrar uma boa solução, mesmo sabendo que os fatores que causam perda de performance precisam ser analisados antes de qualquer ação. Bom proveito!&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;01. No comando SELECT, utilize apenas o número de colunas que seja necessário&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Deve-se evitar a utilização do * para o retorno da consulta. Tudo porque ao
utilizar o * em sua consulta, o SQL consulta a tabela syscolumns para retornar
todas as colunas e agregar a seu comando o resultado.&lt;/p&gt;


&lt;p&gt;Exemplo:&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;c2&quot;&gt;SELECT&lt;/span&gt; cNome, dNascimento, lAtivo &lt;br /&gt;&lt;span class=&quot;c2&quot;&gt;FROM&lt;/span&gt; tbPessoaFisica&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;02. Não declare variáveis que você não utilizará ou reaproveite variáveis
existentes.&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Este procedimento aumenta sua banda de cache, deixando assim, uma
margem extra de cache para outros processos.&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;03. Utilize CONVERT apenas para conversão de dados do tipo DATETIME&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Para as
demais conversões de tipos, utilize CAST, pois CAST faz parte do padrão ANSI-92
e CONVERT funciona apenas para o SQL Server, sendo que em versões futuras este
comando corre o risco de ser cortado do Transact.&lt;/p&gt;


&lt;p&gt;Exemplo:&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;c2&quot;&gt;SELECT&lt;/span&gt; &lt;br /&gt;&lt;span class=&quot;c3&quot;&gt;CAST&lt;/span&gt;(&lt;span class=&quot;c3&quot;&gt;GETDATE()&lt;/span&gt; &lt;span class=&quot;c2&quot;&gt;AS VARCHAR(20)&lt;/span&gt;) &lt;span class=&quot;c2&quot;&gt;AS&lt;/span&gt; dCast, &lt;br /&gt;&lt;span class=&quot;c3&quot;&gt;CONVERT&lt;/span&gt;(&lt;span class=&quot;c2&quot;&gt;VARCHAR(20)&lt;/span&gt;, &lt;span class=&quot;c3&quot;&gt;GETDATE()&lt;/span&gt;, 100) &lt;span class=&quot;c2&quot;&gt;AS&lt;/span&gt; dConvert&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;04. Utilize SELECT ao invés de SET&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Com apenas um SELECT você pode definir
valores para uma ou mais variáveis, enquanto para o comando SET você precisaria
de vários comandos. Sendo assim, a utilização do comando SELECT para atribuição
de valores a variáveis fica bem mais rápida.&lt;/p&gt;


&lt;p&gt;Exemplo:&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;DECLARE @id AS INT, @nome AS VARCHAR(25), @nascimento AS DATETIME&lt;br /&gt;&lt;br /&gt;-- EX de atribuição de valores utilizando SET&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SET @id = 10&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SET @nome = 'Rodrigo'&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SET @nascimento = '1984-02-29'&lt;br /&gt;&lt;br /&gt;-- EX de atribuição de valores utilizando SELECT&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;SELECT @id = 10, @nome = 'Rodrigo', @nascimento = '1984-02-29'&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;05. Cuidado com ORDER BY e DISTINCT&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Use apenas se necessário. A
utilização destes comandos pode gerar sobrecarga no engine do SQL Server,
causando a lentidão no processo ou em processos simultâneos.&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;06. Utilize as palavras reservadas do transact em letras maiúsculas para se
diferenciar das demais.&lt;/strong&gt; &lt;/p&gt;


&lt;p&gt;Esta dica não influencia no quesito performance do script, mas
ajuda a encontrar possíveis erros de sintaxe logo na escrita. Isto pode aumentar sua produtividade.&lt;/p&gt;


&lt;p&gt;Exemplo:&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;--SEM ERRO&lt;br /&gt;SELECT cNome, dNascimento, lAtivo&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;FROM tbPessoaFisica&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;WHERE idPessoaFisica = 10&lt;br /&gt;GO&lt;br /&gt;&lt;br /&gt;--COM ERRO&lt;br /&gt;SEELCT cNome, dNascimento, lAtivo&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;FROM tbPessoaFisica&lt;br /&gt;WHERE idPessoaFisica = 10;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;07. SELECT * INTO funciona muito bem para com pequenas tabelas&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Mas, ao lidar
com grande número de registro ou longo processo de consulta, pode causar
bloqueios em objetos da base tempDB, ocasionando filas para outros processos
que utilizam a tempDB. Isto ocorre porque, ao criar um objeto, é causado um lock
exclusivo nas tabelas sysobjects e syscolunms. Então, utilize este recurso com cuidado!&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;08. Utilize variáveis de tabela ao invés de tabelas temporárias&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Tabelas
temporárias podem causar recompilações em algumas procedures. Variáveis de
tabelas foram criadas para solucionar este problema de recompilações em
procedures que necessitam de objetos temporários.&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;09. Clausula WHERE&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Os diversos operadores, usados diretamente, afetam no tempo
de resposta de suas consultas. Para conhecer melhor sua proficiência, você pode
ler o artigo disponibilizado pela Microsoft em &lt;a href=&quot;http://msdn.microsoft.com/en-us/library/ms190276.aspx&quot; class=&quot;ext&quot;&gt;http://msdn.microsoft.com/en-us/library/ms190276.aspx&lt;/a&gt;&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;10. Utilize a procedure sp_executesql&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;A utilização da sp_executesql evita
recompilações de procedures. Mas, lembre-se, recompilações nem sempre são ruins.
Para maior informação sobre recompilações e cache de planos de execução, leia o
artigo do TechNet &lt;a href=&quot;http://technet.microsoft.com/en-us/library/cc966425.aspx&quot; class=&quot;ext&quot;&gt;http://technet.microsoft.com/en-us/library/cc966425.aspx&lt;/a&gt;&lt;/p&gt;


&lt;p&gt; Exemplo:&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;&lt;span class=&quot;c2&quot;&gt;EXECUTE&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;sp_executesql&lt;/span&gt;
N&lt;span class=&quot;c1&quot;&gt;'SELECT cNome, dNascimento, lAtivo FROM tbPessoaFisica&lt;br /&gt;WHERE idPessoaFisica = @idPessoaFisica'&lt;/span&gt;, N&lt;span class=&quot;c1&quot;&gt;'@idPessoaFisica INT'&lt;/span&gt;, @idPessoaFisica =&lt;br /&gt;10&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;É isso aí, pessoal, espero que essas dicas sejam úteis no dia-a-dia de vocês.&lt;/p&gt;


&lt;p&gt;Grande abraço e até a próxima.&lt;/p&gt;


&lt;p&gt; &lt;/p&gt;

</description>
            <author>rzuini@gmail.com (Rodrigo Zuini)</author>
            <pubDate>Fri, 13 Nov 2009 11:00:00 +0100</pubDate>
            <guid>http://imasters.uol.com.br/artigo/14745</guid>
        </item>
        <item>
            <title>O paradigma FillFactor - Parte 02</title>
            <link>http://imasters.uol.com.br/artigo/14819/sql_server/o_paradigma_fillfactor_parte_02/</link>
            <description>&lt;p&gt;Na &lt;a href=&quot;http://imasters.uol.com.br/artigo/14751/sql_server/o_paradigma_fillfactor_parte_01/&quot;&gt;primeira parte&lt;/a&gt; do artigo vimos como funciona internamente quando trabalhamos com um fillfactor de 100%. Hoje mostrarei o que acontece se usarmos um fillfactor baixo, de 30%,  por exemplo. E garanto que neste caso não veremos &quot;Page Spliiiiiiiiits&quot;.&lt;/p&gt;


&lt;h4&gt;A verdade com 30% de FillFactor&lt;/h4&gt;


&lt;p&gt;Digamos que John , como um bom amigo pensou: &quot;Bom, eu conheço a mala do Luca há muito tempo e sei que ele vai mudar de ideia. Vou deixar um espaço em cada carro, caso ele queira levar mais alguém também&quot;.&lt;/p&gt;


&lt;p&gt;E isto aconteceu, no último minuto Luca chegou. E trouxe sua amiga, Jana. Como John deixou um espaço livre em cada carro, não houve problema em acomodar todo mundo, e todos curtiram o final de semana!&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14819/1.JPG&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Uma coisa já vemos de cara: o número de carros aumentou com o mesmo número de pessoas que iam (antes do Luca e Jana).&lt;/p&gt;


&lt;p&gt;Vamos usar o (script 1) para vermos o que acontece , mas alterando o FillFactor para 30%.&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;if object_id('Testfillfactor') is not null&lt;br /&gt;&lt;br /&gt;    drop table Testfillfactor&lt;br /&gt;&lt;br /&gt;    go&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;create table Testfillfactor    (    id int not null,    &lt;br /&gt;&lt;br /&gt;                                    name char(2000)&lt;br /&gt;&lt;br /&gt;                                )&lt;br /&gt;&lt;br /&gt;create unique clustered index  PK_TestFill on Testfillfactor(id)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;WITH(    PAD_INDEX = OFF,&lt;br /&gt;&lt;br /&gt;        FILLFACTOR = 30,&lt;br /&gt;&lt;br /&gt;        STATISTICS_NORECOMPUTE = OFF,&lt;br /&gt;&lt;br /&gt;        IGNORE_DUP_KEY = OFF,&lt;br /&gt;&lt;br /&gt;        ALLOW_ROW_LOCKS = ON,&lt;br /&gt;&lt;br /&gt;        ALLOW_PAGE_LOCKS = ON&lt;br /&gt;&lt;br /&gt;    )&lt;br /&gt;&lt;br /&gt;ON [PRIMARY]&lt;br /&gt;&lt;br /&gt;go&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;set nocount on&lt;br /&gt;&lt;br /&gt;declare @loop int&lt;br /&gt;&lt;br /&gt;set @loop = 1&lt;br /&gt;&lt;br /&gt;while @loop &amp;lt; 11&lt;br /&gt;&lt;br /&gt;begin&lt;br /&gt;&lt;br /&gt;    insert into TestFillfactor values (@loop,'Name ' + convert(char(10),@loop))&lt;br /&gt;&lt;br /&gt;    set @loop = @loop + 2&lt;br /&gt;&lt;br /&gt;end &lt;br /&gt;&lt;br /&gt;go&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;dbcc ind(dba, Testfillfactor, 1, 0)&lt;br /&gt;&lt;br /&gt;go&lt;br /&gt;&lt;br /&gt;alter index PK_TestFill on Testfillfactor Rebuild &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;E o que a figura abaixo nos diz, sem pensar muito?&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14819/2.JPG&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Uma página de dados foi criada, mas com o mesmo número de linhas com o FillFactor de 100%&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Como podemos ver:&lt;/strong&gt;&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;Três Páginas de Dados (Data Pages)  Coluna PageType = 1, que são as páginas 7430,7406 e 7408;&lt;/li&gt;

&lt;li&gt;Uma Página de Índice (Index Page)  Coluna Pagetype = 2, que é a página 7407;&lt;/li&gt;

&lt;li&gt;Uma IAM Page  - Coluna PageType = 10, que é a página 7431.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Nosso foco são as páginas de dados (Data Pages)&lt;/p&gt;


&lt;p&gt;01. Página 7430&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;Próxima Página de dados (NextPageID) = 7406&lt;/li&gt;

&lt;li&gt;Página de Dados Anterior (PrevPageID) = 0&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;02. Página 7406&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;Próxima Página de dados (NextPageID) = 7408&lt;/li&gt;

&lt;li&gt;Página de Dados Anterior (PrevPageID) = 7430&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;03. Página 7408&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;Próxima Página de dados (NextPageID) = 0&lt;/li&gt;

&lt;li&gt;Página de Dados Anterior (PrevPageID) = 7406&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Com eu criei a coluna fixa (char 2000) devemos ter na página 7430 duas linhas , que seriam o ID 3 e 5. Lembre-se que o índice cluster &lt;br /&gt;sobre o ID.&lt;/p&gt;


&lt;p&gt;Verifcando:&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;dbcc page( 0, 1, 7430, 3)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;span class=&quot;c3&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14819/3.JPG&quot; /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;Como a coluna m_slotCnt me informa o número de linhas que esta página tem (2) e a coluna m_freeCnt o número de bytes livres na página, minha conta novamente está correta. E outra, a página 7406, terá os IDs 5 e 7, e a página 7408 o ID 9.&lt;/p&gt;


&lt;p&gt;Podemos executar este script para visualizar esta afirmação:&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;dbcc traceon( 3604 )       &lt;br /&gt;go&lt;br /&gt;create table #dbccpageresults (    ParentObject varchar(max),&lt;br /&gt;                                [Object] varchar(max),&lt;br /&gt;                                [Field] varchar(max),&lt;br /&gt;                                [Value] varchar(max)&lt;br /&gt;                              )   &lt;br /&gt;insert into #dbccpageresults exec ('dbcc page( 0, 1, 7430, 3 ) with tableresults')                                &lt;br /&gt;go&lt;br /&gt;select 'PAGE 7430',* from #dbccpageresults where [field] = 'id'&lt;br /&gt;go&lt;br /&gt;truncate table #dbccpageresults&lt;br /&gt;go&lt;br /&gt;insert into #dbccpageresults exec ('dbcc page( 0, 1, 7406, 3 ) with tableresults')   &lt;br /&gt;go&lt;br /&gt;select 'PAGE 7406',* from #dbccpageresults where [field] = 'id'&lt;br /&gt;go&lt;br /&gt;truncate table #dbccpageresults&lt;br /&gt;go&lt;br /&gt;insert into #dbccpageresults exec ('dbcc page( 0, 1, 7408, 3 ) with tableresults')   &lt;br /&gt;go&lt;br /&gt;select 'PAGE 7408', * from #dbccpageresults where [field] = 'id'&lt;br /&gt;go&lt;br /&gt;drop table #dbccpageresults&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;span class=&quot;c3&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14819/4.JPG&quot; /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;Ou&lt;/p&gt;


&lt;p&gt;&lt;span class=&quot;c3&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14819/5.JPG&quot; /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;Novamente vamos inserir mais 4 linhas pares :&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;Insert into Testfillfactor  values (2,'Name 2')&lt;br /&gt;go&lt;br /&gt;Insert into Testfillfactor  values (4,'Name 4')&lt;br /&gt;go&lt;br /&gt;Insert into Testfillfactor  values (6,'Name 6')&lt;br /&gt;go&lt;br /&gt;Insert into Testfillfactor  values (8,'Name 8')&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;E vamos rodar o script novamente  :&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;dbcc traceon( 3604 )       &lt;br /&gt;go&lt;br /&gt;create table #dbccpageresults (    ParentObject varchar(max),&lt;br /&gt;                        [Object] varchar(max),&lt;br /&gt;                        [Field] varchar(max),&lt;br /&gt;                        [Value] varchar(max)&lt;br /&gt;                              )   &lt;br /&gt;insert into #dbccpageresults exec ('dbcc page( 0, 1, 7430, 3 ) with tableresults')                                &lt;br /&gt;go&lt;br /&gt;select 'PAGE 7430',* from #dbccpageresults where [field] = 'id'&lt;br /&gt;go&lt;br /&gt;truncate table #dbccpageresults&lt;br /&gt;go&lt;br /&gt;insert into #dbccpageresults exec ('dbcc page( 0, 1, 7406, 3 ) with tableresults')   &lt;br /&gt;go&lt;br /&gt;select 'PAGE 7406',* from #dbccpageresults where [field] = 'id'&lt;br /&gt;go&lt;br /&gt;truncate table #dbccpageresults&lt;br /&gt;go&lt;br /&gt;insert into #dbccpageresults exec ('dbcc page( 0, 1, 7408, 3 ) with tableresults')   &lt;br /&gt;go&lt;br /&gt;select 'PAGE 7408', * from #dbccpageresults where [field] = 'id'&lt;br /&gt;go&lt;br /&gt;drop table #dbccpageresults&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;O resultado será :&lt;/p&gt;


&lt;p&gt;&lt;span class=&quot;c3&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14819/6.JPG&quot; /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;As páginas estão contínuas, e o engine não precisa pular de uma página a outra para achar os dados.&lt;/p&gt;


&lt;p&gt;Não temos Page Split!!!!&lt;/p&gt;


&lt;p&gt;Será?&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;dbcc ind(dba, Testfillfactor, 1, 0)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14819/7.JPG&quot; /&gt;&lt;/p&gt;


&lt;p&gt;O resultado é o mesmo! Vamos mais a fundo:&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;dbcc page( 0, 1, 7430, 1 )&lt;br /&gt;go&lt;br /&gt;dbcc page( 0, 1, 7406, 1 )&lt;br /&gt;go&lt;br /&gt;dbcc page( 0, 1, 7408, 1 )&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;span class=&quot;c3&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14819/8.JPG&quot; /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;E representando:&lt;/p&gt;


&lt;p&gt;&lt;span class=&quot;c3&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14819/9.JPG&quot; /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;&quot;Eu não vejo Page Spliiiiiiitttsssss&quot;&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Qual é a mágica?&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Não tem mágica. É matemática pura. Temos espaço em cada página para acomodar os dados novos!&lt;/p&gt;


&lt;h4&gt;O Paradigma FillFactor III - Índice Cluster+Identity = 100% FillFactor sem Page Splits?&lt;/h4&gt;


&lt;p&gt;É comum encontrarmos em fóruns a seguinte pergunta (ou parecida com ela): &quot;tenho um CIX (clustered index) no campo identity. Posso deixar com FillFactor de 100%?&quot;. E a resposta tradicional: &quot;Sim. Já que os dados são inseridos no final da página, não haverá page splits&quot;.&lt;/p&gt;


&lt;p&gt;A resposta correta na verdade é &lt;strong&gt;DEPENDE&lt;/strong&gt;.&lt;/p&gt;


&lt;p&gt;Depende dos tipos de colunas que terão sua tabela e, principalmente, se elas terão atualizações no tamanho do seu conteúdo (varchar, nvarchar, varbinary). &lt;/p&gt;


&lt;p&gt;Page Split ocorre no update também.&lt;/p&gt;


&lt;p&gt;Vamos lá?&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;if object_id('Testfillfactor') is not null&lt;br /&gt;    drop table Testfillfactor&lt;br /&gt;    go&lt;br /&gt;&lt;br /&gt;create table Testfillfactor    (    id int identity(1,1) not null,    &lt;br /&gt;                                    name1 varchar(1000),&lt;br /&gt;                                    name2 varchar(1000),&lt;br /&gt;                                    name3 varchar(1000),&lt;br /&gt;                                    name4 varchar(1000),&lt;br /&gt;                                    name5 varchar(1000),&lt;br /&gt;                                    name6 varchar(1000),&lt;br /&gt;                                    name7 varchar(1000),&lt;br /&gt;                                    name8 varchar(1000),&lt;br /&gt;                                )&lt;br /&gt;create unique clustered index  PK_TestFill on Testfillfactor(id)&lt;br /&gt;&lt;br /&gt;WITH(    PAD_INDEX = OFF,&lt;br /&gt;        FILLFACTOR = 100,&lt;br /&gt;        STATISTICS_NORECOMPUTE = OFF,&lt;br /&gt;        IGNORE_DUP_KEY = OFF,&lt;br /&gt;        ALLOW_ROW_LOCKS = ON,&lt;br /&gt;        ALLOW_PAGE_LOCKS = ON&lt;br /&gt;    )&lt;br /&gt;ON [PRIMARY]&lt;br /&gt;go&lt;br /&gt;&lt;br /&gt;set nocount on&lt;br /&gt;declare @loop int&lt;br /&gt;set @loop = 1&lt;br /&gt;while @loop &amp;lt; 50&lt;br /&gt;begin&lt;br /&gt;    insert into TestFillfactor(name1,name2,name3,name4,name5,name6,name7,name8)&lt;br /&gt;    values ('Name 1 ' + convert(char(10),@loop),'Name 2 ' + convert(char(10),@loop),'Name 3 ' + convert(char(10),@loop),&lt;br /&gt;    'Name 4 ' + convert(char(10),@loop),'Name 5 ' + convert(char(10),@loop),'Name 6 ' + convert(char(10),@loop),'Name 7 ' + convert(char(10),@loop),&lt;br /&gt;    'Name 8 ' + convert(char(10),@loop))&lt;br /&gt;    set @loop = @loop + 1&lt;br /&gt;end &lt;br /&gt;go&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Criamos uma tabela com tipos de dados variante. Reparem que os campos nome são varchar. Populamos com 50 linhas contínuas (identity , com CIX no campo ID). Teoricamente não haveria Pages Splits. &lt;/p&gt;


&lt;p&gt;Será?&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;alter index PK_TestFill on Testfillfactor Rebuild&lt;br /&gt;go&lt;br /&gt;dbcc ind(dba, Testfillfactor, 1, 0)&lt;br /&gt;go&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;span class=&quot;c3&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14819/10.JPG&quot; /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;Correto. Não vemos &quot;Page SPLIIIIIIIIITTSSS&quot;. MAS, vamos alterar o conteúdo dos campos nome?&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;update Testfillfactor set    name1 = REPLICATE('X',1000),&lt;br /&gt;                            name2 = REPLICATE('X',1000),&lt;br /&gt;                            name3 = REPLICATE('X',1000),&lt;br /&gt;                            name4 = REPLICATE('X',1000),&lt;br /&gt;                            name5 = REPLICATE('X',1000),&lt;br /&gt;                            name6 = REPLICATE('X',1000),&lt;br /&gt;                            name7 = REPLICATE('X',1000),&lt;br /&gt;                            name8 = REPLICATE('X',1000)&lt;br /&gt;go&lt;br /&gt;dbcc ind(dba, Testfillfactor, 1, 0)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Vendo como ficou: &lt;/p&gt;


&lt;p&gt;&lt;span class=&quot;c3&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14819/11.JPG&quot; /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Uma CIVILIZAÇÂO DE PAGE SPLITS!&lt;/strong&gt;&lt;br /&gt;&lt;br /&gt;Certeza, Laerte? Apesar de o campo NextPageId já me mostrar isso, mas vamos ver:&lt;/p&gt;


&lt;p&gt;Peguei a Página 7548. O próximo deveria ser a Página 7549.&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;create table #dbccpageresults (    ParentObject varchar(max),&lt;br /&gt;                        [Object] varchar(max),&lt;br /&gt;                        [Field] varchar(max),&lt;br /&gt;                        [Value] varchar(max)&lt;br /&gt;                      )   &lt;br /&gt;insert into #dbccpageresults exec ('dbcc page( 0, 1, 7548, 3 ) with tableresults')   &lt;br /&gt;&lt;br /&gt;select * from #dbccpageresults where [field] = 'id'&lt;br /&gt;go&lt;br /&gt;truncate table #dbccpageresults&lt;br /&gt;go&lt;br /&gt;insert into #dbccpageresults exec ('dbcc page( 0, 1, 7549, 3 ) with tableresults')   &lt;br /&gt;go&lt;br /&gt;select * from #dbccpageresults where [field] = 'id'&lt;br /&gt;go&lt;br /&gt;drop table #dbccpageresults&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;span class=&quot;c3&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14819/12.JPG&quot; /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Errado!&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Segundo o NextPageID, a Próxima Página é a 8174, que está lá embaixo e nem aparece na imagem por não caber!&lt;/p&gt;


&lt;p&gt;Pegando as páginas 7548 e 8174:&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;create table #dbccpageresults (    ParentObject varchar(max),&lt;br /&gt;                        [Object] varchar(max),&lt;br /&gt;                        [Field] varchar(max),&lt;br /&gt;                        [Value] varchar(max)&lt;br /&gt;                      )   &lt;br /&gt;insert into #dbccpageresults exec ('dbcc page( 0, 1, 7548, 3 ) with tableresults')   &lt;br /&gt;&lt;br /&gt;select * from #dbccpageresults where [field] = 'id'&lt;br /&gt;go&lt;br /&gt;truncate table #dbccpageresults&lt;br /&gt;go&lt;br /&gt;insert into #dbccpageresults exec ('dbcc page( 0, 1, 8174, 3 ) with tableresults')   &lt;br /&gt;go&lt;br /&gt;select * from #dbccpageresults where [field] = 'id'&lt;br /&gt;go&lt;br /&gt;drop table #dbccpageresults&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;span class=&quot;c3&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14819/13.JPG&quot; /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;Agora sim! Ou seja da página 7548 pulou para a página 8174 para continuar a ordem do CIX (clustered index)!&lt;/p&gt;


&lt;p&gt;Ou seja, a afirmação que só basta colocar o CIX no identity e pode deixar o FillFactor com 100% que não terá Page Splits é &lt;strong&gt;LENDA&lt;/strong&gt;. Além do que, fazendo isso, se sua tabela tiver uma carga alta de inserts, você poderá criar um hotspot (muita inserção ao mesmo tempo).&lt;/p&gt;


&lt;p&gt;Se sua tabela tem campos de tamanho variável (varchar, nvarchar, varbinary) e estes são altamente atualizados (seja com valores maiores ou menores) provavelmente você terá Page Split.&lt;/p&gt;


&lt;p&gt;Se sua tabela tem campos de tamanho variável (varchar, nvarchar, varbinary) e estes NÃO são atualizados ou não tem, a colocação do CIX no identity e FillFactor de 100% parece ser mais viável.&lt;/p&gt;


&lt;h4&gt;O Paradigma FillFactor IV - Muito cuidado!&lt;/h4&gt;


&lt;p&gt;Espera, deixa eu ver se entendi. &lt;strong&gt;NESTE CASO&lt;/strong&gt;:&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;Com FillFactor de 100% o Engine acomoda 4 linhas na página. Eu tenho um menor número de páginas e Page Split.&lt;/li&gt;

&lt;li&gt;Com FillFactor de 30% o Engine acomoda 2 linhas na página. Eu tenho um número maior de páginas, mas não tenho Page Split.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Mas um número maior de páginas não quer dizer mais Logical Reads?&lt;/p&gt;


&lt;p&gt;CORRETO!&lt;/p&gt;


&lt;p&gt;Vamos ver se é verdade?&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Primeiro, com 30% de FillFactor:&lt;/strong&gt;&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;if object_id('Testfillfactor') is not null&lt;br /&gt;    drop table Testfillfactor&lt;br /&gt;    go&lt;br /&gt;&lt;br /&gt;create table Testfillfactor    (    id int not null,     &lt;br /&gt;                                    name char(2000) &lt;br /&gt;                                )&lt;br /&gt;create unique clustered index  PK_TestFill on Testfillfactor(id) &lt;br /&gt;&lt;br /&gt;WITH(    PAD_INDEX = OFF, &lt;br /&gt;        FILLFACTOR = 30, &lt;br /&gt;        STATISTICS_NORECOMPUTE = OFF, &lt;br /&gt;        IGNORE_DUP_KEY = OFF, &lt;br /&gt;        ALLOW_ROW_LOCKS = ON, &lt;br /&gt;        ALLOW_PAGE_LOCKS = ON&lt;br /&gt;    ) &lt;br /&gt;ON [PRIMARY]&lt;br /&gt;Go&lt;br /&gt;go &lt;br /&gt;alter index PK_TestFill on Testfillfactor Rebuild &lt;br /&gt;go&lt;br /&gt;set nocount on&lt;br /&gt;declare @loop int&lt;br /&gt;set @loop = 1&lt;br /&gt;while @loop &amp;lt; 10000&lt;br /&gt;begin&lt;br /&gt;    insert into TestFillfactor values (@loop,'Name ' + convert(char(10),@loop))&lt;br /&gt;    set @loop = @loop + 1&lt;br /&gt;end  &lt;br /&gt;go &lt;br /&gt;set statistics io on&lt;br /&gt;go&lt;br /&gt;select ID from TestFillfactor where id = 9999&lt;br /&gt;go&lt;br /&gt;set statistics io off&lt;br /&gt;&lt;br /&gt;Table&lt;br /&gt;'Testfillfactor'. Scan count 1, logical reads 55, physical reads 0,&lt;br /&gt;read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob&lt;br /&gt;read-ahead reads 0.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Agora, com 100% de FillFactor:&lt;/strong&gt;&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;if object_id('Testfillfactor') is not null&lt;br /&gt;    drop table Testfillfactor&lt;br /&gt;    go&lt;br /&gt;&lt;br /&gt;create table Testfillfactor    (    id int not null,    &lt;br /&gt;                                    name char(2000)&lt;br /&gt;                                )&lt;br /&gt;create unique clustered index  PK_TestFill on Testfillfactor(id)&lt;br /&gt;&lt;br /&gt;WITH(    PAD_INDEX = OFF,&lt;br /&gt;        FILLFACTOR = 100,&lt;br /&gt;        STATISTICS_NORECOMPUTE = OFF,&lt;br /&gt;        IGNORE_DUP_KEY = OFF,&lt;br /&gt;        ALLOW_ROW_LOCKS = ON,&lt;br /&gt;        ALLOW_PAGE_LOCKS = ON&lt;br /&gt;    )&lt;br /&gt;ON [PRIMARY]&lt;br /&gt;go&lt;br /&gt;alter index PK_TestFill on Testfillfactor Rebuild&lt;br /&gt;go&lt;br /&gt;&lt;br /&gt;set nocount on&lt;br /&gt;declare @loop int&lt;br /&gt;set @loop = 1&lt;br /&gt;while @loop &amp;lt; 10000&lt;br /&gt;begin&lt;br /&gt;    insert into TestFillfactor values (@loop,'Name ' + convert(char(10),@loop))&lt;br /&gt;    set @loop = @loop + 1&lt;br /&gt;end &lt;br /&gt;go&lt;br /&gt;set statistics io on&lt;br /&gt;go&lt;br /&gt;select ID from TestFillfactor where id = 9999&lt;br /&gt;go&lt;br /&gt;set statistics io off&lt;br /&gt;&lt;br /&gt;Table 'Testfillfactor'. Scan count 1, logical reads 30, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;É verdade!&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Baixo FillFactor --&amp;gt; Alto Page Reads&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;Alto FillFactor  --&amp;gt; Baixo Page Reads&lt;/strong&gt; &lt;/p&gt;


&lt;p&gt;Se formos pela lógica: &lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Muito Insert, update = Baixo FillFactor&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;Muito Select = Alto FillFactor&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Mas sabemos que ná prática não existe regra. Cada ambiente é diferente do outro e possui características próprias, seja pela regra de negócio envolvida, seja por limitação de infra (falta de janela de reindexação) ou alta disponibilidade. &lt;/p&gt;


&lt;p&gt;Algumas vezes é interessante deixar um alto Fillfactor para uma tabela de muito insert e update, mas também esta tabela tem muito select, pois alimenta um relatório muito importante pra sua organização e é chamada muitas vezes ao dia... Neste caso, reindexar ONLINE (ou reorganizar) mais vezes este índice pode ser mais vantajoso. &lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Eu tenho certeza disso? Claro que não.&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Mas eu faço algumas coisas que me ajudam na análise e definição da solução:&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;Analise a carga de seus índices separadamente (cada um deles tem uma caraterística diferente)&lt;/li&gt;

&lt;li&gt;Identifique as consultas que usam as tabelas destes índices (DMVs são uma boa ajuda)&lt;/li&gt;

&lt;li&gt;Converse com o pessoal de Negócios (analista de negócios) e entenda o &quot;produto final&quot; do uso destas consultas. Por exemplo, um relatório de vendas online por filial&lt;/li&gt;

&lt;li&gt;Crie suas próprias métricas de controle e organização. Um baseline não serve somente para dados macros do servidor&lt;/li&gt;

&lt;li&gt;Se o seu problema é um caso particular, crie um baseline para este processo. Como você poderá saber se melhorou ou ficou pior com seus ajustes se você não sabe com era o comportamento dele antes, durante e depois?&lt;/li&gt;

&lt;li&gt;Entenda o Produto (SQL Server)&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Nada melhor que você entender um pouco como funciona internamente o que está acontecendo. Isto lhe dará uma visão clara e objetiva para definir sua solução.&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Exemplo de ótimas leituras:&lt;/strong&gt;&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;Inside Microsoft SQL Server 2005: T-SQL Querying &lt;/li&gt;

&lt;li&gt;Inside Microsoft SQL Server 2005: The Storage Engine&lt;/li&gt;

&lt;li&gt;Inside Microsoft SQL Server 2005: Query Tuning and Optimization&lt;/li&gt;

&lt;li&gt;E os mesmos livros no  SQL SERVER 2008&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Não tenha medo de dizer &quot;eu não sei, mas vou ver e aprender&quot;. Isso é muito melhor do que dizer &quot;eu não sei... sempre foi assim... aprendi assim&quot;. Na verdade você esta dizendo &quot;eu não sei, não quero saber e não tenho a mínima vontade de aprender&quot; e isso não soa bem.&lt;/p&gt;


&lt;p&gt;Quando tiver todo controle da situação: teste. Teste muito. Canse de testar antes de dizer &quot;o jeito de fazer é assim&quot;. As pessoas esperam isso de você. &lt;/p&gt;


&lt;p&gt;A decisão  correta é &quot;faça assim. Eu sei o que estou falando!&quot;.&lt;/p&gt;


&lt;h4&gt;Conclusão&lt;/h4&gt;


&lt;p&gt;Vimos que o FillFactor pode ser o herói ou vilão da história. &lt;/p&gt;


&lt;p&gt;Teste, implemente com cuidado.&lt;/p&gt;


&lt;p&gt;Trate cada objeto e entidade do seu banco de dados como única e sendo assim o comportamento é diferente uma das outras.&lt;/p&gt;


&lt;p&gt;E não olhe para Paradigmas. Especialmente em TI.&lt;/p&gt;


&lt;p&gt;Até a próxima!&lt;/p&gt;

</description>
            <author>laertejuniordba@hotmail.com (Laerte Poltronieri Junior)</author>
            <pubDate>Tue, 03 Nov 2009 11:15:00 +0100</pubDate>
            <guid>http://imasters.uol.com.br/artigo/14819</guid>
        </item>
        <item>
            <title>O paradigma FillFactor - Parte 01</title>
            <link>http://imasters.uol.com.br/artigo/14751/sql_server/o_paradigma_fillfactor_parte_01/</link>
            <description>&lt;p&gt;Um grupo de cientistas colocou cinco macacos numa jaula. No centro dela colocaram uma escada e, sobre ela, um cacho de bananas. Quando um macaco subia a escada para apanhar as bananas, os cientistas lançavam um jato de água fria nos que estavam no chão. &lt;/p&gt;


&lt;p&gt;Depois de certo tempo, quando um macaco ia subir a escada, os outros o enchiam de pancada. Passado mais algum tempo, mais nenhum macaco subia a escada, apesar da tentação das bananas. Então, os cientistas substituíram um dos cinco macacos.&lt;/p&gt;


&lt;p&gt;A primeira coisa que ele fez foi subir a escada e foi rapidamente retirado pelos outros, que lhe bateram. Depois de algumas surras, o novo integrante do grupo não subia mais a escada. &lt;/p&gt;


&lt;p&gt;Um segundo foi substituído, e o mesmo ocorreu, tendo o primeiro substituto participado, com entusiasmo, na surra ao novato. Um terceiro foi trocado, e repetiu-se o fato. Um quarto e, finalmente, o último dos veteranos foi substituído. &lt;/p&gt;


&lt;p&gt;Os cientistas ficaram, então, com um grupo de cinco macacos que, mesmo nunca tendo tomado um banho frio, continuavam a bater naquele que tentasse chegar às bananas. &lt;/p&gt;


&lt;p&gt;&lt;strong&gt;Moral da História&lt;/strong&gt; &lt;/p&gt;


&lt;p&gt;Se fosse possível perguntar a algum deles por que batiam em quem tentasse subir a escada, com certeza a resposta seria: &lt;/p&gt;


&lt;blockquote&gt;&quot;Não sei, as coisas sempre foram assim por aqui&quot;&lt;/blockquote&gt;


&lt;h4&gt;O Paradigma FillFactor&lt;/h4&gt;


&lt;p&gt;Tive a oportunidade de participar em um projeto em que a empresa tinha um DBA Junior. Em determinada análise que estávamos fazendo, uma análise e reorganização dos índices com base na carga de cada um, surgiu o diálogo:&lt;/p&gt;


&lt;blockquote&gt;- Essa tabela eu conheço é muito Insert . Pode colocar um Fillfactor baixo!&lt;br /&gt;- Por que?, perguntei eu.&lt;br /&gt;- Eu não sei, aprendi assim e todo mundo fala isso...&lt;/blockquote&gt;


&lt;h4&gt;O paradigma FillFactor I  &quot;A existência do page split&quot;&lt;/h4&gt;


&lt;p&gt;Continuando a minha pergunta, questionei-o sobre Page Split e a resposta foi:&lt;/p&gt;


&lt;blockquote&gt;&quot;Ah, isso eu sei, é fácil. É quando o SQL SERVER fica parecendo pipoca pulando de um lado para outro para achar os dados.&quot;&lt;/blockquote&gt;


&lt;p&gt;De uma certa maneira ele não estava errado e eu, particularmente, adorei o &quot;pulando feito pipoca&quot;. Mas, sinceramente, essa era uma resposta que eu preferia não ter ouvido. Imagine se fosse alguém do Storage Team...&lt;/p&gt;


&lt;p&gt;&lt;strong&gt;O que é Page Split?&lt;/strong&gt;&lt;/p&gt;


&lt;p&gt;Nós sabemos que o SQL SERVER trabalha com páginas de 8kb e cada página possui um fator de preenchimento, chamado fillfactor. Por default este fator é 0, ou seja, página totalmente preenchida e sem espaço livre para novos registros.&lt;/p&gt;


&lt;p&gt;Quando um registro é inserido ou atualizado, este registro tem que ser armazenado no espaço da página. Se a página não possui espaço livre, o SQL Server precisa reorganizar todas as páginas para armazenar o novo registro.&lt;/p&gt;


&lt;p&gt;Mas estas novas páginas não são contínuas.&lt;/p&gt;


&lt;p&gt;O &lt;strong&gt;FillFactor&lt;/strong&gt; é aplicado em um índice quando este é criado ou reorganizado e informa o quanto cada página do nível folha será preenchida. &lt;strong&gt;Page Split&lt;/strong&gt; é um processo interno que o Engine usa para acomodar os dados novos (ou quando mudamos o tamanho da informação em um campo varchar, por exemplo). Este processo consome uma carga considerável de I/O, tendo em vista que o Engine tem que ler a IAM (Index allocation Map), alocar novas páginas, mover os dados na ordem correta do índice etc. O FillFactor é mais efetivo no índice cluster, mas pode ser aplicado nos índices não cluster.&lt;/p&gt;


&lt;h4&gt;O Paradigma FillFactor II  &quot;A verdade está lá dentro&quot;&lt;/h4&gt;


&lt;ul&gt;&lt;li&gt;&lt;span class=&quot;c1&quot;&gt;&lt;strong&gt;A verdade com 100% de FillFactor&lt;/strong&gt;&lt;/span&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Simplificando:&lt;/p&gt;


&lt;p&gt;John, Robert, Phil, Luca, Lucy, Chloe, Hannah and Julia decidiram ir à praia. John era o organizador e resolveu colocar homens em um carro e mulheres em outro. Um dia antes da viagem, Luca resolveu não ir e Richard foi convidado. O layout ficou parecido com esse :&lt;/p&gt;


&lt;p&gt;&lt;span class=&quot;c3&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14751/1.jpg&quot; /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;No último minuto, Luca decidiu ir, mas não havia espaço no carro e, como a ordem era carro dos homens primeiro e mulheres depois, ele teve que ir com o seu.&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14751/2.jpg&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Se John quisesse falar com Luca, ele tinha que pular o carro das garotas e, o pior, se ele quisesse falar com Lucy , ele tinha que pedir pro Luca, pois ele era o único a ter o número do celular dela.&lt;/p&gt;


&lt;p&gt;Se isto é custoso para John, imagine pro Engine do SQL Server....&lt;/p&gt;


&lt;p&gt;Na verdade, aqui temos um Page Split  &quot;praiano.&quot;&lt;/p&gt;


&lt;p&gt;Vamos ver o que acontece:&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;if object_id('Testfillfactor') is not null&lt;br /&gt;drop table Testfillfactor&lt;br /&gt;go&lt;br /&gt;create table Testfillfactor ( id int not null,&lt;br /&gt;name char(2000)&lt;br /&gt;)&lt;br /&gt;create unique clustered index PK_TestFill on Testfillfactor(id)&lt;br /&gt;WITH( PAD_INDEX = OFF,&lt;br /&gt;FILLFACTOR = 100,&lt;br /&gt;STATISTICS_NORECOMPUTE = OFF,&lt;br /&gt;IGNORE_DUP_KEY = OFF,&lt;br /&gt;ALLOW_ROW_LOCKS = ON,&lt;br /&gt;ALLOW_PAGE_LOCKS = ON&lt;br /&gt;)&lt;br /&gt;ON [PRIMARY]&lt;br /&gt;go&lt;br /&gt;set nocount on&lt;br /&gt;declare @loop int&lt;br /&gt;set @loop = 1&lt;br /&gt;while @loop &amp;lt; 11&lt;br /&gt;begin&lt;br /&gt;insert into TestFillfactor values (@loop,'Name ' + convert(char(10),@loop))&lt;br /&gt;set @loop = @loop + 2&lt;br /&gt;end&lt;br /&gt;go&lt;br /&gt;dbcc ind(dba, Testfillfactor, 1, 0)&lt;br /&gt;go&lt;br /&gt;alter index PK_TestFill on Testfillfactor Rebuild&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;span class=&quot;c4&quot;&gt;Script 1&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;Este script nos gera um page split e o resultado é :&lt;/p&gt;


&lt;p&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14751/3.jpg&quot; /&gt;&lt;/p&gt;


&lt;p&gt;Como a gente pode ver :&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;Duas Páginas de Dados (Data Pages) - Coluna PageType = 1 que são as páginas 7412 e 7414&lt;/li&gt;

&lt;li&gt;Uma Página de Índice (Index Page) - Coluna Pagetype = 2 que é a página 7426&lt;/li&gt;

&lt;li&gt;Uma IAM Page - Coluna PageType = 10 que é a página 7413&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Nosso foco são as páginas de dados (Data Pages)&lt;/p&gt;


&lt;p&gt;01. Página 7412&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;Próxima Página de dados (NextPageID) = 7414&lt;/li&gt;

&lt;li&gt;Página de Dados Anterior (PrevPageID) = 0&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;02. Página 7414&lt;/p&gt;


&lt;ul&gt;&lt;li&gt;Próxima Página de dados (NextPageID) = 0&lt;/li&gt;

&lt;li&gt;Página de Dados Anterior (PrevPageID) = 7412&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Como eu criei a tabela com um valor fixo (char 2000), deverá caber na página 7412 quatro linhas, que devem ser o ID 1, 3, 5 e 7.&lt;/p&gt;


&lt;p&gt;Para verificar usamos:&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;dbcc page( 0, 1, 7412, 3)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;span class=&quot;c3&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14751/4.jpg&quot; /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;Como a coluna m_slotCnt me informa o número de linhas que esta página tem (4) e a coluna m_freeCnt o número de bytes livres na página, minha conta está correta.&lt;/p&gt;


&lt;p&gt;Podemos executar este script para visualizar esta afirmação:&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;create table #dbccpageresults ( ParentObject varchar(max),&lt;br /&gt;[Object] varchar(max),&lt;br /&gt;[Field] varchar(max),&lt;br /&gt;[Value] varchar(max)&lt;br /&gt;)&lt;br /&gt;insert into #dbccpageresults exec ('dbcc page( 0, 1, 7412, 3 ) with tableresults')&lt;br /&gt;select * from #dbccpageresults where [field] = 'id'&lt;br /&gt;go&lt;br /&gt;drop table #dbccpageresults&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;span class=&quot;c3&quot;&gt;&lt;br /&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14751/5.jpg&quot; /&gt;&lt;/span&gt; &lt;/p&gt;


&lt;p&gt;Correto. Na página 7412 nós temos os IDs 1, 3, 5 e 7&lt;/p&gt;


&lt;p&gt;Na página 7414 nós temos somente uma linha que é o ID 9, sobrando espaço não pelo FillFactor, e sim porque não tivemos dados suficientes para preenchê-la.&lt;/p&gt;


&lt;p&gt;Usando o script acima, podemos confirmar:&lt;/p&gt;


&lt;p&gt;&lt;span class=&quot;c3&quot;&gt;&lt;br /&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14751/6.jpg&quot; /&gt;&lt;/span&gt; &lt;/p&gt;


&lt;p&gt;E representando:&lt;/p&gt;


&lt;p&gt;&lt;span class=&quot;c3&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14751/7.jpg&quot; /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;Agora vamos inserir mais 4 linhas pares (no script inserimos linhas ímpares). Lembre-se de que o índice cluster é sobre o ID&lt;/p&gt;


&lt;div class=&quot;codigo&quot;&gt;&lt;pre&gt;&lt;code&gt;Insert into Testfillfactor values (2,'Name 2')&lt;br /&gt;go&lt;br /&gt;Insert into Testfillfactor values (4,'Name 4')&lt;br /&gt;go&lt;br /&gt;Insert into Testfillfactor values (6,'Name 6')&lt;br /&gt;go&lt;br /&gt;Insert into Testfillfactor values (8,'Name 8')&lt;br /&gt;go&lt;br /&gt;dbcc ind(dba, Testfillfactor, 1, 0)&lt;br /&gt;go&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;&lt;span class=&quot;c3&quot;&gt;&lt;br /&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14751/8.jpg&quot; /&gt;&lt;/span&gt; &lt;/p&gt;


&lt;p&gt;Como podemos ver, uma nova página de dados (Data Page) foi inserida e nós temos o Page Split.&lt;/p&gt;


&lt;p&gt;Isso me lembra o Haley Joel Osment no filme Sexto Sentido: &quot;Eu Vejo Page Spliiiiiiiiitsssss!!!!&quot;&lt;/p&gt;


&lt;p&gt;Perceba que pelas colunas NextPageID e PrevPageID, o Engine tem que ir da página 7412 para a 7427 e depois para a 7414 para completar o ciclo da informações.&lt;/p&gt;


&lt;p&gt;Representando:&lt;/p&gt;


&lt;p&gt;&lt;span class=&quot;c3&quot;&gt;&lt;img alt=&quot;&quot; src=&quot;http://conteudo.imasters.uol.com.br/14751/9.jpg&quot; /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;


&lt;p&gt;Este &quot;pulando feito pipoca&quot; é uma operação de I/O muito pesada pro Engine.&lt;/p&gt;


&lt;p&gt;Com isso verificamos como é a ocorrência de Page Split e internamente representada.&lt;/p&gt;


&lt;p&gt;Na continuação deste artigo veremos como será se usarmos um fillfactor baixo, de 30%, por exemplo. Até lá.&lt;/p&gt;

</description>
            <author>laertejuniordba@hotmail.com (Laerte Poltronieri Junior)</author>
            <pubDate>Tue, 27 Oct 2009 10:30:00 +0100</pubDate>
            <guid>http://imasters.uol.com.br/artigo/14751</guid>
        </item>
    </channel>
</rss>
