Restaurou o banco de dados com os índices inativados? Claro que era somente um situação de contingência,
porque você continua precisando deles. Então para REATIVAR os índices novamente, rode este código no
banco de dados:
As tabelas que apresentarem erros, o índice não será ativado, mas você continua precisando dele –
recomendo avaliar e corrigir – depois de corrigir, rode o script novamente.

SET TERM ^;
execute block returns (x varchar(255))
as
declare variable ordem integer;
declare variable stm varchar(1024);
begin
   for
     select y,stm, x from (
        SELECT 1 y,' alter index '||trim(rdb$index_name)||' active ' stm , rdb$index_name x from rdb$indices
    where rdb$index_inactive=1
          and (not rdb$index_name like 'FK%')
          and rdb$unique_flag=0
  union
     SELECT 2 y, ' alter index '||trim(rdb$index_name)||' active ' stm , rdb$index_name x from rdb$indices
     where rdb$index_inactive=1
           and (not rdb$index_name like 'FK%')
           and rdb$unique_flag=1
  union
     SELECT 3 y, ' alter index '||trim(rdb$index_name)||' active ' stm , rdb$index_name x from rdb$indices
     where rdb$index_inactive=1
           and (rdb$index_name like 'FK%')
    ) k order by y
into :ordem, :stm, :x
do
begin
    in autonomous transaction do
       execute statement stm;
    --execute statement 'commit work';
    suspend;
    when any do
      begin
        --exception;
      end
    end
end^
SET TERM;^

O departamento fiscal quer saber se tem alguma nota fiscal faltando no banco de dados.
Cla…ro que não vamos ficar lendo uma lista para ver se tem alguma que pulou número.
Vamos perguntar para o banco de dados.
A mecânica não é muito trivial, já que não tem uma instrução que descubra algo desconhecido,
então vamos preparar o banco para que conheça o problema a ser resolvido.

1) criar uma procedure selecionável que monte uma sequência esperada de números possíveis:

CREATE OR ALTER PROCEDURE DIM_INTEGER (
nmin integer,
nmax integer)
returns (
     numero integer)
as
begin
   numero = nMin;
   while (numero<=nMax) do
   begin
      suspend;
      numero = numero+1;
   end
end

2) agora de posse de uma lista com os números esperados, podemos perguntar para o banco
qual nota esta faltando em um intervalo (ex: entre 1000 e 2000):

select a.numero
from dim_integer( 1000 , 2000 ) a
where not exists (select notafiscal from tab_NotaFiscal b where b.numeroNotaFiscal=a.numero)

3)resultado:
Uma relação de números que não existem na tabela de nota fiscal;

Emprestado de BI, fazer agrupamentos que mostre dados em dimensão de tempo é uma ferramenta
de grande ajuda.
São diversas as situações onde é aplicável dimensionamento de dados por tempo – melhor utilizar
exemplos:
Ex: Deseja saber qual dia da semana as vendas são mais concentradas – Seg, Ter, Qua, Qui, Sex,
Sab, Dom….

with CTE as
(
     select data,wdia,cdia from DIM_DATE('12/01/2015','12/31/2015')
)
Select d.wdia as dia, d.cdia as cdia, sum(valor) as Valor
from cte d join TAB_VENDAS a on (d.data=a.data)
group by d.wdia,d.cdia

Código para a procedure DIM_DATE:

SET TERM ^ ;
CREATE OR ALTER PROCEDURE DIM_DATE (
p_data_de date,
p_data_ate date)
returns (
   data date,
   dia integer,
   wdia integer,
   cdia varchar(1),
   mes integer,
   ano integer,
   semestre integer,
   trimestre integer,
   nsemana integer,
   inimes date,
   fimmes date)
as
declare variable dt date;
begin
   dt = :p_data_de;
   while (dt <= :p_data_ate ) do
   begin
     data = :dt;
     dia = extract(day from :dt);
     wdia = extract(weekday from :dt); -- cdia base
     cdia = substr('DSTQQSS',wdia+1,wdia+1);
     mes = extract(month from :dt);
     ano = extract(year from :dt);
     semestre = case when mes<=6 then 1 else 2 end;
     trimestre = trunc((mes-1) / 3)+1;
     inimes = :dt - dia +1;
     fimmes = dateadd(month, 1,:inimes) - 1;
     nsemana = extract(week from :data);
     suspend;
     dt = dt+1;
   end
end^
SET TERM ; ^
GRANT EXECUTE ON PROCEDURE DIM_DATE TO SYSDBA;

Com a implementação de recurso de criação de funções para o Firebird 3.0, abre-se novos horizontes de recursos no banco de dados.

Entender como criar utilizar funções será uma poderosa ferramenta para soluções do dia-a-dia.

Exemplo

SET TERM ^ ;
create or alter function StartOfMonth(PDate date) returns date...
  as -- retorna a data do primeiro dia do mes
  begin
      return PDate - extract(day from PDate)+1;
  end^
SET TERM ; ^

Utilizando a função

select StartOfMonth( cast(’01/10/2016′ as date) ) from RDB$Database

Nos últimos meses dedico algum tempo lendo as mensagens postadas nos foros e a tempo estava me
coçando para dar meu palpite, dado aos erros básicos que vejo – que vou tentar lembrar de alguns.

1) fazer um select de todas as linhas da tabela e depois fazer um filtro para pegar um único registro.
Correto: é construir um select que retorna só registros de interesse; Ex: select codigo,nome from
usuario where codigo=’123’
2) fazer pouca gestão sobre índices é um erro; Correto: é você por no banco de dados somente
índices que sejam favoráveis… lembrando, um índice demanda tempo de processamento quando faz
INSERT, UPDATE ou DELETE; O SELECT demanda tempo para o motor do banco de dados
escolher qual índice é melhor (no prepare); Então não crie muitos índices se você executa muitas
atualizações… No Update, não altere colunas que não sofreram alteração, para não demandar tempo
para atualizar o índice ( altere somente o necessário );

3) colunas que são candidatas a índice são aquelas que fazem parte de uma WHERE, ORDEM BY
ou GROUP BY… nem todos devem ser transformados em índices… somente alguns… entenda como
o banco de dados usa as estatística para escolher qual o índice é mais adequado para inclui no plano
de otimização;

4) índice composto ou simples ? depende do banco de dados… no Firebird sempre que testei o
resultado melhor foi obtido em índices simples Ex: create index cadastroNome on
produtos(codigo,nome)….. pode não ser tão eficiente quanto criar 2 índices… analise o resultado
antes de criá-los;

5) os índices são bons se a coluna registrar o maior numero possível de informações diferentes… por
isto o melhor índice é o da chave primária, pois existe somente uma linha possível, nenhuma é igual
a outra. Um índice de FILIAL em uma tabela pode não valer NADA se a empresa tem uma única
loja… o mesmo vale para uma coluna S ou N – resulta em muitas linhas com o mesmo valor;

6) usar funções dentro de WHERE ou GROUP BY deve ser avaliado com cuidado, na dúvida eviteos.

7) não use NOT IN ou <> em uma WHERE, em geral o gerenciador ignora todos os índices nestas
situações… Ex: Errado: select qtde from vendas where data<>’today’ (isto mata o índice)… prefira
Correto: select qtde from vendas where ‘today’ >data and ‘today’ < data (isto vai permitir usar o
índice de data);

8) não tenha preguiça…. não pode fazer “select * from xxxxx” (digite as colunas que vai precisar),
isto vai economizar tempo do servidor e de banda de transmissão dos dados;

9) se a sua tabela tem menos de 1000 linhas…. é provável que nem precise de índice. Mas se ela tem
1 milhão de linhas, não dá para ignorar o índice;

10) evite até a morte em criar uma tabela sem indicar a sua chave primária; Bancos de dados foram
feitos para ter chave primária na mais profundo da sua concepção. De a ele uma chave primária para
que ele fique feliz;

11) sempre que for fazer um SELECT tente minimizar ao máximo o número de registro que ele irá
buscar no banco de dados… Se vai precisa da QTDE vendida e o NOME do produto, não faça 2
SELECTs…. junte os 2 em um único SELECT;

12) se acabou de buscar os dados de um cadastro…. não vai buscar de novo o que já conhece…
guarda na memória e reutiliza a informação que já foi obtida. Por isto o GOOGLE é tão rápido – ele
memoriza tudo e vai no buscar somente dados que ainda não conhece.

13) No FIREBIRD, deixar conexões sem dar commit ou as vezes em aberto sem encerrar
adequadamente pode deixar lento o COLETOR DE LIXO do firebird… então inicie e termine um
processo… de preferência, encerre a conexão – isto vai trazer um benefício muito grande para o
banco de dados….. AFINAL devemos escrever para o banco de dados, só assim ele responderá com
eficiência.

Para fechar, “backup “ você vai precisar um dia… faça testes para saber se ele é confiável – não
deixe para descobrir quando for precisar. Não deixe-o no mesmo disco (de preferência, leve para
casa);

Complexas consultas em Firebird podem ser resolvidas utilizando CTE.
CTE é uma construção que monta uma tabela de memória a ser utilizada em um SELECT e é desmontado quando
termina a execução do SELECT.

1) Considerando uma tabela de vendas (existente no banco de dados):

CREATE TABLE vendas (codigo varchar(20), data date, qtde numeric(18,4), valor numeric(18,4) );

2) Inserir alguns dados na tabela que simule as vendas;
3) Executar a seguinte consulta no banco de dados:

with VendasDoMes as
       ( -- agrupa os dados de vendas
         select Codigo,
                extract(month from data) Mes,
                extract(year from data) Ano,
                sum(qtde) Qtde,
                sum(valor) Total
        from vendas
        group by 1,2,3 -- agrupa as colunas
       )
       select * from VendasDoMes --monta a consulta
       where ano=2015 and mes=12
       order by mes, ano

4) Conclusão:
O SELECT irá retornar a quantidade e total de vendas de todos os produto no mês de Dezembro de 2015.

A API do firedac traz um componente que encapsula o nbackup do firebird o que facilita
personalizar o controle de backups. TFDFBNBackup.

Exemplo Nivel 1:

TNBackup.ExecuteNBackup(‘localhost’,’c:\dados\meubanco.fdb’,’sysdba’,’masterkey’,1,’c:\backup\b
ackup2.nbk’);

  • Segestão de como utilizar NIVEL (level):
    a) fazer backup FULL Nivel 0 para um intervalo de período (semanal);
    b) fazer backup Nivel 1, diário;
    c) fazer backup Nivel 2 para backup a cada hora.

Código base:

uses
FireDAC.Phys.IBWrapper,FireDAC.Phys.FB,FireDAC.Phys.FBDef,FireDAC.Comp.UI,FireDAC.Phys;

type

TNBackup = record
   private
     class function GetNBackup(AHost, ABanco, AUser, APass: string;
        ANivel: integer; ADestino: String): TFDFBNBackup;static;
     class function ExecuteNBackup(AHost, ABanco, AUser, APass: string;
        ANivel: integer; ADestino: String): boolean;static;
end;

class function TNBackup.ExecuteNBackup(AHost, ABanco, AUser, APass: string;
     ANivel: integer; ADestino: String): boolean;
begin
   result := false;
   with TNBackup.GetNBackup(Ahost,ABanco,AUser,APass,ANivel,ADestino) do
   try
      Backup; // gerar backup.
      result := true;
   finally
      free;
   end;
end;

class function TNBackup.GetNBackup(AHost, ABanco, AUser, APass: string;
    ANivel: integer; ADestino: String): TFDFBNBackup;
var
nBackup:TFDFBNBackup;
FDGUIxWaitCursorX: TFDGUIxWaitCursor;
FDPhysFBDriverLinkX: TFDPhysFBDriverLink;
begin
     result:=TFDFBNBackup.create(nil);
     try
        FDGUIxWaitCursorX:= TFDGUIxWaitCursor.Create(result);
        FDPhysFBDriverLinkX:= TFDPhysFBDriverLink.Create(result);
        with result do
        begin
           Level := ANivel;
           host := AHost;
           username := AUser;
           password := APass;
           protocol := ipTCPIP;
           Database := ABanco;
           backupfile := ADestino;
           DriverLink := FDPhysFBDriverLinkX;
        end;
   finally
      // liberar a instancia no metodo chamador
   end;
end;

O Firebird traz uma ferramenta de backup pouco falada – “nbackup” – que faz backup incremental.
Quando roda o backup deve indicar qual o nível deseja… Nível 0 – backup completo. Nível 1 –
somente a diferença entre o último nível 0 e o agora. Nível 2 – somente a diferença desde o último
nível….. Importante – ele não faz validação dos dados precisa de estratégia em paralelo para
validação – é muito bom para fazer cópia sem parar o banco “a quente”. É muito rápido, bom para
fazer cópias intermediárias durante a operação.
Exemplo Nivel 0:

nbackup -B 0 localhost:c:\dados\meubanco.fdb c:\backup\backup.bak -U sysdba -P masterkey

Exemplo Nivel 1:

nbackup -B 1 localhost:c:\dados\meubanco.fdb c:\backup\backup1.bak -U sysdba -P masterkey

Como restaurar Nivel 0 +Nivel 1:

nbackup -R c:\dados\teste.fdb c:\backup\backup.bak c:\backup\backup1.bak

Interessante: permite fazer o backup de uma máquina remota.

Requer: https://github.com/amarildolacerda/helpers

Uses System.uJson;
type
   TMinhaClasse = class
   public
     Valor: Double;
     Codigo: string;
   end;

procedure TForm33.FormCreate(Sender: TObject);
var
mc: TMinhaClasse;
begin
    mc := TMinhaClasse.create;
    try
      mc.Codigo := '123456';
      mc.Valor := 10;
      ShowMessage(mc.asJson);
   finally
      mc.Free;
   end;
end;

Lembra quantas vezes você precisou fazer um Loop em um Dataset para fazer uma soma, uma
contagem ou qualquer outra coisa…
Não gosto de fazer de novo algo que já fiz antes… Pensando nisto passei a usar “anonimous
method” do delphi para executar para mim os trechos repetitivos dos loops…
Veja como ficou.

[code lang=”pascal”]
type
TDatasetHelper = class helper for TDataset
public
procedure DoLoopEvent(AEvent: TProc&lt;TDataset&gt;); overload;
end;

procedure TForm34.execute;
var total:Double;
begin
// abere o Dataset com os dados.
alQuery1.sql.Text := ‘select codigo, total valor from sigcaut1 where data&gt;=:data’;
alQuery1.ParamByName(‘data’).AsDateTime := strTodate(’01/01/2016′);
alQuery1.Open;
// fazer um loop para somar o total, usando metodos anonimos;
total := 0;
alQuery1.DoLoopEvent( procedure( ds:TDataset)
begin
total := total + ds.FieldByName(‘valor’).AsFloat; // executa o loop
end);
showMessage( FloatTOStr(total) ); // mostra o total da soma obtida no loop
end;

procedure TDatasetHelper.DoLoopEvent(AEvent: TProc;TDataset;);
var
book: TBookMark;
begin
book := GetBookmark;
try
DisableControls;
first;
while eof = false do
begin
AEvent(self);
next;
end;
finally
GotoBookmark(book);
FreeBookmark(book);
EnableControls;
end;
end;
[/code]

Código Original