ImaginarTI

quinta-feira, 26 de agosto de 2010

Dicas para SQL Dinâmico - Apóstrofos

Olá pessoal.

A dica de hoje vai para quem precisa construir comandos SQL dinâmicos, seja dentro de stored procedures ou diretamente no código da sua linguagem de preferência. Vou usar Oracle para o exemplos, mas o conceito também se aplica ao SQL Server. Para deixar os posts menores, vou fazer cada dica um post diferente.

Apóstrofo (ou aspas simples)
Todo mundo sabe que, em uma instrução SQL, as strings são delimitadas por apóstrofo (que alguns chamam de aspas simples). Assim:

create table tab_a (col1 varchar2(100) not null, col2 varchar2(100) not null);
declare
    minha_string varchar2(1000);
begin
    minha_string := 'isso é uma string';
end;
/
Muito bem. Mas e se a string em questão for uma instrução SQL? Ela, por natureza é uma linha de texto e que pode ter uma string dentro. Imagine o comando:
insert into tab_A (col1, col2) values ('valor 1', 'valor 2');
Se esse comando for uma instrução SQL dinâmica, você teria algo como isso:
declare
    meu_sql varchar2(1000);
begin
    meu_sql := 'insert into tab_A (col1, col2) values (''valor 1'', ''valor 2'')';
end;
/
Repare que para reproduzir um apóstro dentro de uma string, eu tenho que duplicá-lo, porque ele é um caracter especial (delimitador de string). Se colocasse sem duplicar, o compilador ia acusar erro, achando que haveria strings em sequencia, sem concatenar, nem nada.

Agora vamos adicionar um pouco de complexidade ao exemplo. Vou transformar esse pequeno bloco anônimo numa stored procedure com dois parâmetros do tipo string, que serão usados para preencher col1 e col2.
create or replace procedure proc_exemplo (pCol1 in varchar2, pCol2 in varchar2) as
    vSQL varchar2(1000);
begin
    vSQL := 'insert into tab_A (col1, col2) values (''' || pCol1 || ''', ''' || pCol2 || ''')';
    execute immediate vSQL;
end;
/
Sabemos que usar variáveis de ligação é muito mais eficiente, mas resolvi adotar esta estratéria
"ruim" apenas como exemplo de manipulação de strings. Outro dia eu falo sobre binding vars.


Repare como os diversos apóstrofos colados começam a prejudicar a leitura do código da proc. E é apenas uma linha de código!!! Imagine um insert imenso com muitas colunas string, o comando quebrado em várias linhas... Começa a ficar realmente ruim.

Para deixar o código mais limpo (facilitar a leitura), eu vou substituir cada apóstrofo duplicado (aqueles de dentro da string, e somente esses!) por uma aspa comum (ou dupla, se preferir). Percebe que um apóstrofo duplicado ('') não é a mesma coisa que uma aspa ("). São caracteres diferentes!!!.
create or replace procedure proc_exemplo (pCol1 in varchar2, pCol2 in varchar2) as
    vSQL varchar2(1000);
begin
    vSQL := 'insert into tab_A (col1, col2) values ("' || pCol1 || '", "' || pCol2 || '")';
    execute immediate vSQL;
end;
/
Mais fácil de ler, não?
Mais fácil para identificar onde começa e onde termina cada string (cada "pedaço" do comando SQL).

Só que se você executar esta proc, vai dar erro. Isto porque as aspas não são aceitas (em PL/SQL nem T-SQL) como delimitadores de string.


A correção é bem simples: Pode usar aspas à vontade dentro da sua string e, no último momento antes de executar o comando, você converte todas as aspas para apóstrofos duplos.
create or replace procedure proc_exemplo (pCol1 in varchar2, pCol2 in varchar2) as
    vSQL varchar2(1000);
begin

    -- preparando o comando SQL
    vSQL := 'insert into tab_A (col1, col2) values ("' || pCol1 || '", "' || pCol2 || '")';

    -- fazendo os ultimos ajustes antes de executar
    vSQL := replace(vSQL, '"', '''');
    execute immediate vSQL;

end;
/
A linha do replace é a única que você terá que manter bagunçada (vários caracteres apóstrofo), mas como é uma linha só, que fica só no final do bloco de código, dá para conviver.

Há uma restrição que você não pode usar aspas para nenhum outro fim, dentro do seu comando SQL. Se você puder atender este requisito, está feito. E eu particularmente não considero uma coisa muito esperta usar aspas (") nos nomes dos objetos Oracle. Serve só para dar dor-de-cabeça, mas isso também é papo para outro dia.

É isso pessoal.
Espero que sirva de ajuda para alguém por aí.


Abraços,

LZ

Marcadores: , , , , , ,

1 Comentários:

Postar um comentário

Assinar Postar comentários [Atom]



<< Página inicial