Glauco, como parece que vc tá começando, vou fazer um passo-a-passo bem simples, pra vc entender o mecanismo.
POINTER
Existe no Clipper um cara chamado POINTER (ponteiro), que indica em qual registro do DBF você está. Além disso, além dos registros digamos normais, existem no DBF 2 registros "fantasmas": o BOF e o EOF. Pra que? Veja a seguir o funcionamento das funções EOF() e BOF().
1. vc cria um DBF com só um campo NOME e abre ele SEM indice: USE CLIENTES NEW
2. como ele não tem nenhum registro, o POINTER fica no registro BOF = Begin of File = Início de Arquivo
3. se vc fizer GO BOTT (vá pra base), o POINTER vai parar no registro EOF = End of File = Fim de Arquivo
4. APPEND BLANK: o Clipper ADICIONA um registro em branco no arquivo (número 1) e move o POINTER para ele.
5. REPLACE NOME WITH "XIMENES": o registro 1 vai receber esse nome.
6. novo APPEND BLANK: é adicionado o registro número 2 e o POINTER é movido pra ele.
7. REPLACE NOME WITH "ALVARO": o registro 2 vai receber esse nome.
8. novo APPEND BLANK: é adicionado o registro número 3 e o POINTER é movido pra ele.
9. REPLACE NOME WITH "JOSE": o registro 3 vai receber esse nome.
* quer confirmar? faça ?RECNO() -> vai aparecer 3
* RECNO = Record Number = Número do registro
Fica o seguinte:
Reg1 = XIMENES
REg2 = ALVARO
REg3 = JOSE
10. GO TOP (vá pro topo): faz mover POINTER para o topo do arquivo (registro 1 = XIMENES)
11. SKIP (pule): faz o POINTER pular para o próximo registro (2 = ALVARO)
12. SKIP -1 (pule pra trás): faz o POINTER pular para o registro anterior (1 = XIMENES)
13. GO BOTT (vá pra base): faz mover o POINTER para o fim do arquivo (registro 3 = JOSE)
14. LOCATE FOR NOME="ALVARO": procura pelo nome e move o POINTER para o registro 2
15. LOCATE FOR NOME="XPTO": procura e, como não achou, move o POINTER para o EOF
Bem, até aqui foi SEM indice. Então o SKIP e o GO TOP/BOTT fizeram o POINTER ser movido de acordo com a ordem natural dos registros (ordem de entrada). Agora, vamos usar um ÍNDICE:
16. INDEX ON NOME TO CLIENTES
17. Daqui pra frente, não é mais a ordem natural (de gravação), mas a ordem alfabética do campo NOME. O arquivo DBF continua guardando os nomes, mas agora ele vai trabalhar em conjunto com o arquivo NTX, que contém a posição relativa de cada registro. Fica o seguinte:
REg2 = ALVARO
REg3 = JOSE
Reg1 = XIMENES
18. GO TOP: faz o POINTER pular para o registro 2 = ALVARO
19. SKIP: faz o POINTER pular para o registro 3 = JOSE
20. SKIP: faz o POINTER pular para o registro 1 = XIMENES
21. SKIP -1: faz o POINTER pular para o registro 3 = JOSE
22. GO BOTT: faz o POINTER pular para o registro 1 = XIMESES
Agora, o SEEK (tradução = PROCURAR):
23. SEEK "JOSE": o POINTER vai pular para o registro 3
24. REPLACE NOME WITH "JOSE DA SILVA": vai mudar o conteúdo do registro 3...
25. SEEK "ALVARO": o POINTER vai pro registro 2
Se o arquivo tiver 1.500.000 de registros e o último deles for "XIMENES", se vc fizer SEEK "XIMENES" o POINTER vai pular instantaneamente pra esse registro, e você já pode fazer por exemplo REPLACE NOME WITH "XIMENES SOUZA"
Ao contrário, se vc usar o LOCATE, ocorre o seguinte: o Clipper examina o registro 1. Se não é XIMENES, vai pro registro 2. Idem, vai pro 3... Assim por diante, até chegar no registro número 1.500.000...
Agora, vamos supor que vc mude o nome do "XiMENES" para "ANTONIO XIMENES". O número do registro (FÍSICO) vai continuar 1 mas a ordem vai mudar automaticamente:
REg2 = ALVARO
Reg1 = ANTONIO XIMENES
REg3 = JOSE
Por fim, entra uma função FOUND() = a tradução é "ACHOU", que vc usa junto com o SEEK e as funções EOF() e BOF(). Se vc fizer:
SEEK "ALVARO"
* como existe esse nome, o POINTER vai pro registro 2
* e as funções retornam o seguinte:
?FOUND() -> .T.
?EOF() -> .F.
?BOF() -> .F.
SEEK "XIMENES"
* como não existe mais, o POINTER vai pro EOF
* e as funções retornam o seguinte:
?FOUND() -> .F.
?EOF() -> .T.
?BOF() -> .F.
GO BOTT // vai pro registro JOSE
SKIP // pula pro próximo
* como não tem o próximo...
? EOF() -> .T.
INDEXAÇÂO
No exemplo acima, fiz a indexação só pelo campo NOME. Mas vc pode "misturar" campos no mesmo índice: INDEX ON DTOS(NASC)+NOME -> ordena por data de nascimento e, dentro de cada data, por nome.
Outra coisa: não é preciso fazer a INDEXAÇÂO toda vez que vc abre o arquivo! Você cria o DBF e o INDICE e depois, é só abrir os dois em conjunto: quando vc incluir um registro no DBF, o NTX vai ser atualizado. Mesma coisa na exclusão ou alteração. Veja abaixo em REDE.
Só precisa ficar atento para SEMPRE abrir DBF e NTX em conjunto. Se vc abrir só o DBF e alterar algum registro (que está no indice), da próxima vez que vc abrir o NTX junto vai dar erro (arquivo corrompido).
REDE
Em rede, vc TEM que usar o seguinte:
a) a cláusula EXCLUSIVE
USE CLIENTES NEW EXCLUSIVE
Neste caso, o arquivo ficará de uso exclusivo para você. Ninguém consegue nem visualisar o danado. Esta cláusula é EXIGIDA quando vc faz a iNDEXAÇÂO ou usa os comandos ZAP ou PACK.
b) a cláusula SHARED (compartilhado)
USE CLIENTES NEW SHARED
Neste caso, o arquivo pode ser aberto por mais de um usuário e aí, quando precisar ser feita alguma alteração no DBF, tem que usar o FLOCK() ou o RLOCK().
b.1) FLOCK() = FILE LOCK = Bloqueio do Arquivo: com este comando, o arquivo SHARED continua acessível a todos na rede mas só pode ser alterado por quem conseguiu o comando. É usado quando vc tem que alterar muitos registros e não pode/quer ter mais ninguém mexendo no DBF.
b.1) RLOCK() = RECORD LOCK = Bloqueio do Registro: com este comando, só é bloqueado o REGISTRO onde está o POINTER. Por exemplo, vc está alterando o nome do XIMENES e outro usuário, no mesmo momento, pode estar alterando o nome do ALVARO.
No caso da cláusula EXCLUSIVE, a condição só acaba quando vc fechar o DBF.
No caso do FLOCK() ou RLOCK(), o comando UNLOCK desbloqueia o arquivo ou o registro.
Agora, um exemplo: note que cada programador tem um "jeitão" de trabalhar e existem "n" maneiras de fazer a mesma coisa. Também, o programa vai depender dos objetivos a atingir. Ainda, este exemplo é só uma sequencia de comandos, pra vc ver alguns detalhes. Precisa colocar isso tudo dentro da seu programa.
O IMPORTANTE é vc entender o mecanismo. Depois vc aplica no seu programa. Não use a técnica do COLAR / COPIAR. Via de regra não dá certo... eh eh eh
if (!file("CLIENTES1.NTX") .or. !file("CLIENTES2.NTX"))
* faz a indexação só se os NTX não existirem
use clientes new excl // bastam as primeiras 4 letras...
if neterr()=.t.
* a função NETERR() retorna .T. se deu erro e .F. se não
wait "Não deu pra abrir o DBF!"
quit
endif
index on nome to clientes1
index on prontuario to clientes2
use
endi
use clientes new shar
if neterr()=.t.
wait "Não deu pra abrir o DBF!"
quit
endif
set index to clientes1, clientes2
set orde to 2 // prontuario
set orde to 1 // nome
appe blan
* este comando já bloqueia o novo registro em branco
repl nome with "XIMENES"
repl prontuario with 11
unlock
appe blan
repl nome with "SILVA"
repl prontuario with 22
unlock
seek "XXIMENES"
* não vai achar, certo?
IF !FOUND()
wait "não achou XXIMENES"
ENDIF
SEEK "XIMENES"
* agora vai achar, mas precisa bloquear o registro.
* note vou vou colocar um CONTADOR de tentativas
* se fizer como vc fez, sem limite, e se algum outro usuario bloqueou o registro
* e esqueceu de sair do programa, vc vai passar o resto do dia parado
* esperando ele sair...
oquei=.f.
@ 24,25 SAY " AGUARDE... "
for conta=1 to 5 // TENTANTIVAS
if rlock()=.T.
oquei=.t.
EXIT
ENDI
inkey(10) // faz uma pausa entre as tentativas
conta--
next
@ 24,25 SAY " "
if oquei=.f.
wait "não consegui bloquear o registro!"
else
repl nome with "ANTONIO XIMENES"
dbcommit()
* veja abaixo
unlock
endi
* THE END
quit
DBCOMITT() e DBCOMMITALL(): quando vc faz alguma alteração em um DBF (exclusão ou REPLACE), ela pode não ficar visível na hora para toda a rede (o SO pode estar ocupado, cuidando de outras coisas...). Com o DBCOMMIT(), vc força a coisa a acontecer na hora. DBCOMMIT() age só na área de trabalho em foco (a que está SELECT-cionada). DBCOMMITALL() age em todas (ALL = todas).
Bom, escrevi isto tudo meio direto. Pode ser que tenha algum erro.
Se tiver mais dúvidas, buzina.