Atualmente meu notebook (também conhecido como desktop porque a bateria já era) não tem muita capacidade de suportar alguns testes,,, (Acer Aspire 5050 – AMD Turion 1 core, 1.9GB RAM, 35GB HD),,,
Então tentei fazer uma coisa interessante,,,, criar um banco e apontar os arquivos para meu storage (QNAP TS-110),,,
Tenho 2 instâncias de SQL instaladas nesse note, 1 SQL Server 2008 R2 e 1 SQL Server 2005.
Claro que no 2008 R2 funcionou e no 2005 não…
Ai lembrei que para o SQL 2005 (e para o 2008 sem ser R2) criar uma base em local UNC eu precisava habilitar a trace flag 1807. Feito isso conseguir criar a base sem problema.
Achei interessante relembrar isso, pois vai que uma hora qualquer alguém precisa, ou para fazer um LAB ou por falta de espaço (meu caso) você sempre tem uma alternativa… e muita gente nem lembra dessa possibilidade…
Dessa vez o anfitrião do T-SQL Tuesday é o próprio idealizador Adam Machanic (Blog | Twitter).
E de uma forma diferente, não é na Terça-feira,,, por quê?
Porque não importa, desde que funcione pode ser zuado mesmo,,, o tópico desse mês é sobre “mostrar seu código lixo para o mundo”,,,
Todo mundo já escreveu um código uma vez ou outra,,,, todo o DBA tem aquele conjunto de scripts que o ajudam a identificar alguns problemas, fazer um tunning, arrumar alguma coisa,,, E com certeza todo mundo tem aquele código que hoje, pega para olha e começa a dar risada de como é que teve coragem de fazer uma coisa como aquela…
Meu exemplo é bem simples, O código é horrível, demora demais mas, funciona,,,
Ele troca os dados de posição de uma coluna específica.
WITH cteTableTel AS (
SELECT
ROW_NUMBER() OVER (ORDERBY NEWID()) AS n,
SOBRENOME
FROM CLIENTES
)
UPDATE CLIENTES
SET ENDERECO = (
SELECT ENDERECO
FROM cteTableTel
WHERE cteTableTel.n = CLIENTES.ID)
Esse código funciona, não é nada bonito, não é performático, mas para a necessidade de uma base de treino com dados reais ele ajuda bastante,,,
Se você percebeu ele tem um problema,,, sabe qual é?
Como você garante que todas as linhas da tabela de clientes realmente estão em uma sequencia? Você pode ter apagado algum registro uma hora ou outra,,, Logo, haverá cliente que o endereço não vai ser atualizado,,
Já sei, você nem reparou nesse problema,,, você deve estar pensando: “por que ele esta fazendo um update na tabela de clientes se ele está usando uma CTE?”, ou também, “Legal, ele está fazendo isso em uma tabela que tem algum tipo de ID e quando tem aquelas tabelas que não da pra usar nada de referencia?”
Como eu disse, ele não é performático, e eu nem tinha me atentado nesses detalhes na época,,, Houveram duas situações que me refizeram rever esse código: o primeiro caso dos usuários que não tinham os dados alteradores e o último… ai com muita vontade resolvi reescrever,,,
E ele ficou mais ou menos assim:
;WITH cte AS (
SELECT
ROW_NUMBER() OVER (ORDERBY NEWID()) AS n1,
ROW_NUMBER() OVER (orderby SOBRENOME) AS n2,
SOBRENOME
FROM CLIENTES
)
UPDATE c1
SET SOBRENOME = c2.SOBRENOME
from cte as c1
innerjoin cte as c2
on c1.n1 = c2.n2
Dessa vez não tem problema com o usuário sem o dado alterado, performance melhorada em quase 90% e pouco importa se a tabela em algum registro para referencia,,,
Eu comparei a execução dos 2 códigos em uma tabela com um pouco mais de 3 milhões de linhas e o segundo código demorou quase 4 horas para finalizar,,, já o primeiro código eu parei a execução dele depois de 2 SEMANAS executando.
Tenho códigos piores no meu repositório? claro que sim. Esse é apenas um exemplo de um dos piores que já fiz…
Então, segue um script rapidão que mostra as conversões,,, o chato desse script é que ele tem que ser executado por banco,,,
Vou tentar montar um outro que traga a informação de todos os bancos,,,
SETTRANSACTIONISOLATIONLEVELREAD UNCOMMITTED
DECLARE @dbname SYSNAME
SET @dbname = QUOTENAME(DB_NAME());
WITH XMLNAMESPACES
(DEFAULT'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
SELECT
stmt.value('(@StatementText)[1]', 'varchar(max)'),
t.value('(ScalarOperator/Identifier/ColumnReference/@Schema)[1]', 'varchar(128)'),
t.value('(ScalarOperator/Identifier/ColumnReference/@Table)[1]', 'varchar(128)'),
t.value('(ScalarOperator/Identifier/ColumnReference/@Column)[1]', 'varchar(128)'),
ic.DATA_TYPE AS ConvertFrom,
ic.CHARACTER_MAXIMUM_LENGTH AS ConvertFromLength,
t.value('(@DataType)[1]', 'varchar(128)') AS ConvertTo,
t.value('(@Length)[1]', 'int') AS ConvertToLength,
query_plan
FROM sys.dm_exec_cached_plans AS cp
CROSS APPLY sys.dm_exec_query_plan(plan_handle) AS qp
CROSS APPLY query_plan.nodes('/ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple') AS batch(stmt)
CROSS APPLY stmt.nodes('.//Convert[@Implicit="1"]') AS n(t)
JOIN INFORMATION_SCHEMA.COLUMNS AS ic
ON QUOTENAME(ic.TABLE_SCHEMA) = t.value('(ScalarOperator/Identifier/ColumnReference/@Schema)[1]', 'varchar(128)')
AND QUOTENAME(ic.TABLE_NAME) = t.value('(ScalarOperator/Identifier/ColumnReference/@Table)[1]', 'varchar(128)')
AND ic.COLUMN_NAME = t.value('(ScalarOperator/Identifier/ColumnReference/@Column)[1]', 'varchar(128)')
WHERE t.exist('ScalarOperator/Identifier/ColumnReference[@Database=sql:variable("@dbname")][@Schema!="[sys]"]') = 1
Neste mês o tema escolhido pelo Sr. Amit Banerjee (blog | twitter) foi: Melhores Práticas,,,
A algum tempo atrás escrevi um post com “Dicas para otimizar suas funções SQL”,,, vou adicionar algumas coisas que acho interessante com este post aqui.
Comentários,,,,
Lembra daquela procedure que fazia uma validação de alguma coisa em algum lugar ??? Lembra por que você declarou aquele campo bit no começo ??? Não? tem certeza que sabe? acha que era para alguma coisa importante?
Uma coisa que acho muito importante em todo o código, mas são poucas as pessoas que fazem direito e menos ainda as que fazem, é comentar o que ele faz,,,
Não custa muito adicionar algumas linhas com algum tipo de descritivo do que aquilo deveria fazer, quem fez, quando fez, qual a versão, algum exemplo,,,
Lembre-se: Uma hora ou outra o código pode precisar de manutenção,,, ela pode ser feita por você ou outra pessoa,,, em todo o caso,,, é sempre bom ter alguma coisa para te ajudar a lembrar,,,
Ex:
/* Nome: usp_mostra_nome_base Versão: 2.0 Data de criação: 11-07-2011 Data da última modificação: 12-07-2011
Script: Esta procedure server para listas o nome das bases existentes no sistema.
Exemplo exec usp_mostra_nome_base
Versão 1: Autor: Ricardo Leka Roveri
nesta versão a proc faz XYZ
Versão 2: Autor: Ricardo Leka Roveri
nesta versão a proc faz XYZ ordenado por data de criação
*/
Outra coisa interessante seria comentar partes do código,,, não precisa escrever um livro do motivo que você está fazendo aquele join entre 20 tabelas mas, de uma forma clara, escrever o objetivo dessa bagunça,,,
NOLOCK,,,
Você é um fanático por NOLOCK? Legal,,, nada contra,,, mas se vai usar isso umas 40 vezes dentro de uma procedure porque você não declara ele como:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITED;
é legal, ajuda, mais simples pra você e para qualquer outra pessoa que veja seu código…
Lembrete,,,
Lembre-se:
A maior parte de vocês não desenvolve coisas pra vocês,,, vocês desenvolvem coisas pra outras pessoas usarem…
Não é porque na sua maquina tudo funciona direitinho que quando for para produção tem que funcionar,,, Você precisa testar,,, faça teste de carga… stresse sua aplicação até ela parar,,, 10,,, 100,,, 1000,,, acessos simultâneos,,,
Existem muitas ferramentas que fazem teste de carga. Ex.: JMeter, o próprio SQL Profiler, etc..
Mesmo que saiba o que fazer, é importante treinar para situações complicadas…
É melhor fazer muita tentativa em erro em um ambiente controlado do que no ambiente de produção,,, certo?
Então,,, antes de mais nada… eu sei que você sabe,,, mas não custa nada relembrar,,,
NUNCA FAÇA ISSO EM PRODUÇÃO !!!
Bom,,, com isso em mente,,, vamos começar,,,
1. Vamos criar uma base:
CREATE DATABASE [corrompeu] GO
2. Vamos alterar o modo de recovery da base:
alter database corrompeu set recovery full GO
3. Agora vamos colocar uma tabela:
use corrompeu GO
create table vendas ( vendasID int identity, clienteID int default convert(int, 100000 * RAND()), vendaData datetime default getdate(), vendaTotal money ) GO
4. Adicionamos um índice pra dar gosto…
create clustered index vendaCI on vendas (vendasID) GO
5. Colocamos uns dados,,,
set nocount ON GO
declare @conta INT select @conta = 0 while (@conta < 50000) begin insert into vendas (vendaTotal) values (100*RAND()) select @conta = @conta +1 end GO
7. E vamos fazer uns backups
use master GO
backup database corrompeu to disk = ‘d:\db01\local\corrompeu_1.bak’ with init go
backup log corrompeu to disk = ‘d:\db01\local\corrompeu_2.trn’ go
8. Bom,,, com os backups feitos,,, vamos ver as páginas que foram criadas,,,
dbcc ind (‘corrompeu’,‘vendas’,1) GO
9. Escolha uma página e coloque no lugar do XXXXX
DBCC TRACEON (3604) GO dbcc page(‘corrompeu’,1,XXXXX,3)
Você deve ver alguma coisa do tipo:
A página que eu escolhi foi a 1:493. Meu vendasID vai do registro 24256 até 245000.
Agora começa a ficar legal….
10. Vamos colocar a base offline:
alter database corrompeu set offline GO
11. Agora um simples cálculo…
select 493*8192 GO
Temos o número em decimal da localização da página no arquivo .mdf
12. Com esse número vamos utilizar um editor Hexadecimal para achar a linha dentro do arquivo .mdf.
Dentro do editor de Hexadecimal, abra o arquivo .mdf (neste caso D:\DB01\Corrompeu.mdf).
Clique em “Localizar” e escolha “Ir Para”.
Escolha a opção “DEC”, digite ou cole o resultado do cálculo acima e depois cliente em “HEX”, ele vai converter o valor para Hexadecimal.
clique em “OK”
Altere a linha onde o cursor esta piscando para 00 (zero-zero), ela vai ficar em vermelho.
Salve o arquivo.
13. Agora dentro do SQL vamos voltar com a base online:
alter database corrompeu set online GO
14. Usando o DBCC CHECKDB, vamos ver se a base está realmente corrompida…
Legal,,, temos uma base corrompida…
15. vamos tentar um select na tabela,,, e olha lá o erro,,,
Bom,,, legal… temos uma base corrompida,,, e agora?
Agora fica legal… o objetivo é deixar a base operacional sem perder informação… imagine que essa é sua base de produção e justamente essa tabela é a folha de pagamento,,, olha que legal…
Uma dica: tentei fazer o processo de restore no SQL Server Denali CTP 1 e não consegui restaurar apenas a página, tive que remover ela e reinserir os dados através de outra base, fiz o mesmo processo de restore apenas da página no SQL Server 2008 R2 e funcionou sem problema.
Se alguém precisar de ajuda é só deixar o comentário…
ATUALIZAÇÃO:
Segue o link do SkyDrive com a base, backup e o script desse exemplo:
Esse post é para falar de ferramentas gratuitas,,, é com muito pesar que estou retirando o SSMS Tools Pack do primeiro lugar, a partir da versão 2.5.0.0 ele deixou de ser de graça, logo, vai contra o intuito do post…
Tem gente que gosta de fazer as coisas na marra,,, sem ajuda de nada,,, script de baixo de script,,, Isso é muito legal, tem muita coisa que só se resolve assim,,,
O importante é conhecer o que o mercado oferece quando você quer “uma ajuda” ou pra realmente facilitar o dia a dia,,,
O meu TOP 5 de ferramentas gratuitas são:
Pra quem gosta de trabalhar com o SSMS, um add-on bem legal é o SSMS Tools Pack desenvolvido por Mladen Prajdić. Ele adiciona algumas funções bem legais como: histórico, snippets, gerador de código… Acho uma ferramenta pequena e legal… Uma ferramenta muito interessante para adicionar funcionalidades ao SSMS é o SSMSBoost ele adicionar recursos muito bons como snippets, localizador de objetos, alterador de barra de titulo e uma coisa bem legal que é o cadastro de conexão onde você pode colocar alerta de ambiente de produção,,, ai ele avisa, dependendo do comando que você precisa prestar atenção antes de dar um truncate table por exemplo…. Ele é de graça, mas naquelas, você precisa reinstalar ele a cada 45 dias (não é trial, é só uma coisa chata que o desenvolvedor colocou),,,
Quem nunca passou raiva com o gerador de plano de execução do SSMS que drop um banco?,,, Se você usar o SQL Sentry Plan Explorer pelo menos uma vez, não vai querer deixar de usar,,, ele mostra de uma forma fácil de entender qual parte do plano estásendo mais custoso para a operação… fora outras coisas legais…
Não pode faltar de jeito nenhum o Who is Active desenvolvido por Adam Machanic e por falar nele, existe um add-on da Schema Solutionsque adiciona uma interface gráfica para a execução de procedure.
Na primeira vez que vi essa ferramenta não achei que seria tão útil, mas o SQL Trace Analyzeré bem interessante. Ele analisa o Profiler capturado em arquivo ou banco e gera um relatório consolidado mostrando o impacto, tempo, processamento, IO, etc.. E de brinde ele instala um monitorador de Locks/Blocks. O problema dessa ferramenta é a parafernália que ele instala, mas você pode remover o resto das coisas e ficar só com o programa principal.
E não podia faltar alguma forma de monitorar o que acontece com o banco,,, para isso achei o IgniteFree, uma ferramenta muito simples de configurar e com muita informação relevante. Claro que a versão Trial/Full tem mais opções, mas mesmo na versão free é uma ótima ferramenta. Ela é leve, não ocupa muito espaço, não gera pressão na máquina que está sendo monitorada e de quebra ainda consegue monitorar uns Oracles que você tenha perdido no ambiente…
Quando estamos no SQL Server (2005 ou superior), existem uma função que mostra algumas propriedades interessante sobre o login do SQL chamada LoginProperty.
Não conheço muitas aplicações que possuem interface que permite o usuário trocar a senha dele no SQL,,, mas achei interessante ter essa opção para saber quando alguém trocou a senha e “esqueceu” de avisar,,, ai tem aplicação que não abre,,, usuário que não loga,,, e quase sempre ninguém nunca fez nada….
Aqui vão alguns selects interessantes….
Mostra todos os logins que tiveram a senha trocada a mais de 30 dias:
Bom,,, para este mês o Sr. Bob Pusateri (twitter | Blog) escolheu o tópico Common Table Expressions (CTE).
Você não sabe o que é? Não faz ideia? Isso é mais comum do que você imagina,,, vejo muitos códigos por ai onde o pessoal de dev poderia utilizar este recurso, mas como ainda estão presos em conceitos antigos do SQL 2000 ou as vezes até mais velhos não fazem ideia de alguns novos recursos interessantes…
Para uma leitura interessante sobre este assunto acesse:
Você vai achar muita coisa voltada pra dev… mas,,, como sou mais um cada de infra,,, achei interessante este código no Site do Sr. Paul Randal (twitter | Blog) sobre wait statistics usando a sys.dm_os_wait_stats, nada muito complexo.
1:WITH Waits AS
2: (SELECT
3: wait_type,
4: wait_time_ms / 1000.0 AS WaitS,
5: (wait_time_ms - signal_wait_time_ms) / 1000.0 AS ResourceS,
6: signal_wait_time_ms / 1000.0 AS SignalS,
7: waiting_tasks_count AS WaitCount,
8: 100.0 * wait_time_ms / SUM (wait_time_ms) OVER() AS Percentage,
9: ROW_NUMBER() OVER(ORDERBY wait_time_ms DESC) AS RowNum
O tópico deste mês para o T-SQL Tuesday é o operador apply,,,
Caso você não faça ideia de pra que serve o apply leia aqui.
Para este post vou coloca um script simples usando o apply.
Ele não faz nada muito importante, apenas mostra as 20 querys que mais gerão stress de disco:
SELECT TOP 20 SUBSTRING(qt.text, (qs.statement_start_offset/2)+1,
((CASE qs.statement_end_offset WHEN -1 THENDATALENGTH(qt.text) ELSE qs.statement_end_offset END – qs.statement_start_offset)/2)+1),
qs.execution_count,
qs.total_logical_reads, qs.last_logical_reads,
qs.min_logical_reads, qs.max_logical_reads,
qs.total_elapsed_time, qs.last_elapsed_time,
qs.min_elapsed_time, qs.max_elapsed_time,
qs.last_execution_time,
qp.query_plan FROMsys.dm_exec_query_stats qs CROSS APPLYsys.dm_exec_sql_text(qs.sql_handle) qt CROSS APPLYsys.dm_exec_query_plan(qs.plan_handle) qp WHERE qt.encrypted=0 ORDER BY qs.total_logical_reads DESC
let's make things better
Manage Cookie Consent
To provide the best experiences, we use technologies like cookies to store and/or access device information. Consenting to these technologies will allow us to process data such as browsing behavior or unique IDs on this site. Not consenting or withdrawing consent, may adversely affect certain features and functions.
Functional
Always active
The technical storage or access is strictly necessary for the legitimate purpose of enabling the use of a specific service explicitly requested by the subscriber or user, or for the sole purpose of carrying out the transmission of a communication over an electronic communications network.
Preferences
The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.
Statistics
The technical storage or access that is used exclusively for statistical purposes.The technical storage or access that is used exclusively for anonymous statistical purposes. Without a subpoena, voluntary compliance on the part of your Internet Service Provider, or additional records from a third party, information stored or retrieved for this purpose alone cannot usually be used to identify you.
Marketing
The technical storage or access is required to create user profiles to send advertising, or to track the user on a website or across several websites for similar marketing purposes.
To provide the best experiences, we use technologies like cookies to store and/or access device information. Consenting to these technologies will allow us to process data such as browsing behavior or unique IDs on this site. Not consenting or withdrawing consent, may adversely affect certain features and functions.