Coletando tamanho de tabelas de um database – SQL Server

Pessoal, há algum tempo recebi um pedido de ajuda para realizarem uma coleta de informações do tamanho de tableas de um database.
Como sabem no SQL conseguimos executar um SP_DATABASES e conseguimos informações do tamanho de um database mas esse tipo de informação detalhada referente a tabelas não temos em uma proc de sistema.

Sabendo disso, encontramos uma procedure que faz esse trabalho, e ao executa-la saberemos o tamanho de cada tabela. Segue a proc:

create proc dba_sp_espacotabela
as

declare @vname sysname

declare @tmpTamTabela table (
name sysname null
, rows int null
, reserved varchar(25) null
, data varchar(25) null
, index_size varchar(25) null
, unused varchar(25) null
)

declare cp1 cursorlocalfast_forwardread_onlyfor
select name
from sysobjects
where type = ‘U’
order by name

open cp1

while 1 = 1

begin
fetchnextfrom cp1 into @vname
if @@fetch_status <> 0 
break

insertinto @tmpTamTabela (name, rows, reserved
, data, index_size, unused )
exec sp_spaceused @vname

end

close cp1
deallocate cp1

select name as ‘Nome’
rows as ‘Linhas’
convert (int,replace (reserved,‘ KB’,) ) as ‘Tamanho total’
convert (int,replace (data,‘ KB’,) ) as ‘Dados’
convert (int,replace (index_size,‘ KB’,) ) as ‘Index’
convert (int,replace (unused,‘ KB’,) ) as ‘Não utilizado’
from @tmpTamTabela
order by convert (intreplace (reserved,‘ KB’,) ) desc
go

See you amiguinhos.

Migrando Logins – SQL Server

Quando o assunto é ‘migração’, muitas possibilidades, mudanças e acima de tudo, problemas, surgem na cabeça de um DBA. Montar um banco do zero não é nada comparado ao processo de migrar um banco em funcionamento, com diversos processos executando e aplicações apontando.

Um dos grandes problemas que encontramos no SQL Server é justamente a migração de logins/senhas de usuários. E nesses problemas e dores de cabeça, encontramos a solução, vindo direto da microsoft, extremamente funcional!

Trata-se de uma procedure, que faz o link entre usuário e login, e caso o login não exista, ele trata de cria-lo, já com o hash que existia antes. Assim, ao migrar um database de servidor, ou criar um ambiente de testes, você não terá problemas em migrar os users. Segue a proc abaixo!

USE master
GO
IF OBJECT_ID (‘sp_hexadecimal’) IS NOT NULL
DROP PROCEDURE sp_hexadecimal
GO
CREATE PROCEDURE sp_hexadecimal
@binvalue varbinary(256),
@hexvalue varchar (514) OUTPUT
AS
DECLARE @charvalue varchar (514)
DECLARE @i int
DECLARE @length int
DECLARE @hexstring char(16)
SELECT @charvalue = ‘0x’
SELECT @i = 1
SELECT @length = DATALENGTH (@binvalue)
SELECT @hexstring = ‘0123456789ABCDEF’
WHILE (@i <= @length)
BEGIN
DECLARE @tempint int
DECLARE @firstint int
DECLARE @secondint int
SELECT @tempint = CONVERT(int, SUBSTRING(@binvalue,@i,1))
SELECT @firstint = FLOOR(@tempint/16)
SELECT @secondint = @tempint – (@firstint*16)
SELECT @charvalue = @charvalue +
SUBSTRING(@hexstring, @firstint+1, 1) +
SUBSTRING(@hexstring, @secondint+1, 1)
SELECT @i = @i + 1
END
SELECT @hexvalue = @charvalue
GOIF OBJECT_ID (‘sp_help_revlogin’) IS NOT NULL
DROP PROCEDURE sp_help_revlogin
GO
CREATE PROCEDURE sp_help_revlogin @login_name sysname = NULL
AS
DECLARE @name sysname
DECLARE @type varchar (1)
DECLARE @hasaccess int
DECLARE @denylogin int
DECLARE @is_disabled int
DECLARE @PWD_varbinary  varbinary (256)
DECLARE @PWD_string  varchar (514)
DECLARE @SID_varbinary varbinary (85)
DECLARE @SID_string varchar (514)
DECLARE @tmpstr  varchar (1024)
DECLARE @is_policy_checked varchar (3)
DECLARE @is_expiration_checked varchar (3)
DECLARE @defaultdb sysnameIF (@login_name IS NULL)
DECLARE login_curs CURSOR FORSELECT p.sid, p.name, p.type, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin FROM 
sys.server_principalsLEFT JOIN sys.syslogins l
ON ( l.name = p.name ) WHERE p.type IN ( ‘S’, ‘G’, ‘U’ ) AND p.name <> ‘sa’
ELSE

DECLARE login_curs CURSOR FOR

SELECT p.sid, p.name, p.type, p.is_disabled, p.default_database_name, l.hasaccess, l.denylogin FROM
sys.server_principalsLEFT JOIN sys.syslogins l
ON ( l.name = p.name ) WHERE p.type IN ( ‘S’, ‘G’, ‘U’ ) AND p.name = @login_name
OPEN login_curs

FETCH NEXT FROM login_curs INTO @SID_varbinary, @name, @type, @is_disabled, @defaultdb, @hasaccess, @denylogin
IF (@@fetch_status = -1)
BEGIN
  PRINT ‘No login(s) found.’
CLOSE login_curs
DEALLOCATE login_curs
RETURN -1
END
SET @tmpstr = ‘/* sp_help_revlogin script ‘
PRINT @tmpstr
SET @tmpstr = ‘** Generated ‘ + CONVERT (varchar, GETDATE()) + ‘ on ‘ + @@SERVERNAME + ‘ */’
PRINT @tmpstr
PRINT
WHILE (@@fetch_status <> -1)
BEGIN
IF (@@fetch_status <> -2)
BEGIN
PRINT
SET @tmpstr = ‘– Login: ‘ + @name
PRINT @tmpstr
IF (@type IN ( ‘G’, ‘U’))
BEGIN — NT authenticated account/group

SET @tmpstr = ‘CREATE LOGIN ‘ + QUOTENAME( @name ) + ‘ FROM WINDOWS WITH DEFAULT_DATABASE = [‘ + @defaultdb + ‘]’
END
ELSE BEGIN — SQL Server authentication
— obtain password and sid
SET @PWD_varbinary = CAST( LOGINPROPERTY( @name, ‘PasswordHash’ ) AS varbinary (256) )
EXEC sp_hexadecimal @PWD_varbinary, @PWD_string OUT
EXEC sp_hexadecimal @SID_varbinary,@SID_string OUT

— obtain password policy state
SELECT @is_policy_checked = CASE is_policy_checked WHEN 1 THEN ‘ON’ WHEN 0 THEN ‘OFF’ ELSE NULL END FROM sys.sql_logins WHERE name = @name
SELECT @is_expiration_checked = CASE is_expiration_checked WHEN 1 THEN ‘ON’ WHEN 0 THEN ‘OFF’ ELSE NULL END FROM sys.sql_logins WHERE name = @name

SET @tmpstr = ‘CREATE LOGIN ‘ + QUOTENAME( @name ) + ‘ WITH PASSWORD = ‘ + @PWD_string + ‘ HASHED, SID = ‘ + @SID_string + ‘, DEFAULT_DATABASE = [‘ + @defaultdb + ‘]’

IF ( @is_policy_checked IS NOT NULL )
BEGIN
SET @tmpstr = @tmpstr + ‘, CHECK_POLICY = ‘ + @is_policy_checked
END
IF ( @is_expiration_checked IS NOT NULL )
BEGIN
SET @tmpstr = @tmpstr + ‘, CHECK_EXPIRATION = ‘ + @is_expiration_checked
END
END
IF (@denylogin = 1)
BEGIN — login is denied a ccess
SET @tmpstr = @tmpstr + ‘; DENY CONNECT SQL TO ‘ + QUOTENAME( @name )
END
ELSE IF (@hasaccess = 0)
BEGIN  — login exists but does not have access
SET @tmpstr = @tmpstr + ‘; REVOKE CONNECT SQL TO ‘ + QUOTENAME( @name )
END
IF (@is_disabled = 1)
BEGIN — login is disabled
SET @tmpstr = @tmpstr + ‘; ALTER LOGIN ‘ + QUOTENAME( @name ) + ‘ DISABLE’
END
PRINT @tmpstr
END

FETCH NEXT FROM login_curs INTO @SID_varbinary, @name, @type, @is_disabled, @defaultdb, @hasaccess, @denylogin
END
CLOSE login_curs
DEALLOCATE login_curs
RETURN 0
GO

—- EXECUTAR —-
EXEC sp_help_revlogin
Espero ter ajudado!
Até mais pessoal =)

Backup apontando para outro servidor – SQL Server

Já falamos de backup, em SQL Server aqui , porém, recebi a pouco um e-mail sobre uma dúvida ao qual não foi colocado no post: como apontar o backup para um outro servidor?
Na verdade não tem segredo, são alguns detalhes que vão ajudar a fazer o backup apontar para outro servidor. A parte mais importante é: No servidor de destino (onde o backup será armazenado), deve ter o disco compartilhado, e preferencialmente uma pasta compartilhada. Aqui, nosssa pasta compartilhada será a bkp_databases, meu servidor ficticio de backup irá responder pelo ip 192.168.50.13 , sendo assim, nosso script de backup ficará:

BACKUP DATABASE [database]
TO DISK = N’\\192.168.50.13\bkp_databases\nome_do_arquivo.bkp’
WITH NOINIT, NOUNLOAD,
Name = N’meu backup’
COMPRESSION, NOSKIP,
STATS = 10, NOFORMAT
GO

E aqui o detalhe importante é: quando vamos direcionar o backup, colocamos no script o clássico \\ip . Note que eu não estou apontando a um disco e sim a uma pasta, pois eu deixei ela compartilhada. Caso tenha um disco compartilhado, ele pode ser adicionado no local da pasta.

See you amiguinhos.

Convertendo Engine de uma tabela – MySQL

Como já explicamos aqui no blog, o mysql possui vários tipos de engines para suas tabelas, cada qual para uma função e que aceita algumas funções que outras não. Por isso, as vezes ao criarmos uma tabela, não definimos sua engine e ela fica no padrão do mysql. Porém, as vezes a engine padrão não é a melhor para a utilização do bd, e precisamos altera-las. A idéia é explicar rapidamente, 2 modos de alterar a engine das tabelas. Um exeplo via Linux, restaurando a tabela, e o outro dando um alter direto no banco de dados.
O comando via MySQL é simples, um alter na engine resolve o problema, como no exemplo abaixo, alterando a engine da tabela ‘posts_dba’ para a engine InnoDB:

ALTER TABLE `blog`.`posts_dba`ENGINE = InnoDB;

O problema desta alteração: versões de mysql 5.0 não aceitam o alter table engine. Para isso, temos um outro modo de fazer: via backup/restore. No meu exemplo, estou alterando TODA a base para engine innoDB, ou seja, todas as tabelas. Porém, você pode executar backup de apenas uma tabela e restaurar utilizando o comando abaixo (comando de restore apenas):

[root@localhost ~]$ zcat blog.sql.gz |sed ‘s/ENGINE=MyISAM/ENGINE=Innodb/g’ | mysql -u root -p blog

Com isso, no restore, convertemos a engine de myisan para innodb.

Espero ter ajudado!
Até mais pessoal =)

Descobrindo Definer de Objetos – MySQL

Em alguns posts antigos, como o de erro com o root e delimiter citamos algumas vezes o ‘definer’. Descobri que poucos sabem o que é um definer, sua função e sua importância no mysql.

Definer funciona como um owner de objetos dentro do mysql. Ao criar alguns objetos, como triggers e views, um definer deve ser especificado, ou, será criado com o seu próprio user.
Por ‘boas práticas’ recomendamos que o definer dos objetos seja sempre o ‘root’@’localhost’. Por que? O root é um user ao qual você não vai excluir do MySQL, ao contrário do user daquele DBA que hoje está na sua empresa e amanhã sai. E ele sempre terá acesso local no MySQL. Uma vez o user sendo Definer de um objeto, ele não pode deixar de existir no banco. Se deixar de existir, o objeto para de funcionar. O Mysql dá um erro ao qual você nunca irá entender, e ao excluir o user, não é retornado nenhum aviso quanto aos seus definers.

Por isso, se atente aos seus objetos e na criação dos mesmos, trabalhe com padrão e tenha segurança.
Para descobrir definers de objetos, trabalhe na tabela ‘information_schema‘, onde você consegue as informações dos objetos.
Abaixo, exemplos de select para descobrir os definers.

SELECT `specific_name`, `routine_schema`, `routine_name`, `routine_type`, `definer` FROM `information_schema`.`routines` WHERE `definer` = ‘meu_user’;

SELECT `trigger_catalog`, `trigger_schema`, `trigger_name`, `definer` FROM `information_schema`.`triggers` WHERE`definer` = ‘meu_user’;

SELECT `table_catalog`, `table_schema`, `table_name`, `definer` FROM `information_schema`.`views` WHERE`definer` = ‘meu_user’;

Espero ter ajudado!
Até mais pessoal =)