Escolar Documentos
Profissional Documentos
Cultura Documentos
O DBAccess é um produto que permite a conexão do ERP Protheus aos diversos bancos
de dados SQL suportados.
O DBAccess permite aos desenvolvedores trabalhar com banco de dados SQL usando
uma metodologia ISAM, exatamente como se estivessem usando um banco ISAM nativo.
A recomendação é utilizar sempre que possível a linguagem SQL para acesso aos dados
quando se utiliza DBAccess.
Além disso, a linguagem SQL permite o uso de funções de agregação, entre outros, que
otimizam ainda mais o desempenho.
Campo Descrição
Os campos tipo memo são armazenados como BLOB (binary large object). O nome
do tipo de campo pode variar conforme o banco de dados. No MS Sql Server o
campo é armazenado como image. Os campos MEMO vituais estilo Protheus
(MSMM) são armazenados de forma diferente, na tabela SYP.
Tabela TOP_FIELD
A tabela Top Field é utilizada para conversão automática dos tipos de dados diferentes de
caractere para os tipos correspondentes na linguagem ADVPL, quando utilizamos modo
de acesso ISAM. Ela também armazena a quantidade de inteiros e casas decimais dos
campos numéricos.
Por outro lado, se a tabela é manipulada diretamente no banco, pode ocorrer divergência
entre a Top Field e a tabela física. Isso vai causar mau funcionamento do sistema.
A tabela Top Field só faz a conversão automática de tipo no modelo ISAM. Quando
trabalhamos com result sets de querys, devemos utilizar a função TcSetField() para
converter manualmente os tipos de dados.
A linguagem SQL
Definição:
Vantagens do SQL:
•É uma linguagem universal de acesso a dados, e apesar das variações, roda em uma
enorme quantidade de plataformas.
•Concentra-se nos resultados desejados, deixando ao banco de dados a missão de
escolher a melhor estratégia para consulta dos dados.
•Reduz o trafego de rede pois muitos dados são avaliados no servidor do banco de dados,
sem que obrigatoriamente sejam enviados à aplicação, como no ISAM.
Desvantagens:
•A lógica de conjuntos não é natural para muitas pessoas, o que exige certo tempo de
adaptação.
•Queries grandes e complexas podem ser tornar muito abstratas, dificultando o
entendimento.
•Devido ao desconhecimento dos fundamentos da linguagem, pode-se fazer uma query
esperando uma resposta e receber algo totalmente diferente, e o pior, sem saber disso.
Queries no Protheus
Comando / Descrição
função
dbCloseArea() Fecha a área de trabalho e apaga o record set. Após essa função,
o record set não pode mais ser acessado. Uso obrigatório ao
final do processo.
Desenvolvendo Queries
Nos primeiros momentos do DBAccess, um tipo de query foi bastante utilizado pois
necessitava de poucas mudanças na aplicação original. É a chamada “query de recno”.
Apesar de menos eficiente, esse modelo ainda é útil quando é necessário posicionar a
tabela real para alterar dados ou excluir.
Esta query retorna um campo R_E_C_N_O_ para que na varredura seja efetuado um
dbgoto() no registro real.
Ao contrário do que se poderia supor, esta abordagem é mais rápida que simplesmente
varrer a tabela ISAM, mesmo indexada, pelas seguintes razões:
Efetuar um dbkip() no record set é muito mais rápido que o dbskip() na tabela real.
O dbGoto() na tabela real é muito eficiente, pois o R_E_C_N_O_ é a chave primária de todas
as tabelas e seu índice é diferenciado e muito rápido.
Exemplo
lQuery := .T.
cAliasSC9 := GetNextAlias()
•Defina um Alias (apelido) para o record set da query. Não utilize alias fixo, pois em caso
de recursividade haverá erro por duplicidade. Utilize GetNextAlias():
cAliasAB9 := GetNextAlias()
•Comece a escrever a string da query. Evite usar “*” na seleção de campos, pois todos os
campos serão retornados aumentando o trárego de rede.
•Efetue o filtro por filial. Sempre iguale o campo filial ao xFilial da tabela correspondente.
Nunca compare o campo _FILIAL de uma tabela com o campo _FILIAL de outra. Algumas
rotinas de processamento multi-filial podem comparar o campo _FILIAL a uma variável
contendo o código da filial (exceção).
•Efetue o filtro para remover linhas excluídas. Compare D_E_L_E_T_=‘ ‘ pois é mais
rápido que fazer D_E_L_E_T_<>’*’
cQuery += "AB9.D_E_L_E_T_=' '"
•Coloque o maior número possível de condições de filtro na query. Quanto mais restritivo
for o filtro, menos registros vão ser incluídos no record set e menos trafégo ocorrerá na
rede local
cQuery += "AB9_NUMOS='" + ( cAliasAB7 )->AB7_NUMOS + ( cAliasAB7 )->AB7_ITEM + "' AND
•Utilize a cláusula ORDER BY apenas quando precisar de resultados ordenados. ORDEY
BY diminui a performance da query.
•Utilize a função ChangeQuery() para que a sintaxe da query seja adequada aos diferentes
bancos de dados.
cQuery := ChangeQuery( cQuery )
•Efetue o disparo da query e criação de seu record set através do uso conjunto de
dbUseArea() e TcGenQry(). Nessa chamada utilizaremos:
•Caso existam campos com tipo diferente de caractere na lista de retorno, deve-se utilizar
TcSetField() para converter para um tipo válido no ADVPL. Nesse caso, converteremos o
campo AB9_DTFIM para o tipo data do ADVPL.
•Quando for necessário converter uma grande quantidade de campos de uma tabela,
pode-se usar a estrutura da mesma usando a função dbStruct() e efetuar uma varredura.
Next nLoop
•Utilizar o record set conforme desejado. Nesse caso, será efetuada varredura por
While.Utilizar dbSkip() para varrer e Eof() para testar o fim do record set.
( cAliasAB9)->( dbCloseArea() )
Em muitos casos, os dados que precisamos dependem da interação entre duas ou mais
tabelas. O SQL é bastante eficiente para tratar este tipo de consulta, que chamamos de
JOIN.
O tipo mais comum de JOIN é chamado de INNER JOIN. Neste modelo, devem existir
dados tanto na primeira tabela (“esquerda”) quanto na segunda (“direita”) para que alguma
linha seja recuperada.
O INNER JOIN pode ser escrito tanto no “estilo antigo”, em que os campos de uma tabela
simplesmente são comparados com os campos de outra tabela, ou no “estilo novo” (ANSI)
onde o relacionamento é explicitado.
Abaixo listamos um exemplo de INNER JOIN usando o “estilo antigo”. As tabelas AB9
(atendimentos) e AAG (tipos de ocorrências) são relacionadas através dos campo
AB9_CODPRB e AAG_CODPRB (código de ocorrência). O que se deseja é trazer, junto
dos dados do atendimento, o tipo da ocorrência (AAG_TIPPRB) que existe apenas em
AAG.
cAlias := GetNextAlias()
cQuery := “”
cQuery += "SELECT AB9_DTINI,AB9_HRINI,AB9_DTFIM,AB9_HRFIM,AAG_TIPPRB FROM "
cQuery += RetSqlName( "AB9" ) + " AB9," + RetSqlName( "AAG" ) + " AAG WHERE "
cQuery += "AB9_NUMOS='" + AB7->AB7_NUMOS + AB7->AB7_ITEM + "' AND "
cQuery += "AB9_CODPRB=AAG_CODPRB AND "
cQuery += "AB9_FILIAL='" + xFilial("AB9") + "' AND "
cQuery += "AAG_FILIAL='" + xFilial("AAG") + "' AND "
cQuery += "AB9.D_E_L_E_T_=‘ ' AND "
cQuery += "AAG.D_E_L_E_T_=‘ '"
Funções de agregação
Ao informar uma função de agregação em um SELECT, não se pode trazer outros campos
na seleção a menos que façam parte de uma cláusula GROUP BY. Isso ocorre porque a
função de agregação é resultado de vários registros e não faz sentido trazer apenas um
deles.
Esta query exemplo retorna a quantidade total de itens liberados de um pedido de vendas
(que ainda não foram faturados), considerando todas as sequencias de liberação
existentes (SC9).
cAliasSC9 := GetNextAlias()
cQuery := "SELECT SUM(C9_QTDLIB) C9_QTDLIB "
cQuery += "FROM "+RetSqlName("SC9")+" SC9 "
cQuery += "WHERE SC9.C9_FILIAL='"+xFilial("SC9")+"' AND "
cQuery += "SC9.C9_PEDIDO='" +(cCurSorSC6)->C6_NUM+"' AND "
cQuery += "SC9.C9_ITEM='" +(cCurSorSC6)->C6_ITEM+"' AND "
cQuery += "SC9.C9_NFISCAL='"+Space(Len(SC9->C9_NFISCAL))+"' AND
cQuery += "SC9.D_E_L_E_T_=' ' "
cQuery := ChangeQuery(cQuery)
dbUseArea(.T.,"TOPCONN",TcGenQry(,,cQuery),cAliasSC9,.T.,.T.)
nQtdLib := (cAliasSC9)->C9_QTDLIB
dbCloseArea()
dbSelectArea("SC9")