Olá! Seja bem-vindo!

Criar scripts que previnam e tratem de erros, também conhecida como “criar scripts resilientes”, é uma habilidade geralmente negligenciada por muitos desenvolvedores e DBAs, o que gera oportunidade de destaque para nós que almejamos ascensão profissional. 

Existem casos onde é compreensível priorizar velocidade de entrega vs resiliência de scripts, no entanto, as vezes, a resiliência que precisamos é tão simples quanto a inclusão de uma palavra adicional em um comando SQL.

Assista aqui ao vídeo de demonstração:

RESILIÊNCIA NA PRÁTICA

No dia a dia da evolução do softwares, é frequente a necessidade de criar, alterar e excluir objetos do banco SQL (ex: tabelas, usuários, numeradores, procedures, functions, etc) para que eles correspondam as necessidades do negócio.

Por padrão, a maioria dos comandos SQL NÃO são resilientes, ou seja, se tentar excluir uma tabela que NÃO EXISTE (ex: DROP TABLE XPTO), o SQL Server retornará um erro.

Em minha experiência, nenhum cliente sente-se a vontade quando recebe um script de atualização do banco que gera erros, sendo estratégico prevermos cenários onde eles possam ocorrer e tratar isso antecipadamente.

Fazer isso pode parecer complicado a primeira vista, mas geralmente não é! Por exemplo: Se nosso script precisa excluir uma tabela, é prudente verificar se a tabela existe e se o usuário que está executando o script possui permissão de exclui-la, antes de executarmos o comando DROP!

Quem já atua na área de banco a mais tempo sabe que “verificar permissão” geralmente é algo que não se gasta tempo fazendo, agora, verificar se uma tabela existe antes do DROP é super simples, como vimos na demonstração.

Além disso, a partir do SQL 2016, temos o DROP IF EXISTS, que permite executarmos o comando DROP de forma resiliente (se você executar DROP TABLE IF EXISTS XPTO), o comando não gerará erro, mesmo que a tabela não exista!

Conclusão

Geralmente sou meio paranoico com evitar erros nos scripts que crio.

Por um lado, essa paranoia sempre gera mais trabalho durante as minhas atuações, por outro sempre trouxe respeito em relação as coisas que entrego.

No vídeo como medir performance do SQL com o jMetter, discutimos um pouco sobre a importância de você testar o que faz antes da entrega, e acho importante reforçar essa mensagem dizendo que:

Em minha experiência, vale a pena investir de 5 a 10% do tempo de uma entrega na prevenção e tratativa de erros de implantação antes de concluir suas entregas.

Essa atitude pode parecer impossível ou pouco razoável a primeira vista, mas lhe asseguro que se fizer isso, rapidamente você se destacará da manada, porque as pessoas passarão a confiar mais no seu trabalho ao perceber seu cuidado com a qualidade.

DISCLAIMER:

Alguns chefes serão contra a ideia de “gastar mais tempo” com qualidade, então você terá de ser versátil e rápido para poder implementar A ideia no seu dia a dia aos poucos e sem falar para ninguém… rsrs

Acredite, ganhar respeito pela qualidade de suas entregas é algo que nunca vi dar errado. Nesse contexto, o DROP IF EXISTS, junto com as demais estratégias que vimos hoje, são curingas que lhe ajudarão muito.

Abraço do seu amigo Josué

Script da aula



----------------------------------------------------------------------------------------------------
-- DROP [IF EXISTS] no SQL
----------------------------------------------------------------------------------------------------
--------------------------------------------------
-- Tratamento de erro: Com vs Sem
--------------------------------------------------
-- ERRO: DROP de uma base que não existe
use master
go
drop database curso
go

-- ERRO: CREATE de uma base que já existe
create database tempdb
go


-- ERRO "clássico": Usuário executa 2 vezes o mesmo script
create database curso
go


-- OK: Exemplos de scripts com tratamento de erro. Pode ser executado multiplas vezes!
if db_id('curso') is not null 
	begin
		alter database curso set single_user with rollback immediate
		drop database curso
	end
go
create database curso
go




--------------------------------------------------
-- Database
--------------------------------------------------
-- Exemplos de tratamento manual
if db_id('curso') is not null drop database curso 
if exists (select * from sys.databases where name = 'curso') drop database curso
go

-- Pós SQL2016
drop database if exists curso
go

-- Create
create database curso
go
use curso
go


--------------------------------------------------
-- Tabela
--------------------------------------------------
-- Exemplos de tratamento manual
if object_id('teste', 'u') is not null drop table teste
if exists (select * from information_schema.tables where table_name = 'teste') drop table teste
if (select object_id from sys.objects where name = 'teste' and type_desc = 'user_table') is not null drop table teste
go

-- Pós SQL2016
drop table if exists teste
go

-- Create
create table teste (id smallint constraint pk_teste primary key, nm varchar(100))
go


--------------------------------------------------
-- Coluna
--------------------------------------------------
-- Exemplos de tratamento manual
if col_length('teste', 'nm') is not null alter table teste drop column nm
if exists (select * from information_schema.columns where table_name = 'teste' and column_name = 'nm') alter table teste drop column nm
if (select object_id from sys.all_columns where object_id = object_id('teste', 'u') and name = 'nm') is not null alter table teste drop column nm
go

-- Pós SQL2016
alter table teste drop column if exists nm
go


--------------------------------------------------
-- Constraint (primary key, check, default, etc...)
--------------------------------------------------
-- Exemplos de tratamento manual (EXEMPLO PARA PRIMARY KEY)
if object_id('pk_teste', 'pk') is not null alter table teste drop constraint pk_teste 
if exists (select * from information_schema.table_constraints where table_name = 'teste' and constraint_name = 'pk_teste') alter table teste drop constraint pk_teste 
if (select object_id from sys.key_constraints where type = 'PK' and parent_object_id = object_id('teste', 'u') and name = 'pk_teste') is not null alter table teste drop constraint pk_teste 
go

-- Pós SQL2016
alter table teste drop constraint if exists pk_teste
go


--------------------------------------------------
-- Índices
--------------------------------------------------
-- Exemplos de tratamento manual
if indexproperty(object_id('teste', 'u'), 'ix_teste_id', 'indexid') is not null drop index ix_teste_id on teste
if exists (select * from sys.indexes where object_id = object_id('teste', 'u') and name = 'ix_teste_id') drop index ix_teste_id on teste
go

-- Pós SQL2016
drop index if exists ix_teste_id on teste
go

-- Create
create index ix_teste_id on teste(id)
go


--------------------------------------------------
-- Procedures, functions, triggers e views
--------------------------------------------------
-- Exemplos de tratamento manual
if object_id('usp_teste', 'p' ) is not null drop procedure usp_teste
if object_id('ufc_teste', 'if') is not null drop function ufc_teste
if object_id('trg_teste', 'tr') is not null drop trigger trg_teste
if object_id('uvw_teste', 'v') is not null drop view uvw_teste
go
if exists (select * from sys.objects where name like 'usp_teste' and type = 'p' ) drop procedure usp_teste
if exists (select * from sys.objects where name like 'ufc_teste' and type = 'if') drop function ufc_teste
if exists (select * from sys.objects where name like 'trg_teste' and type = 'tr') drop trigger trg_teste
if exists (select * from sys.objects where name like 'uvw_teste' and type = 'v' ) drop view uvw_teste
go

-- Pós SQL2016
drop procedure if exists usp_teste
drop function  if exists ufc_teste
drop trigger   if exists trg_teste
drop view      if exists uvw_teste
go

-- Pós SQL2016 (Create OR Alter)
create OR ALTER procedure usp_teste as select 1
go
create OR ALTER function ufc_teste() returns table as return (select 1 id)
go
create OR ALTER trigger trg_teste on teste for delete as print 'trigger for delete'
go
create OR ALTER view uvw_teste as select 1 as id
go


--------------------------------------------------
-- Schema
--------------------------------------------------
-- Exemplos de tratamento manual
if schema_id('desenvolvimento') is not null drop schema desenvolvimento
if exists (select * from information_schema.schemata where schema_name = 'desenvolvimento') drop schema desenvolvimento
if (select schema_id from sys.schemas where name = 'desenvolvimento') is not null drop schema desenvolvimento
go

-- Pós SQL2016
drop schema if exists desenvolvimento
go

-- Create
create schema desenvolvimento
go


--------------------------------------------------
-- Sinônimos
--------------------------------------------------
-- Exemplos de tratamento manual
if object_id('teste_sinonimo', 'sn') is not null drop synonym teste_sinonimo
if (select object_id from sys.synonyms where name = 'teste_sinonimo') is not null drop synonym teste_sinonimo
if exists (select * from sys.objects where name = 'teste_sinonimo' and type_desc = 'synonym') drop synonym teste_sinonimo

go

-- Pós SQL2016
drop synonym if exists teste_sinonimo
go

-- Create
create synonym teste_sinonimo for dbo.sinonimo
go


--------------------------------------------------
-- Type
--------------------------------------------------
-- Exemplos de tratamento manual
if type_id('produto') is not null drop type produto
if exists (select * from sys.types where name = 'produto') drop type produto
go

-- Pós SQL2016
drop type if exists produto
go

-- Create
create type produto as table (nm varchar(100), vl money)
go


--------------------------------------------------
-- Database User
--------------------------------------------------
-- Exemplos de tratamento manual
if user_id('usr_teste') is not null drop user usr_teste
if exists (select * from sys.database_principals where name = 'usr_teste') drop user usr_teste
go

-- Pós SQL2016
drop user if exists usr_teste
go

-- Create
create user usr_teste without login
go


--------------------------------------------------
-- Database Role
--------------------------------------------------
-- Exemplos de tratamento manual
if database_principal_id('rl_teste') is not null drop role rl_teste
if exists (select * from sys.database_principals where name = 'rl_teste') drop role rl_teste
go

-- Pós SQL2016
drop role if exists rl_teste
go

-- Create
create role rl_teste
go


--------------------------------------------------
-- Sequence
--------------------------------------------------
-- Exemplos de tratamento manual
if object_id('seq_teste', 'so') is not null drop sequence seq_teste
if exists (select * from sys.objects where name = 'seq_teste' and type_desc = 'sequence_object') drop sequence seq_teste
go

-- Pós SQL2016
drop sequence if exists seq_teste
go

-- Create
create sequence seq_teste as int
go


--------------------------------------------------
-- Clean-up
--------------------------------------------------
-- Database
use master
go
alter database curso set single_user with rollback immediate
go
drop database if exists curso
go


Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *