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;

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.