Às vezes podemos ser enganados por sutilizas da linguagem e perder um bom tempo tentando entender o motivo do erro. Observem os códigos abaixo:
Embedded SQL :: %exp:cExp% : Exemplo 1 |
1: #include "protheus.ch" 2: #include "tbiconn.ch" 3: 4: #xtranslate USER PROCEDURE <p> => PROCEDURE U_<p> 5: 6: USER PROCEDURE SqlEmb() 7: 8: Local aLastQuery 9: 10: Local cLike 11: Local cQuery 12: Local cError 13: Local cTCSqlError 14: 15: Local cAlias 16: Local bError 17: Local bErrorBlock 18: 19: Local oError 20: 21: RpcSetType(3) 22: PREPARE ENVIRONMENT EMPRESA "01" FILIAL "01" 23: 24: bError := { |e| oError := e , BREAK(e) } 25: bErrorBlock := ErrorBlock( bError ) 26: 27: cLike := "F%" 28: cAlias := GetNextAlias() 29: 30: BEGIN SEQUENCE 31: 32: BEGINSQL ALIAS cAlias 33: %noParser% 34: SELECT 35: SRA.* 36: FROM 37: %table:SRA% SRA 38: WHERE 39: SRA.%notDel% 40: AND 41: SRA.RA_NOME LIKE %exp:cLike% 42: ENDSQL 43: 44: aLastQuery := GetLastQuery() 45: cLastQuery := aLastQuery[2] 46: 47: (cAlias)->( dbCloseArea() ) 48: 49: RECOVER 50: 51: aLastQuery := GetLastQuery() 52: cLastQuery := aLastQuery[2] 53: 54: cError := oError:Description 55: cTCSqlError := TCSqlError() 56: 57: END SEQUENCE 58: ErrorBlock( bErrorBlock ) 59: 60: RESET ENVIRONMENT 61: 62: Return( NIL ) |
No exemplo acima montamos a expressão que será utilizada na clausula LIKE como:
Obtendo, corretamente, a seguinte instrução:
SELECT SRA.* FROM SRA010 SRA WHERE SRA.D_E_L_E_T_= ' ' AND SRA.RA_NOME LIKE 'F%' |
Que, quando executada, nos retornará a seguinte “View”
|
Uma simples alteração e erros colaterais poderão ocorrer. Observe:
Embedded SQL :: %exp:cExp% : Exemplo 2 |
1: #include "protheus.ch" 2: #include "tbiconn.ch" 3: 4: #xtranslate USER PROCEDURE <p> => PROCEDURE U_<p> 5: 6: USER PROCEDURE SqlEmb() 7: 8: Local aLastQuery 9: 10: Local cLike 11: Local cQuery 12: Local cError 13: Local cTCSqlError 14: 15: Local cAlias 16: Local bError 17: Local bErrorBlock 18: 19: Local oError 20: 21: RpcSetType(3) 22: PREPARE ENVIRONMENT EMPRESA "01" FILIAL "01" 23: 24: bError := { |e| oError := e , BREAK(e) } 25: bErrorBlock := ErrorBlock( bError ) 26: 27: cLike := "%F%" 28: cAlias := GetNextAlias() 29: 30: BEGIN SEQUENCE 31: 32: BEGINSQL ALIAS cAlias 33: %noParser% 34: SELECT 35: SRA.* 36: FROM 37: %table:SRA% SRA 38: WHERE 39: SRA.%notDel% 40: AND 41: SRA.RA_NOME LIKE %exp:cLike% 42: ENDSQL 43: 44: aLastQuery := GetLastQuery() 45: cLastQuery := aLastQuery[2] 46: 47: (cAlias)->( dbCloseArea() ) 48: 49: RECOVER 50: 51: aLastQuery := GetLastQuery() 52: cLastQuery := aLastQuery[2] 53: 54: cError := oError:Description 55: cTCSqlError := TCSqlError() 56: 57: END SEQUENCE 58: ErrorBlock( bErrorBlock ) 59: 60: RESET ENVIRONMENT 61: 62: Return( NIL ) |
Neste segundo exemplo, montamos a expressão que será utilizada na clausula LIKE como:
Fazendo com que Embedded SQL não conseguisse resolver corretamente a nossa intenção retornando:
SELECT SRA.* FROM SRA010 SRA WHERE SRA.D_E_L_E_T_= ' ' AND SRA.RA_NOME LIKE F |
Uma instrução inválida. A instrução acima fará com que a seguinte exceção será gerada:
: Error : 207 (S0022) (RC=-1) - [Microsoft][ODBC SQL Server Driver][SQL Server]Invalid column name 'F'. ( From tMSSQLConnection::GetQueryFile ) Thread ID [7748] User [marinaldo.jesus] IO [1877] Tables [2] MaxTables [3] Comment [] Status [] SP [ ] Traced [No] InTran [No] DBEnv [MSSQL/rnp] DBThread [(SPID: 55) ] Started [26/05/2012 16:37:07] LastIO [] IP [127.0.0.1] RCV [22054] SND [61739] TCBuild [20110919] SELECT SRA.* FROM SRA010 SRA WHERE SRA.D_E_L_E_T_= ' ' AND SRA.RA_NOME LIKE F |
|
Para contornar o problema tentaremos resolver, alterando a instrução para: cLike := "'%F%'"
Embedded SQL :: %exp:cExp% : Exemplo 3 |
1: #include "protheus.ch" 2: #include "tbiconn.ch" 3: 4: #xtranslate USER PROCEDURE <p> => PROCEDURE U_<p> 5: 6: USER PROCEDURE SqlEmb() 7: 8: Local aLastQuery 9: 10: Local cLike 11: Local cQuery 12: Local cError 13: Local cTCSqlError 14: 15: Local cAlias 16: Local bError 17: Local bErrorBlock 18: 19: Local oError 20: 21: RpcSetType(3) 22: PREPARE ENVIRONMENT EMPRESA "01" FILIAL "01" 23: 24: bError := { |e| oError := e , BREAK(e) } 25: bErrorBlock := ErrorBlock( bError ) 26: 27: cLike := "'%F%'" 28: cAlias := GetNextAlias() 29: 30: BEGIN SEQUENCE 31: 32: BEGINSQL ALIAS cAlias 33: %noParser% 34: SELECT 35: SRA.* 36: FROM 37: %table:SRA% SRA 38: WHERE 39: SRA.%notDel% 40: AND 41: SRA.RA_NOME LIKE %exp:cLike% 42: ENDSQL 43: 44: aLastQuery := GetLastQuery() 45: cLastQuery := aLastQuery[2] 46: 47: (cAlias)->( dbCloseArea() ) 48: 49: RECOVER 50: 51: aLastQuery := GetLastQuery() 52: cLastQuery := aLastQuery[2] 53: 54: cError := oError:Description 55: cTCSqlError := TCSqlError() 56: 57: END SEQUENCE 58: ErrorBlock( bErrorBlock ) 59: 60: RESET ENVIRONMENT 61: 62: Return( NIL ) |
Ao alterar a instrução para:
Não resolveremos o problema. Obteremos, na verdade, uma nova exceção, Observe, baseado no exemplo acima, como Embedded SQL retorna a instrução:
SELECT SRA.* FROM SRA010 SRA WHERE SRA.D_E_L_E_T_= ' ' AND SRA.RA_NOME LIKE ''%F%'' |
Causa da seguinte exceção:
: Error : 207 (S0022) (RC=-1) - [Microsoft][ODBC SQL Server Driver][SQL Server]Invalid column name 'F'. ( From tMSSQLConnection::GetQueryFile ) Thread ID [7392] User [marinaldo.jesus] IO [1877] Tables [2] MaxTables [3] Comment [] Status [] SP [ ] Traced [No] InTran [No] DBEnv [MSSQL/rnp] DBThread [(SPID: 55) ] Started [26/05/2012 16:48:01] LastIO [] IP [127.0.0.1] RCV [22058] SND [61739] TCBuild [20110919] SELECT SRA.* FROM SRA010 SRA WHERE SRA.D_E_L_E_T_= ' ' AND SRA.RA_NOME LIKE ''%F%'' |
|
A Solução definitiva: cLike := "%'%F%'%"
Embedded SQL :: %exp:cExp% : Exemplo 4 |
1: #include "protheus.ch" 2: #include "tbiconn.ch" 3: 4: #xtranslate USER PROCEDURE <p> => PROCEDURE U_<p> 5: 6: USER PROCEDURE SqlEmb() 7: 8: Local aLastQuery 9: 10: Local cLike 11: Local cQuery 12: Local cError 13: Local cTCSqlError 14: 15: Local cAlias 16: Local bError 17: Local bErrorBlock 18: 19: Local oError 20: 21: RpcSetType(3) 22: PREPARE ENVIRONMENT EMPRESA "01" FILIAL "01" 23: 24: bError := { |e| oError := e , BREAK(e) } 25: bErrorBlock := ErrorBlock( bError ) 26: 27: cLike := "%'%F%'%" 28: cAlias := GetNextAlias() 29: 30: BEGIN SEQUENCE 31: 32: BEGINSQL ALIAS cAlias 33: %noParser% 34: SELECT 35: SRA.* 36: FROM 37: %table:SRA% SRA 38: WHERE 39: SRA.%notDel% 40: AND 41: SRA.RA_NOME LIKE %exp:cLike% 42: ENDSQL 43: 44: aLastQuery := GetLastQuery() 45: cLastQuery := aLastQuery[2] 46: 47: (cAlias)->( dbCloseArea() ) 48: 49: RECOVER 50: 51: aLastQuery := GetLastQuery() 52: cLastQuery := aLastQuery[2] 53: 54: cError := oError:Description 55: cTCSqlError := TCSqlError() 56: 57: END SEQUENCE 58: ErrorBlock( bErrorBlock ) 59: 60: RESET ENVIRONMENT 61: 62: Return( NIL ) |
Embedded SQL irá resolver corretamente a instrução. Convertendo, literalmente:
gerando a seguinte instrução:
SELECT SRA.* FROM SRA010 SRA WHERE SRA.D_E_L_E_T_= ' ' AND SRA.RA_NOME LIKE '%F%' |
Que ao ser executada, retornara, conforme o previsto:
|
Mas porquê isso acontece?
O “parser” do Embedded SQL irá resolver, literalmente, o conteúdo entre um duplo par de %% (considerando, aqui o % de %exp:cExp%).
Fica a Dica. O mesmo exemplo poderá ser utilizado para obter, dinamicamente, o nome de uma tabela. Observe o exemplo abaixo em que não quero utilizar %table:[Alias]% para retornar o nome de uma tabela, mas sim usar uma expressão. |
Embedded SQL :: %exp:cExp% : Exemplo 5 |
1: #include "protheus.ch" 2: #include "tbiconn.ch" 3: 4: #xtranslate USER PROCEDURE <p> => PROCEDURE U_<p> 5: 6: USER PROCEDURE SqlEmb() 7: 8: Local aLastQuery 9: 10: Local cLike 11: Local cQuery 12: Local cError 13: Local cTCSqlError 14: 15: Local cAlias 16: Local bError 17: Local bErrorBlock 18: 19: Local oError 20: 21: RpcSetType(3) 22: PREPARE ENVIRONMENT EMPRESA "01" FILIAL "01" 23: 24: bError := { |e| oError := e , BREAK(e) } 25: bErrorBlock := ErrorBlock( bError ) 26: 27: cLike := "%'%F%'%" 28: cTable := "%" + RetSqlName("SRA") + "%" 29: cAlias := GetNextAlias() 30: 31: BEGIN SEQUENCE 32: 33: BEGINSQL ALIAS cAlias 34: %noParser% 35: SELECT 36: SRA.* 37: FROM 38: %exp:cTable% SRA 39: WHERE 40: SRA.%notDel% 41: AND 42: SRA.RA_NOME LIKE %exp:cLike% 43: ENDSQL 44: 45: aLastQuery := GetLastQuery() 46: cLastQuery := aLastQuery[2] 47: 48: (cAlias)->( dbCloseArea() ) 49: 50: RECOVER 51: 52: aLastQuery := GetLastQuery() 53: cLastQuery := aLastQuery[2] 54: 55: cError := oError:Description 56: cTCSqlError := TCSqlError() 57: 58: END SEQUENCE 59: ErrorBlock( bErrorBlock ) 60: 61: RESET ENVIRONMENT 62: 63: Return( NIL ) |
Neste novo exemplo o retorno da instrução SQL será:
SELECT SRA.* FROM SRA010 SRA WHERE SRA.D_E_L_E_T_= ' ' AND SRA.RA_NOME LIKE '%F%' |
|
[]s
иαldσ dj
1 ano depois, mais um novato feliz :)
ResponderExcluir2 ANOS DEPOIS, MAIS UMA PESSOA FELIZ! obrigado
ResponderExcluir3 anos depois mais um desenvolvedor FELIZ!!!
ResponderExcluir3 Anos depois mais um Desenvolvedor Feliz...
ResponderExcluir3,99 anos depois, mais um dev feliz.
ResponderExcluirEm pleno 2019, ainda fazendo desenvolvedores felizes.
ResponderExcluirAinda em 2019, ajudando a fazer dev feliz.
ResponderExcluirMeio de 2019... ainda me salvando.
ResponderExcluirIsso me ajudou muito, mas tenho uma dúvida maior.
ResponderExcluirele precisa dar LIKE '%CAMPO%', e não em uma variável com um valor fixo.
Como eu faço?