Hi all,
     had an unusual requirement regarding some sql statements and I
wanted to share what I've learned with the list, it might be useful to
someone.

We have 3 tables, say T1, T2, T3.
T1 has a one to many relationship to T2 and to T3.
The customer wants to list the tables as follows.

Say a record in T1 can be linked to 2 records in T2 and 5 records in T3.
Then he would want :
(record #s in parenthesis)(Fn = Field # n)

T1.F1(1)     T1.F2(1)    T1.F3(1)    T1.F4(1)    T2.F1(1)    T2.F2(1)   
T2F3(1)     T3.F1(1)    T3.F2(1)    T3.F3(1)
T1.F1(1)     T1.F2(1)                                        T2.F1(2)   
T2.F2(2)    T2F3(2)     T3.F1(2)    T3.F2(2)    T3.F3(2)
T1.F1(1)     T1.F2(1)                                                
                                             T3.F1(3)    T3.F2(3)   
T3.F3(3)
T1.F1(1)     T1.F2(1)                                                
                                             T3.F1(4)    T3.F2(4)   
T3.F3(4)
T1.F1(1)     T1.F2(1)                                                
                                             T3.F1(5)    T3.F2(5)   
T3.F3(5)
T1.F1(2)     T1.F2(2)    T1.F3(2)    T1.F4(2)    T2.F1(3)    T2.F2(3)   
T2F3(3)     T3.F1(6)    T3.F2(6)    T3.F3(6)
T1.F1(2)     T1.F2(2)                                        T2.F1(4)   
T2.F2(4)    T2F3(4)     T3.F1(7)    T3.F2(7)    T3.F3(7)
T1.F1(2)     T1.F2(2)                                                
                                             T3.F1(8)    T3.F2(8)   
T3.F3(8)
T1.F1(3)     T1.F2(3)    T1.F3(3)    T1.F4(3)    T2.F1(5)    T2.F2(5)   
T2F3(5)     T3.F1(9)    T3.F2(9)    T3.F3(9)
T1.F1(3)     T1.F2(3)                                        T2.F1(6)   
T2.F2(6)    T2F3(6)     T3.F1(10)  T3.F2(10)  T3.F3(10)
T1.F1(3)     T1.F2(3)                                        T2.F1(7)   
T2.F2(7)    T2F3(7)    
T1.F1(3)     T1.F2(3)                                        T2.F1(8)   
T2.F2(8)    T2F3(8)    

With normal joins I would get 1x2x5=10 lines in the first document while
the customer wants only 5.
And 1x2x3=6 lines in the second document while the customer wants only 3.
And 1x4x2=8 lines in the third document while the customer wants only 4.

Had to perform this in SQL Server queries.
So I first found out how to put correlative numbers to every record in
an SQL Server query :
----------------------
select  IDENTITY(smallint, 1, 1) as 'nro', *
into #tempo
from adm35.ordpgd
where ordpgosuc = 1 and ordpgotpo = 1 and ordpgo in (3, 4, 5, 6)

select *
from #tempo

drop table #tempo
----------------------

Then I found out how to make the count start again when a group of
fields changed :
----------------------
select  IDENTITY(smallint, 1, 1) as 'nro', *
into #tempo
from adm35.ordpgd
where ordpgosuc = 1 and ordpgotpo = 1 and ordpgo in (3, 4, 5, 6)

select nro-prim.Numero+1, *
from #tempo tempo
inner join (
            select OrdPgoSuc, OrdPgoTpo, OrdPgo, min(nro) as 'Numero'
            from #tempo
            group by OrdPgoSuc, OrdPgoTpo, OrdPgo
            ) prim
    on prim.OrdPgoSuc = tempo.OrdPgoSuc
        and prim.OrdPgoTpo = tempo.OrdPgoTpo
        and prim.OrdPgo = tempo.OrdPgo

drop table #tempo
----------------------
So now I can have line numbers to a document. The idea being to join the
tables by the corresponding fields AND ALSO by the line numbers in order
to comply with the requirement.

And now in the real query I join T2 and T3 not only by their
corresponding keys but also by their line number (the one that is
restarted for every document), sorry, I don't have the time to make an
easier example right now, this is the actual code (before putting the
filter variables in) where T1 = adm35.OrdCab (purchase orders), T2 =
adm35.AnxPgo (payments), and T3 = adm35.OrdPgd (invoices)

----------------------
select IDENTITY(smallint, 1, 1) as 'nro', Pgd.OrdPgoSuc, Pgd.OrdPgoTpo,
Pgd.OrdPgo
        , Pgd.OrdPgoFOpe, Pgd.OrdPgoTOpe, Pgd.OrdPgoROpe,
Pgd.OrdPgoSOpe, Pgd.OrdPgoNOpe
        , Pgd.OrdPgoIOpe
into #AuxPgd
from adm35.OrdPgd Pgd
inner join adm35.OrdCab OC
    on OC.OrdPgoSuc = Pgd.OrdPgoSuc
        and OC.OrdPgoTpo = Pgd.OrdPgoTpo
        and OC.OrdPgo = Pgd.OrdPgo
where OC.OrdSta <> 'A'
        and OC.OrdFch between '01/01/2008' and '22/01/2008'
Order by Pgd.OrdPgoSuc, Pgd.OrdPgoTpo, Pgd.OrdPgo

select nro-prim.Numero+1 as 'Por_Pgo', AuxPgd.*
into #Pgd
from #AuxPgd AuxPgd
inner join (
            select OrdPgoSuc, OrdPgoTpo, OrdPgo, min(nro) as 'Numero'
            from #AuxPgd
            group by OrdPgoSuc, OrdPgoTpo, OrdPgo
            ) prim
    on prim.OrdPgoSuc = AuxPgd.OrdPgoSuc
        and prim.OrdPgoTpo = AuxPgd.OrdPgoTpo
        and prim.OrdPgo = AuxPgd.OrdPgo
order by AuxPgd.OrdPgoSuc, AuxPgd.OrdPgoTpo, AuxPgd.OrdPgo, AuxPgd.nro
drop table #AuxPgd

select IDENTITY(smallint, 1, 1) as 'nro', AnxSuc, AnxPgoTpo, AnxPgoNro,
AnxOpeEnt
        , AnxTpoOpe, AnxSucOpe, AnxNroOpe, AnxOpeImp, AnxOpeVto
into #AuxAnx
from adm35.AnxPgo Anx
inner join adm35.OrdCab OC
    on OC.OrdPgoSuc = Anx.AnxSuc
        and OC.OrdPgoTpo = Anx.AnxPgoTpo
        and OC.OrdPgo = Anx.AnxPgoNro
where OC.OrdSta <> 'A'
        and OC.OrdFch between '01/01/2008' and '22/01/2008'
order by AnxSuc, AnxPgoTpo, AnxPgoNro

select nro-prim.Numero+1 as 'Por_Pgo', AuxAnx.*
into #Anx
from #AuxAnx AuxAnx
inner join (
            select AnxSuc, AnxPgoTpo, AnxPgoNro, min(nro) as 'Numero'
            from #AuxAnx
            group by AnxSuc, AnxPgoTpo, AnxPgoNro
            ) prim
    on prim.AnxSuc = AuxAnx.AnxSuc
        and prim.AnxPgoTpo = AuxAnx.AnxPgoTpo
        and prim.AnxPgoNro = AuxAnx.AnxPgoNro
order by AuxAnx.AnxSuc, AuxAnx.AnxPgoTpo, AuxAnx.AnxPgoNro, AuxAnx.nro
drop table #AuxAnx

select OC.OrdFch as 'Emision', OC.OrdPrvId as 'Entidad', OC.OrdPrvNom as
'Nombre'
        , OC.OrdPgo as 'OP', case when lins.Renglon = 1 then
TPgd.TotIOpe else NULL end as 'Total_Cancelado'
        , case when lins.Renglon = 1 then OC.OrdIvaImp else NULL end as
'Ret_IVA'
        , case when lins.Renglon = 1 then OC.OrdGanImp1 else NULL end as
'Ret_Gan'
        , case when lins.Renglon = 1 then OC.OrdIBrImp else NULL end as
'Ret_IB'
        , case when lins.Renglon = 1 then OC.OrdSUSSImp else NULL end as
'Ret_SUSS'
        , case when lins.Renglon = 1
                then
TPgd.TotIOpe-OC.OrdIvaImp-OC.OrdGanImp1-OC.OrdIBrImp-OC.OrdSUSSImp
                else NULL end as 'Neto_Pagado'
        , Lins.AnxOpeEnt as 'Bco', Lins.AnxTpoOpe as 'Tipo',
Lins.AnxSucOpe as 'Sucursal'
        , Lins.AnxNroOpe as 'Chq_Nro', Lins.AnxOpeImp as 'Importe',
Lins.AnxOpeVto as 'Fec_Cobro'
        , Lins.OrdPgoFOpe as 'Fec_Pgo', Lins.OrdPgoTOpe as 'Tipo_Pgo',
Lins.OrdPgoROpe as 'Letra'
        , Lins.OrdPgoSOpe as 'Suc_Pgo', Lins.OrdPgoNOpe as 'Nro_Pgo',
Lins.OrdPgoIOpe as 'Imp_pgo'
from adm35.OrdCab OC
left join (select OrdPgoSuc, OrdPgoTpo, OrdPgo, sum(OrdPgoIOpe) as 'TotIOpe'
            from adm35.OrdPgd
            group by OrdPgoSuc, OrdPgoTpo, OrdPgo
            ) TPgd
    on OC.OrdPgoSuc = TPgd.OrdPgoSuc
        and OC.OrdPgoTpo = TPgd.OrdPgoTpo
        and OC.OrdPgo = TPgd.OrdPgo
left join (
            select IsNull(Pgd.OrdPgoSuc, Anx.AnxSuc) as 'Suc',
IsNull(Pgd.OrdPgoTpo, Anx.AnxPgoTpo) as 'Tpo'
                    , IsNull(Pgd.OrdPgo, Anx.AnxPgoNro) as 'Nro',
IsNull(Pgd.Por_Pgo, Anx.Por_Pgo) as 'Renglon'
                    , AnxOpeEnt, AnxTpoOpe, AnxSucOpe, AnxNroOpe,
AnxOpeImp, AnxOpeVto, Pgd.OrdPgoFOpe
                    , Pgd.OrdPgoTOpe, Pgd.OrdPgoROpe, Pgd.OrdPgoSOpe,
Pgd.OrdPgoNOpe, Pgd.OrdPgoIOpe
            from #Pgd Pgd
            full join #Anx Anx
                on Pgd.OrdPgoSuc = Anx.AnxSuc
                    and Pgd.OrdPgoTpo = Anx.AnxPgoTpo
                    and Pgd.OrdPgo = Anx.AnxPgoNro
                    and Pgd.Por_Pgo = Anx.Por_Pgo
            ) Lins
    on OC.OrdPgoSuc = Lins.Suc
        and OC.OrdPgoTpo = Lins.Tpo
        and OC.OrdPgo = Lins.Nro
where  OC.OrdSta <> 'A'
        and OC.OrdFch between '01/01/2008' and '22/01/2008'
order by OC.OrdFch, OC.OrdPrvId, OC.OrdPgo, Lins.Renglon


drop table #Pgd
drop table #Anx
----------------------

These queries will return exactly what I was asked for, they would be
run from a vfp application (through sqlExec()) which will then copy and
format them into an Excel spreadsheet.

Run into a problem, sometimes SQL Server's ODBC will not give any error
but will return no cursors (usually when I use temporary tables like
#AuxPgd), so I had to find a workaround.
This is how I solved it (this is VFP6), these first queries are against
SQL Server (filter variables are in, this is the final product) :
----------------------
select Pgd.OrdPgoSuc, Pgd.OrdPgoTpo, Pgd.OrdPgo
        , Pgd.OrdPgoFOpe, Pgd.OrdPgoTOpe, Pgd.OrdPgoROpe,
Pgd.OrdPgoSOpe, Pgd.OrdPgoNOpe
        , Pgd.OrdPgoIOpe
-- Cursor Salida #AuxPgd#
from adm35.OrdPgd Pgd
inner join adm35.OrdCab OC
    on OC.OrdPgoSuc = Pgd.OrdPgoSuc
        and OC.OrdPgoTpo = Pgd.OrdPgoTpo
        and OC.OrdPgo = Pgd.OrdPgo
where OC.OrdSta <> 'A'
        and OC.OrdPgoSuc = @Empresa
        and OC.OrdFch between @DdeFecha and @HtaFecha
        and (@TipoOP = -1 or OC.OrdPgoTpo = @TipoOP)
        and OC.OrdPgo between @OPDesde and @OPHasta
        and (@Empid = -1 or OC.OrdPrvId = @EmpId)
Order by Pgd.OrdPgoSuc, Pgd.OrdPgoTpo, Pgd.OrdPgo

select AnxSuc, AnxPgoTpo, AnxPgoNro, AnxOpeEnt
        , AnxTpoOpe, AnxSucOpe, AnxNroOpe, AnxOpeImp, AnxOpeVto
-- Cursor Salida #AuxAnx#
from adm35.AnxPgo Anx
inner join adm35.OrdCab OC
    on OC.OrdPgoSuc = Anx.AnxSuc
        and OC.OrdPgoTpo = Anx.AnxPgoTpo
        and OC.OrdPgo = Anx.AnxPgoNro
where OC.OrdSta <> 'A'
        and OC.OrdPgoSuc = @Empresa
        and OC.OrdFch between @DdeFecha and @HtaFecha
        and (@TipoOP = -1 or OC.OrdPgoTpo = @TipoOP)
        and OC.OrdPgo between @OPDesde and @OPHasta
        and (@Empid = -1 or OC.OrdPrvId = @EmpId)
order by AnxSuc, AnxPgoTpo, AnxPgoNro

select OC.OrdFch as 'Emision', OC.OrdPrvId as 'Entidad', Emp.EmpNom as
'Nombre'
        , OC.OrdPgoSuc as 'Suc', Oc.OrdPgoTpo as 'Tipo', OC.OrdPgo as 'OP'
        , TPgd.TotIOpe, OC.OrdIvaImp, OC.OrdGanImp1, OC.OrdIBrImp,
OC.OrdSUSSImp
-- Cursor Salida #OC#
from adm35.OrdCab OC
left join (select OrdPgoSuc, OrdPgoTpo, OrdPgo, sum(OrdPgoIOpe) as 'TotIOpe'
            from adm35.OrdPgd
            group by OrdPgoSuc, OrdPgoTpo, OrdPgo
            ) TPgd
    on OC.OrdPgoSuc = TPgd.OrdPgoSuc
        and OC.OrdPgoTpo = TPgd.OrdPgoTpo
        and OC.OrdPgo = TPgd.OrdPgo
left join adm35.emp Emp
    on Emp.EmpId = OC.OrdPrvId
where  OC.OrdSta <> 'A'
        and OC.OrdPgoSuc = @Empresa
        and OC.OrdFch between @DdeFecha and @HtaFecha
        and (@TipoOP = -1 or OC.OrdPgoTpo = @TipoOP)
        and OC.OrdPgo between @OPDesde and @OPHasta
        and (@Empid = -1 or OC.OrdPrvId = @EmpId)
----------------------

Next, in my vfp application I take the cursors created by these previous
queries and process them (the following are VFP6 queries) :

----------------------
select Recno() as 'nro', *
into cursor PrePgd
from Auxpgd
Order by OrdPgoSuc, OrdPgoTpo, OrdPgo

select OrdPgoSuc, OrdPgoTpo, OrdPgo, min(nro) as 'Numero'
into cursor primPgd
from PrePgd
group by OrdPgoSuc, OrdPgoTpo, OrdPgo

select nro-prim.Numero+1 as 'Por_Pgo', PrePgd.*
into cursor Pgd
from PrePgd
inner join primPgd prim
    on prim.OrdPgoSuc = PrePgd.OrdPgoSuc
        and prim.OrdPgoTpo = PrePgd.OrdPgoTpo
        and prim.OrdPgo = PrePgd.OrdPgo
order by PrePgd.OrdPgoSuc, PrePgd.OrdPgoTpo, PrePgd.OrdPgo, PrePgd.nro

select Recno() as 'nro', *
into cursor PreAnx
from AuxAnx
order by AnxSuc, AnxPgoTpo, AnxPgoNro

select AnxSuc, AnxPgoTpo, AnxPgoNro, min(nro) as 'Numero'
into cursor primAnx
from PreAnx
group by AnxSuc, AnxPgoTpo, AnxPgoNro

select nro-prim.Numero+1 as 'Por_Pgo', PreAnx.*
into cursor Anx
from PreAnx
inner join primAnx prim
    on prim.AnxSuc = PreAnx.AnxSuc
        and prim.AnxPgoTpo = PreAnx.AnxPgoTpo
        and prim.AnxPgoNro = PreAnx.AnxPgoNro
order by PreAnx.AnxSuc, PreAnx.AnxPgoTpo, PreAnx.AnxPgoNro, PreAnx.nro

select NVL(Pgd.OrdPgoSuc, Anx.AnxSuc) as 'Suc', NVL(Pgd.OrdPgoTpo,
Anx.AnxPgoTpo) as 'Tpo'
        , NVL(Pgd.OrdPgo, Anx.AnxPgoNro) as 'Nro', NVL(Pgd.Por_Pgo,
Anx.Por_Pgo) as 'Renglon'
        , AnxOpeEnt, AnxTpoOpe, AnxSucOpe, AnxNroOpe, AnxOpeImp,
AnxOpeVto, Pgd.OrdPgoFOpe
        , Pgd.OrdPgoTOpe, Pgd.OrdPgoROpe, Pgd.OrdPgoSOpe,
Pgd.OrdPgoNOpe, Pgd.OrdPgoIOpe
into cursor Lins
from Pgd
full join Anx
    on Pgd.OrdPgoSuc = Anx.AnxSuc
        and Pgd.OrdPgoTpo = Anx.AnxPgoTpo
        and Pgd.OrdPgo = Anx.AnxPgoNro
        and Pgd.Por_Pgo = Anx.Por_Pgo

select OC.Emision, OC.Entidad, OC.Nombre, OC.Suc, OC.Tipo as 'Tipo_OP',
OC.OP
        , iif(lins.Renglon = 1, transf(OC.TotIOpe), space(20)) as
'Total_Cancelado'
        , iif(lins.Renglon = 1, transf(OC.OrdIvaImp), space(20)) as
'Ret_IVA'
        , iif(lins.Renglon = 1, transf(OC.OrdGanImp1), space(20)) as
'Ret_Gan'
        , iif(lins.Renglon = 1, transf(OC.OrdIBrImp), space(20)) as 'Ret_IB'
        , iif(lins.Renglon = 1, transf(OC.OrdSUSSImp), space(20)) as
'Ret_SUSS'
        , iif(lins.Renglon = 1
                ,
transf(OC.TotIOpe-OC.OrdIvaImp-OC.OrdGanImp1-OC.OrdIBrImp-OC.OrdSUSSImp)
                , space(20)) as 'Neto_Pagado'
        , Lins.AnxOpeEnt as 'Bco', Lins.AnxTpoOpe as 'Tipo',
Lins.AnxSucOpe as 'Sucursal'
        , Lins.AnxNroOpe as 'Chq_Nro', Lins.AnxOpeImp as 'Importe',
Lins.AnxOpeVto as 'Fec_Cobro'
        , Lins.OrdPgoFOpe as 'Fec_Pgo', Lins.OrdPgoTOpe as 'Tipo_Pgo',
Lins.OrdPgoROpe as 'Letra'
        , Lins.OrdPgoSOpe as 'Suc_Pgo', Lins.OrdPgoNOpe as 'Nro_Pgo',
Lins.OrdPgoIOpe as 'Imp_pgo'
from OC
left join Lins
    on OC.Suc = Lins.Suc
        and OC.Tipo = Lins.Tpo
        and OC.OP = Lins.Nro
order by OC.Emision, OC.Entidad, OC.OP, Lins.Renglon

use in select('PrePgd')

use in select('primPgd')

use in select('Pgd')

use in select('PreAnx')

use in select('primAnx')

use in select('Anx')

use in select('Lins')
----------------------

To finally get the result (the last select, the one that has no "into
cursor" clause) and copy and format it into an Excel spreadsheet.
HTH





_______________________________________________
Post Messages to: [email protected]
Subscription Maintenance: http://leafe.com/mailman/listinfo/profox
OT-free version of this list: http://leafe.com/mailman/listinfo/profoxtech
Searchable Archive: http://leafe.com/archives/search/profox
This message: http://leafe.com/archives/byMID/profox/[email protected]
** All postings, unless explicitly stated otherwise, are the opinions of the 
author, and do not constitute legal or medical advice. This statement is added 
to the messages for those lawyers who are too stupid to see the obvious.

Reply via email to