Rodolfo,
vamos por partes....
O VFP é como Neston tem mil maneiras de fazer, o SQL Server só algumas...
Vou te passar 2 ideias que me vem a mente agora de como se pode tentar resolver o problema.
1) Vc pode criar uma função tipo Verifica_campos( ) na classe do Form ou uma classe generica e em cada formulário antes do metodo para salvar, vc pode verificar se os campos estão preenchidos conforme as regras.
(esta forma é a mais facil pois vc domina o VFP)
O problema deste caso é que o controle fica no frontend em vez de no servidor.
A vantagem é que não existe trafego na rede se a verificação falhar.
2) Outra forma de vc resolver este problema, é vc partir para Stored Procedures (SP) no SQL Server.
Neste caso em lugar de construir um string de inclusão/atualização/exclusão vc terá de criar pelo menos 3 Stored Procedures por tabela para capturar todas as possibilidades.
Neste caso vc tera de construir uma chamada a Stored Procedure no VFP, passando os parametros na mesma ordem dos campos, ou passando o nome de referencia do campo a SP do SQL.
Vc deve saber que pode criar mensagens proprias no SQL Server, desde que tenham uma numeração superior a 50.000 (veja o BOL).
Vamos supor que a sua SP de inclusão basica seja esta...
CREATE PROCEDURE SPA_INS_Clientes
@CPF CHAR(14)
,@NOME CHAR(50)
,@ENDERECO CHAR(50) = NULL
,@OCUPACAO CHAR(50) = NULL
AS
BEGIN TRANSACTION
INSERT INTO Clientes(
CPF
,NOME
,ENDERECO
,OCUPACAO
)
VALUES (@CPF
,@NOME
,@ENDERECO
,@OCUPACAO
)
IF (@@ERROR <> 0) -- Houve ERRO na inserção do registro
BEGIN
ROLLBACK TRANSACTION
RAISERROR 65006 'Erro na inclusão do registro'
END
ELSE
BEGIN
COMMIT TRANSACTION
END
*****
No VFP vc tem que fazer a chamada a esta SP, se fosse via CA o codigo seria desta forma:
EXECUTE SPA_INS_CLIENTES ?ca_Clientes.CPF, ?ca_Clientes.NOME, ?ca_Clientes.ENDERECO, ?ca_Clientes.OCUPACAO
O mesmo vc teria de fazer com Update e Delete.
Vamos supor que vc queira incluir a verificação de alguma coluna...
A Stored Procedure permite que vc faça a verificação antes de 'BEGIN TRANSACTION' no código acima.
Basta vc inserir o codigo equivalente ao do VFP em T-SQL...(um SELECT na Tabela para comparar o valor do CPF do parametro com os CPF cadastrados, se já existir, apresenta mensagem de erro).
(Não vou exibir aqui por falta de tempo) Sorry ;).
Lembra acima que pode criar mensagens proprias no SQL ?
Bem em vez de retornar como no exemplo Acima o erro 65006, que é uma menssagem que eu criei no SQL vc pode simplificar e retornar a mensagem de erro normal e um código.
Vc pode fazer o Aerror( ) do VFP capturar este código e exibir uma mensagem para o seu usuário no frontend em lugar da mensagem do SQL.
Vou abordar algo relacionado ao assunto...
Eu não gosto das constraints pois elas complicam a vida do desenvolvedor.
Prefiro usar uma abordagem do seguinte tipo:
a) fazer chamada a SP no SQL passando os parametros.
b) se tiver de verificar algo, posso fazer a verificação na propria SP antes de inserir/atualizar ou fazendo chamada a um função.
c) Fazer a validação via trigger, chamando Stored Procedures. (Veja o codigo abaixo)
Mas neste caso vc não pode usar Constraints, pois estas são disparadas antes dos Triggers.
INFELISMENTE O SQL server não tem BEFORE trigger.
Segue abaixo um codigo p/ validar CPF p/ SQL Server
Na verdade, é 1 procedure e 1 trigger. O trigger chama as procedures passando CPF e CGC como parâmetros e recebe o retorno de erro que irá determinar se a transação será abortada ou não.
(adapte a ideia para o seu caso)
O trigger que estou apresentando considera que a mesma tabela tenha o campo (CPF), mas você pode adaptar esse trigger de acordo com o seu projeto!
CREATE PROCEDURE VALIDA_CPF
@valor_cpf char(11)
AS
declare @s1 numeric(20,10)
declare @s2 numeric(20,10)
declare @m1 integer
declare @m2 integer
declare @i integer
declare @l char(1)
declare @v integer
declare @d1 integer
declare @d2 integer
declare @mod integer
declare @int integer
declare @fra numeric(20,10)
declare @msg char(50)
declare @cpf char(11)
select @s1 = 0
select @s2 = 0
select @m2 = 2
select @cpf = @valor_cpf
select @i = 9
while 1 = 1
begin
select @l = substring(@cpf,@i,1)
select @m1 = @m2
select @m2 = @m2 + 1
select @v = convert(integer,@l)
select @s1 = @s1 + (@v * @m1)
select @s2 = @s2 + (@v * @m2)
select @i = @i - 1
if @i < 1
break
else
continue
end
select @int = @s1 / 11
select @fra = @s1 / 11
select @mod = round((@fra - @int) * 11,0)
if @mod < 2
begin
select @d1 =0
end
else
begin
select @d1 = 11 - @mod
end
select @s2 = @s2 + (2 * @d1)
select @int = @s2 / 11
select @fra = @s2 / 11
select @mod = round((@fra - @int) * 11,0)
if @mod < 2
begin
select @d2 = 0
end
else
begin
select @d2 = 11 - @mod
end
if substring(@cpf, 10, 2) <> (ltrim(str(@d1)) + ltrim(str(@d2)))
begin
raiserror ('digito verificador do cpf invalido', 16, -1)
return(1)
end
else
return(0)
go
CREATE TRIGGER TRG_CPFCGC ON dbo.Clientes
FOR INSERT,UPDATE
AS
declare @cpf char(11)
declare @cgc char(14)
declare @retorno integer
select @cpf = inserted.cpf from inserted
exec @retorno = valida_cpf @cpf
if @retorno = 1
begin
rollback transaction
end
select @cgc = inserted.cgc from inserted
exec @retorno = valida_cgc @cgc
if @retorno = 1
begin
rollback transaction
end
go
Veja, que desta forma o trigger funciona para 2 situações e chama a mesma SP aliviando o trabalho.
Eu pessoalmente faço uso da verificação usando uma função do tipo Verifica_campos() no VFP bem como triggers e SP no SQL server.
O motivo é o seguinte:
Se o cliente tem uma banda lerda (ADSL), procuro verificar todas as condições possiveis para minimizar o trafego na rede, pois o custo seria de ida e volta, o que pode tornar tudo lento (o gargalo geralmente em sistemas Cliente/servidor esta na rede).
Vc deve estar percebendo que se tem que escrever muito mais codigo no SQL Server que no VFP, bem o meu conselho para este caso é vc criar construtores de Stored Procedures para SQL no proprio VFP e gerar um script p/ SQL.
A maior vantagem no uso de Stored Procedures no SQL é que permite que vc tenha controle de toda a situação.
PS: Procure usar uma nomenclatura para organizar as coisas no SQL, tipo....
TB_<nome que identifica> = TABELAS
VW_<nome que identifica> = VIEWS
FN_<<nome que identifica> = FUNCTION
RL_<nome que identifica> = RULES
DF_<nome que identifica> = DEFAULTS
Procedure sempre iniciando com SPA_
Procedure de inserção - SPA_INS_<nome que identifica>
Procedure de update - SPA_UPD_<nome que identifica>
Procedure de deleteção - SPA_DEL_<nome que identifica>
Procedure de consulta - SPA_CONS_<nome que identifica>
Procedure SPA_<nome que identifica>
Evite usar SP_ como inicio de uma stored procedue, pois gera sobrecarga no sistema, pois este inicio reservado ao SQL que pesquisa tabelas do sistema antes de usar a sua SP.
No lugar va de SPA_ (stored Procedure da Aplicação) ou SPU (stored Procedure do Usuário)
Espero que tenha entendido....
[ ]'s
Peter
>Obrigado pelas respostas... Já estou estudando o SQL aqui!
>
>Estou gostando de algumas coisas, mas ficando um pouco preocupado com outras...
>
>Criei uma tabela chamada Clientes (banco de dados ESTUDO do SQL Server) conforme o script abaixo:
>
>Create Table Clientes
>(
>Codigo Int Identity Constraint Clientes_Codigo_PK Primary Key,
>Cpf Char(14) Constraint Clientes_Cfp_Formato Check (Cpf Like '[0-9][0-9][0-9].[0-9][0-9][0-9].[0-9][0-9][0-9]-[0-9][0-9]') NOT NULL Constraint Clientes_CPF_Unico Unique NonClustered,
>Nome Char(50) NOT NULL,
>Endereco Char(50),
>Ocupacao Char(50) Constraint Clientes_Ocupacao_Default Default 'Consultor Independente'
>)
>
>
>Com esta tabela criada no banco de dados Estudo do SQL Server, vou tentar fazer algumas inclusões no VFP:
>
>
>nConn = SQLStringConnect("driver={sql server};server=(local);database=estudo")
>? SQLExec(nConn,[Insert Into Clientes (Cpf, Nome) Values ('000.000.000-00','Meu Primeiro Cliente')])
>? SqlDisconnect(nConn)
>
>
>Ok - Até aqui tudo perfeito... Inclui meu primeiro cliente. Vamos continuar!
>
>
>nConn = SQLStringConnect("driver={sql server};server=(local);database=estudo")
>
>? SQLExec(nConn,[Insert Into Clientes (Cpf, Nome) Values ('00.000.000-00','Meu Segundo Cliente')])
>* Erro: O Formato do Campo está fora das regras...
>? Str(SqlError[1,1])+" - "+SqlError[1,3]
>* A mensagem é essa:
>* 1526 - [Microsoft][ODBC SQL Server Driver][SQL Server]INSERT statement conflicted with COLUMN CHECK constraint 'Clientes_Cfp_Formato'. The conflict occurred in database 'Estudo', table 'Clientes', column 'Cpf'.
>
>? SQLExec(nConn,[Insert Into Clientes (Cpf, Nome) Values ('000.000.000-00','Meu Segundo Cliente')])
>* Erro: O campo CPF foi duplicado!
>* A mensagem é essa:
>* 1526 - [Microsoft][ODBC SQL Server Driver][SQL Server]Violation of UNIQUE KEY constraint 'Clientes_CPF_Unico'. Cannot insert duplicate key in object 'Clientes'.
>
>? SqlDisconnect(nConn)
>
>
>Bom, agora o bicho pegou... Eu entendi perfeitamente o que aconteceu, mas o usuário não vai entender isso de jeito nenhum. Preciso de uma mensagem amigável, algo como:
Erro 1526 - Formato do CPF inválido! ou então ,
Erro 1526 - CPF já cadastrado!>
>Hoje, usando o DBF com TRIGGER de INSERT e UPDATE, faço algo do tipo:
>
>Function ChecarClientes
> m.MsgErro = Iif(Empty(Nome),'Nome não informado'+Chr(13),'')+;
> Iif(FormatoInvalidoCPF(Cpf),'Formato do CPF inválido'+Chr(13),'')
> Return Empty(m.MsgErro)
>EndFunc
>
>
>Então, a variável m.MsgErro já foi declarada anteriormente e se um trigger falhar, basta pegar a mensagem nesta variável e exibir para o usuário.
>
>Tem jeito de fazer algo parecido com o SQL Server?
>
>PS: O exemplo acima foi referente ao formato do CPF, mas eu teria essa condição por todo o sistema. Além disso, quero usar os relacionamentos entre as tabelas do SQL também: ex: não excluir um produto que tenha vendas, etc etc etc - e neste caso, eu também vou precisar de uma mensagem que o usuário entenda!
>
>Abraços e obrigado pela ajuda!