Página 1 de 1

Funções que ajudam no SQL

Enviado: 29 Abr 2021 12:58
por JoséQuintas
Não tem a ver diretamente com SQL, mas ajuda.
Conforme a gente vai tendo situações semelhantes, a gente cria funções para facilitar.

Por exemplo: vai criar a query com campos data, numérico, caractere, etc.

Código: Selecionar todos

cSql := Ltrim( Str( nNumero ) )
cSql := ['] + Transform( Dtos( Date() ), "@R 9999-99-99" ) + ['] 
cSql := ['] + cString + [']
Não apenas SQL, poderia ser XML, Json, etc.
Uma função auxiliar facilita.

Código: Selecionar todos

FUNCTION ValueSQL( xValue )
DO CASE
CASE xValue == Nil
   RETURN "NULL"
CASE ValType( xValue ) == "N"
   RETURN Ltrim( Str( xValue ) )
CASE ValType( xValue ) == "D"
   RETURN ['] + Transform( Dtos( xValue ), "@R 9999-99-99" ) + [']
ENDCASE
RETURN ['] + Transform( xValue ) + [']
Ou poderia ser NumberSQL( nNumero ), DateSQL( nNumero ), StringSQL( nNumero )

Depois nos fontes só usar:

Código: Selecionar todos

cSql := ValueSql( xValor )
A vantagem do NumberSQL(), StringSQL() seria poder definir o tipo desejado, independente do conteúdo.
Gravar "1" como numérico ou string, por exemplo.
Isto pode ser útil em migrações, que foi o que eu usei: no MySQL pode gravar "000001" em um campo numérico ou string.
Isso permite mudar a base de dados de caractere para numérica, sem precisar mexer nos fontes depois.

Uma outra que criei:

Código: Selecionar todos

FUNCTION ArrayAsSQL( aList )

   LOCAL cTxt := "", oItem

   FOR EACH oItem IN aList
      IF oItem:__EnumIndex != 1
         cTxt += ", "
      ENDIF
      cTxt += ValueSQL( oItem )
   NEXT

   RETURN cTxt
Isso acima permitiria passar um array { 1, 2, 3 } para a função e ela retornar "1,2,3" pra ser usado no SQL, por exemplo em um IN ( 1,2,3 )
Tanto faz se é data, string, valor, ou tudo misturado.

Funções que ajudam no SQL

Enviado: 29 Abr 2021 13:08
por JoséQuintas
Outro exemplo: em muitas situações só precisa um único campo.
Pra que encher de comandos SQL pra isso em tudo que é fonte.

Código: Selecionar todos

ValueFromSql( "NOME", "C", "CADASTRO", "IDCADASTRO=10" )
Acabei usando do jeito acima.
A função consulta na tabela CADASTRO, onde o IDCADASTRO=10, e retorna NOME, no formato Caractere.
Se tiver NULL, equivalente a Nil, mesmo assim vai retornar string, uma string vazia, é isso que a função faz.

Não muito diferente de DBF:
Você também poderia ter a função pra fazer o SEEK 10 no cadastro, e mostrar o cadastro->Nome.

Então, não é que SQL dá mais trabalho.
É que já temos várias funções prontas pra trabalhar com DBF.
Quando o SQL está no começo, ainda precisamos começar a montar nossa biblioteca conforme a necessidade.
Mas nem sequer sabemos ainda qual a nossa necessidade.

Começamos do jeito mais trabalhoso, mas devemos prestar atenção no que pode facilitar, pra começar a criar nossa lib pra SQL.
Quanto mais usar/facilitar, tudo vai ficar cada vez mais prático.
Primeiro é funcionar, e depois é começar a facilitar, porque só depois de começar e funcionar, é que vamos enxergar o que poderia facilitar.

Também por isso é bom fazer devagar, quando tudo está bem.
Se for tudo pra ontem, não vai dar tempo de facilitar, e pode complicar fontes cada vez mais, sem perceber.

Funções que ajudam no SQL

Enviado: 29 Abr 2021 13:19
por JoséQuintas
Dois exemplos do que mencionei usando junto com minha classe de ADO:

Código: Selecionar todos

FUNCTION ADORecCount( cTable, cWhere, cnSQL )

   LOCAL nValue

   IF cnSQL == NIL
      cnSQL := ADOClass():New( AppConexao() )
   ENDIF
   WITH OBJECT cnSQL
      :cSQL := "SELECT COUNT(*) AS QTD FROM " + cTable + iif( cWhere == Nil .OR. Empty( cWhere ), "", " WHERE " + cWhere )
      :Execute()
      nValue := :Number( "QTD" )
      :CloseRecordset()
   ENDWITH

   RETURN nValue

FUNCTION ADOField( cField, cType, cTable, cWhere, cnSQL )

   LOCAL xResult

   IF cnSQL == NIL
      cnSQL := ADOClass():New( AppConexao() )
   ENDIF
   WITH OBJECT cnSQL
      :cSQL := "SELECT " + cField + " FROM " + cTable + iif( cWhere == Nil .OR. Empty( cWhere ), "", " WHERE " + cWhere )
      :Execute()
      DO CASE
      CASE cType == "N" ; xResult := :Number( cField )
      CASE cType == "D" ; xResult := :Date( cField )
      OTHERWISE         ; xResult := :String( cField )
      ENDCASE
      :CloseRecordset()
   ENDWITH

   RETURN xResult
Quero saber quantos registros tem em clientes, nem precisa saber o que está sendo usado:

Código: Selecionar todos

? ADORecCount( "clientes" )
Quero saber quantos clientes tem em SP:

Código: Selecionar todos

? ADORecCount( "clientes", "CLIUF='SP'" )
Quero o nome do cliente de código 10

Código: Selecionar todos

? ADOField( "NOME", "C", "CLIENTE", "IDCLIENTE=10" )
Não precisa conexão, recordset, comando SQL, nada disso.
Como é algo comum, que acaba sempre precisando em algum lugar do aplicativo, só criar uma função genérica e pronto.

Nem só de SQL vive o aplicativo, mesmo que tudo seja através de comandos SQL.

Funções que ajudam no SQL

Enviado: 29 Abr 2021 13:33
por JoséQuintas
Aí alguém vai dizer..... mas usando LIB tal, faz tudo automático.
Ou até mesmo: usando recursos do ADO não precisa nem comandos SQL....

Ué...
Se podemos criar nossa própria LIB pra isso, do jeito que a gente precisar.... direcionada para nosso uso/estilo....
Porque ficar preso a LIB de alguém, se podemos depender somente de nós?
E fazer tudo de nosso jeito.
Se tudo mudar, vai ser só mudar nossas LIBs e pronto.
Se o ADO deixar de existir, é só ajustar as LIBs, não dependo do ADO pra isso, basta a função ser alterada pra não usar mais ADO.

E por isso é bom ficar nos comandos SQL, e funções pra facilitar, assim não depende de LIB nenhuma, nem do ADO Microsoft.