8.0 KiB
Rapoarte SQL (meniu Rapoarte → „Rapoarte SQL")
Mecanism de raportare fără .frx: SQL-ul stă în baza de date, aplicația îl execută și afișează
rezultatul într-un grid (cu grupare/ordonare/export Excel). Se instalează printr-un script de
migrare, nu prin VFP IDE.
Lansare și drepturi
Meniuri\rapoarte.mn2:36-37→DO alege_raport WITH 2, .T. IN orapoarte_dinamice.prg.alege_raport(COMUN\programe\orapoarte_dinamice.prg:5-79): al doilea parametrutlDrepturi=.T.⇒ lista se face cujoin rapoarte_utilizatori ru on ... and ru.id_utilizator = gnIdUtil. Un raport fără linii înRAPOARTE_UTILIZATORInu apare deloc în meniu, oricât de corect ar fi.executa_raport(tnIdFormularRaport, tnIdRaport)(:85-107) deschide direct un raport după id, fără lista de alegere.
ID_FORMULAR_RAPORT alege formularul:
| valoare | formular | ce e |
|---|---|---|
| 1 | frm_date_rapoarte_rulaje |
rapoarte bazate pe rulaje |
| 2 | frm_date_rapoarte_sql (COMUN\clase\orapoarte.vc2:4076) |
rapoarte SQL |
| 3 | frm_date_rapoarte_balpart |
balanță parteneri |
Tabele
RAPOARTE—CSQL(CLOB, textul SQL),CSQL_PARAMETRI(XML, definiția parametrilor),DENUMIRE(numele din listă, VARCHAR2(1000)),TITLU(titlul listării, 150),ID_FORMULAR_RAPORT,STERS. Secvență:SEQ_RAPOARTE. View de listare:VRAPOARTE.RAPOARTE_FILTRE— o linie per raport,VALOARE= șir concatenattitlu;camp;pozitie_semn;;valoare|(ex.AN;AN;1;;0|LUNA;LUNA;1;;0|). Generat deparametri_rapoarte_sql(orapoarte_dinamice.prg:186-206), citit la deschidere (orapoarte.vc2:2709). Marcajulluna_curenta|e înlocuit la citire cu luna/anul de lucru.RAPOARTE_UTILIZATORI—(ID_RAPORT, ID_UTILIZATOR), drepturile. FK-uri cătreRAPOARTE: doarRAPOARTE_FILTREșiRAPOARTE_UTILIZATORI.
Parametri <%=NUME%>
În CSQL parametrii se scriu <%=AN%>, <%=LUNA%>. La rulare,
ct_selectie_sql.do_instructiune_sql (COMUN\clase\orapoarte.vc2:1552-1637) înlocuiește
textual (STUFFC) fiecare marcaj cu valoarea din criteriile de selecție — nu sunt bind
variables, deci valoarea intră brut în SQL.
Cuvinte rezervate, completate automat din globale (orapoarte.vc2:1578-1586):
| parametru | sursă |
|---|---|
AN |
gnAn |
LUNA |
gnLuna |
SUCURSALA |
gcCondSucursala |
Acestea trebuie declarate cu tipcamp = 'E', iar campid = numele parametrului; altfel valoarea
nu se rezolvă și apare „Nu este completata valoarea parametrului X!".
Valoarea tastată de utilizator pentru un parametru rezervat este ignorată — se pune global. Deci
un interval (ex. lunile 1..7) cere doi parametri tipcamp = 'N' cu alt nume, iar numele nu are
voie să înceapă cu AN/LUNA/SUCURSALA: potrivirea din do_instructiune_sql e = VFP, iar cu
SET EXACT OFF un LUNA_DE_LA s-ar potrivi cu LUNA. Ex. DE_LA_LUNA / PANA_LA_LUNA, folosite
în SQL ca luna between <%=DE_LA_LUNA%> and <%=PANA_LA_LUNA%>.
Parametrul e citit din expresia de filtru spartă pe AND și apoi pe =, deci merge doar operatorul
„egal" (implicit la N) și numele nu poate conține AND.
CSQL_PARAMETRI = XML VFPData (encoding Windows-1252), câte un nod crsparametri cu
parametru / titlu / camp / tipcamp / campid (+ opțional campcautare); citit cu XMLTOCURSOR în
criterii_rapoarte_sql și parametri_rapoarte_sql (orapoarte_dinamice.prg:109-251).
tipcamp: N numeric, C caracter, D dată, D1 perioadă (se traduce în AN*12+LUNA),
E cuvânt rezervat.
Instalare printr-un script de migrare
Șablon de urmat: D:\ROA\DATABASE\SCRIPTURI_CLAR\2026\07\ff_2026_07_23_01_GESTIUNE_RAPOARTE.sql
(raportul CHELTUIELI CUMULAT - REG JURNAL + RULAJE, peste view-ul VRUL_ACT_CHELTUIELI) și
...\2026\08\ff_2026_08_24_03_GESTIUNE_RAPOARTE.sql (VENITURI SI CHELTUIELI - REG JURNAL + RULAJE, view-urile VRUL_ACT_VEN_CHELT_DET + VRUL_ACT_VEN_CHELT_TOT). Structura:
CREATE OR REPLACE VIEWcu logica raportului (SQL-ul dinCSQLrămâne unselectsimplu peste view — mai ușor de depanat și de refolosit).- Bloc anonim idempotent:
IF (select count(*) from rapoarte where upper(denumire) = '...') = 0→SEQ_RAPOARTE.nextval+MERGEînRAPOARTE+MERGEînRAPOARTE_FILTRE+INSERTînRAPOARTE_UTILIZATORIdinsyn_vutilizatori⋈syn_vdef_util_firme⋈syn_nom_firme where schema = user and nvl(sters,0) = 0, cuinactiv = 0. exec pack_migrare.UpdateVersiune('<nume_script>'); commit;
Capcane
- Commit. Blocurile anonime nu comit singure. Rulat dintr-un client care nu comite la ieșire,
view-urile rămân create (DDL = commit implicit) dar raportul lipsește din
RAPOARTE— arată exact ca o problemă de drepturi. Verificare rapidă:SEQ_RAPOARTE.last_numbera avansat, darselect * from rapoartenu are linia ⇒ lipseștecommit. - Drepturile nu sunt opționale (vezi mai sus).
ACT.ID_PARTD/ACT.ID_PARTCsunt0când nu există partener, niciodatăNULL. UnMIN(ID_PARTD)peste notele unui document întoarce0și numele partenerului iese gol; corect eMIN(NULLIF(ID_PARTD, 0)). Numele se iau dinNOM_PARTENERI.NUME(înVACT_TOTsunt deja rezolvate caPARTD/PARTC).- Ordinea coloanelor din
select-ul dinCSQL= ordinea coloanelor din grid. ORDER BYse scrie înCSQL; coloanele tehnice de sortare pot fi selectate sau nu, dar trebuie să existe în view.
Linii de total în același raport
Pentru totaluri la finalul listării (fără al doilea raport): UNION ALL cu rânduri sintetice care au
coloana de grupare NULL (Oracle sortează NULLS LAST la ASC, deci ajung ultimele) și un
R_TIP mai mare decât cele de detaliu; eticheta se pune într-o coloană text lată (EXPLICATIA),
valoarea în coloana de sumă. Exemplu: VRUL_ACT_VEN_CHELT_TOT, R_TIP 9-15.
Când raportul merge pe un interval de perioade, totalurile nu pot sta în view: view-ul le poate
da doar pe perioadă (altfel rândul agregat are an/luna NULL și cade la filtrul exterior). Deci
view-ul dă totalurile pe lună, iar UNION ALL-ul din CSQL le însumează pe intervalul cerut
(group by r_tip, tip, explicatia) — toate liniile fiind sume, adunarea pe luni e validă. Ramura de
totaluri pune NULL pe an/luna, deci blocul iese o singură dată, la finalul listării.
Performanță: WITH materializat blochează filtrul de perioadă
Într-un view de raport filtrat pe an/luna, o subinterogare WITH referită de două ori e
materializată de Oracle într-un tabel temporar (TEMP TABLE TRANSFORMATION în plan). O subinterogare
materializată nu mai primește predicatul exterior: se construiește tot istoricul și abia la final
se aplică an = ... and luna = .... Pe date de producție asta a însemnat 7.4M rânduri și un plan de
3h15m pentru o lună (VRUL_ACT_VEN_CHELT, versiunea inițială).
/*+ INLINE */în CTE nu a rezolvat-o când CTE-ul e unUNION ALL.- Ce funcționează: view-uri separate în loc de CTE-uri referite de mai multe ori — view-urile se
îmbină (view merging) și primesc predicatul. Vezi
VRUL_ACT_VEN_CHELT_DET(detaliul) +VRUL_ACT_VEN_CHELT_TOT(totalurile pe lună, agregă_DET) înSCRIPTURI_CLAR\2026\08\ff_2026_08_24_03_GESTIUNE_RAPOARTE.sql. - Subinterogările auxiliare (ancore de dată, teste de existență, totaluri de control) citesc
tabelele de bază
ACT/RUL, nuVACT_TOT/VRUL_TOT: view-urile astea nu filtrează rânduri (doar left join-uri de nomenclator), darVRUL_TOTareFDOC/ID_FDOCca subinterogări scalare peACTper rând. Indecși utili:ACTIDX_ACT_006 (AN,LUNA,STERS,…),IDX_ACT_002 (LUNA,AN,SCD,SCC,STERS,…);RULIDX_RUL_003 (AN,LUNA,COD). - Verificare rapidă înainte de livrare:
EXPLAIN PLANpe interogarea dinCSQLcu an/luna concrete — dacă apareTEMP TABLE TRANSFORMATIONsau predicatulAN=/LUNA=nu se vede la accesele peACT/RUL, raportul va scana tot istoricul.