Firebird, UDF, brak koncepcji na funkcję

0

Witam, napotkałem na brzydki problem z obliczeniami na datach w Firebird 2.0. Opis problemu:

Baza posiada dwie tabele:
1). Lista spraw, która ma pola:
- Data rozpoczęcia: BeginDate
- Okres przedawnienia: Term
2). Okresy "pauzy" - Przedziały czasowe które zwiększają Term o ileś dni. Tabele złączone relacja
- Data początku: FromDate
- Data końca: ToDate

Problem mam z napisaniem UDFa który obliczy datę końca: EndDate z uwzględnieniem rekordów z tabeli [2]. Zwykły UDF który dodałby Term lat do BeginDate jest prosty. Nie wiem natomias jak ugryźć problem pobierania w UDF wartości z tabeli 2. Zrobiłem dll z DataModule. Niestety program się zawiesza (i serwer bazy) nawet nie wiem w jakim miejscu.

Jak w UDF zrealizować działanie:
EndDate := BeginDate + Term + SIGMA (ToDate-FromDate)

Wszystko się sypie gdy wewnątrz UDF chcę zrobić SELECT * FROM

// P.S Program się sypie podczas tworzenia TDataModule. Nie wiem kiedy mam utworzyć obiekt.

0

Adam po ch** Ci UDF??? Takie rzeczy robi się w stored proc. UDF to biblioteka z różnymi funkcjami (jak zwykła biblioteka), która NIE łączy się z bazą a jedynie wykonuje coś na przekazanych jej danych

0

UDFy używa się tylko do rzeczy z którymi nie radzi sobie baza. Wszystkie inne rzeczy robisz w zwykłych procedurach SQL-owych.

0

Ok, dzięki za wskazówki. Pokombinuję :-)

Kombinuję i nic nie wychodzi. Jak deklaruję

SELECT * FROM Procedura(xxx);

To działa

Ja potrzebuję

SELECT Procedura(xxx) FROM Tabela;

Podczas takiego zapisu baza krzyczy, że procedura jest niezdefiniowana

0

EXECUTE PROCEDURE PROCEDURA(XXX)

albo

SELECT * FROM PROCEDURA(XXX)

Tak jak Ty chcesz używa się funkcji. Chyba że tak

SELECT POLE1, (SELECT * FROM PROCEDURA(POLE2)) FROM TABELA

B

0

Tak czytam to Twoje pytanie i nie mogę zaczaić o co Ci chodzi. Daj może kilka rekordów z obu tabelek i napisz jaki byś chciał wynik.

b

0

Mam w uproszczeniu bazę:

CREATE TABLE TabelaGlowna (
 IDTabelaGlowna INTEGER,
 DataRozpoczecia DATE // Data wszczęcia postępowania
 Okres INTEGER // Po tylu latach nastąpi przedawnienie
); 

CREATE TABLE Daty (
  IDDaty,
  TabelaGlownaID FOREIGN KEY REFERENCES TabelaGlowna,
  Od DATE, // Start amnezji
  Do DATE // Koniec amnezji
);

Rozpoczęcie sprawy: 2004-12-31
Bieg przedawnienia: 5 lat
Amnezia: 2005-07-07..2009-07-12

Data rozpoczęcia + Bieg przedawnienia + Suma dni okresów amnezji

Data zakończenia sprawy: 2014-01-06

I właśnie problem mam, że muszę mieć funkcję która mi to będzie liczyła rekord po rekordzie w SELECT pobierając dane z powiązanej tabeli

SELECT
  DataRozpoczecia,
  ObliczDataKonca(DataRozpoczecia)
FROM
  TabelaGlowna

UDF mi się sypie bo muszę robić select z innej tabeli, łączyć się z bazą i takie tam.
Stored proc nie może być użyty jak funkcja.

Czy mam możliwość zarejestrować funkcję w bazie ale trzymaną na serwerze a nie w dll?

Jakie inne możliwości na wyliczenie daty końca? Trzymanie pola w bazie i na sztywno to masakra i niebezpieczne. Muszę obliczać w select ale nie mam pomysłów jak

0

ech Adamie. Czy chodzi Ci o coś takiego?

założenia
tabela 1
t1 zadania
id int id zadania
BeginDate date data rozpoczęcia
Term int termin w dniach

t2 pauzy
id int id pauzy
t1_id int id zadania, którego dotyczy pauza
FromDtae date od kiedy
ToDate date do kiedy

SELECT t1.id, t1.BeginDate, t1.BeginDate + t1.Termin + t3.Termin FROM t1, (SELECT SUM(DateDiff(DAY, t2.FromDate, t2.ToDate)) Termin FROM t2 WHERE t2.t1_id = t1.id) t3;
0

Zapytanie boskie. Problem jednak inny. Okresy pauzy mają klika wariantów:

Od Do
* (1)
****** (2)
********* (3)
********* (4)
****** (5)


         |                             |
      Begin                       End

Do obliczeń muszę brać część wspólną zbiorów. problem mam wariantami (2) i (4).

0

dla tabelki z pauzami wyglądającej tak

CREATE TABLE PAUZY (
    PAUZA_ID    INTEGER NOT NULL,
    ZADANIE_ID  INTEGER,
    FROMDATE    DATE,
    TODATE      DATE
);

masz procedurę liczącą "poślizg" dla konkrtnego id zadania

CREATE PROCEDURE DAJ_PRZEDZIAL (
    zadanie_id integer)
returns (
    przedzial integer)
as
declare variable akt_begin date;
declare variable akt_end date;
declare variable tmp_begin date;
declare variable tmp_end date;
begin
  przedzial = 0;
  select min(fromdate) from pauzy where zadanie_id = :zadanie_id into :tmp_begin;
  select max(todate) from pauzy p where zadanie_id = :zadanie_id and fromdate = (select min(fromdate) from pauzy where zadanie_id = p.zadanie_id) into :tmp_end;
  for select fromdate, todate
    from pauzy
    where zadanie_id = :zadanie_id
    order by fromdate
    into :akt_begin, :akt_end
  do
  begin
    if (akt_begin <= tmp_end) then
    begin
      if (akt_end > tmp_end) then
        tmp_end = akt_end;
    end
    else begin
      przedzial = przedzial + datediff(day, tmp_begin, tmp_end);
      tmp_begin = akt_begin;
      tmp_end = akt_end;
    end
  end
  przedzial = przedzial + datediff(day, tmp_begin, tmp_end);
  suspend;
end

myślę, że z resztą sobie poradzisz

i jeszcze wywołanie

select * from DAJ_PRZEDZIAL(1);

i zadanie domowe: przeanalizować i zrozumieć :p

0

MisiekD, jesteś WIELKI. Dziękuję za te niezwykle cenne wskazówki. Zmodyfikowałem Twój kod do potrzeb i .... Naprawdę dziękuję. Jak wyskrobię kod do końca to wkleję.

Jak obieałem

CREATE TABLE SuspensionCase (
  IDSuspensionCase INTEGER NOT NULL,
  /* Podatnik */
  TaxPayerID       INTEGER NOT NULL,
  /* Typ podatku */
  TaxID            INTEGER NOT NULL,
  /* Księgowa */
  AccountantID     INTEGER NOT NULL,
  /* Status sprawy: */
  /*  - 0 :przedawnienie */
  /*  - 1 :zapłacone */
  /*  - 2 :zaległość */
  CaseStatus       SMALLINT,
  /* Tytuł wykonawczy */
  /*  - 0 :jest */
  /*  - 1 :nie ma */
  ExecuteStatus    SMALLINT,
  /* Kwota zaległości zł.gr */
  Amount           FLOAT,
  /* Okres przedawnienia w formacie MM/RRRR */
  Term             DATE,
  /* Okres zawieszenia */
  Period           SMALLINT,
  /* Data rozpoczęcia postępowania */
  BeginDate        DATE,
  /* Status wykonania postępowania */
  /*  - 0 :w trakcie */
  /*  - 1 :zakończona */
  Completed        SMALLINT,

  PRIMARY KEY (IDSuspensionCase),
  FOREIGN KEY (TaxPayerID) REFERENCES TaxPayer(IDTaxPayer),
  FOREIGN KEY (TaxID) REFERENCES Tax(IDTax),
  FOREIGN KEY (AccountantID) REFERENCES Accountant(IDAccountant)
);

CREATE TABLE Suspension (
  IDSuspension     INTEGER NOT NULL,
  SuspensionCaseID INTEGER,
  FromDate         DATE,
  ToDate           DATE,

  PRIMARY KEY (IDSuspension),
  FOREIGN KEY (SuspensionCaseID) REFERENCES SuspensionCase(IDSuspensionCase)
);

AS
DECLARE VARIABLE tmpDays INTEGER;
DECLARE VARIABLE tmpEndDate DATE;
DECLARE VARIABLE tmpFromDate DATE;
DECLARE VARIABLE tmpToDate DATE;

BEGIN
  FOR SELECT
    SuspensionCase.IDSuspensionCase,
    SuspensionCase.TaxPayerID,
    SuspensionCase.TaxID,
    SuspensionCase.AccountantID,
    SuspensionCase.CaseStatus,
    CAST(SuspensionCase.Amount AS NUMERIC(18,2)) AS Amount,
    SuspensionCase.ExecuteStatus,
    SuspensionCase.Term,
    SuspensionCase.Period,
    SuspensionCase.BeginDate,
    DATEADD(Period YEAR TO BeginDate),
    SuspensionCase.Completed,

    Tax.IDTax,
    Tax.TaxName,

    Accountant.IDAccountant,
    Accountant.AccountantName

    FROM SuspensionCase
    INNER JOIN Tax ON Tax.IDTax = SuspensionCase.TaxID
    INNER JOIN Accountant ON Accountant.IDAccountant = SuspensionCase.AccountantID
    
    INTO
      :IDSuspensionCase,
      :TaxPayerID,
      :TaxID,
      :AccountantID,
      :CaseStatus,
      :Amount,
      :ExecuteStatus,
      :Term,
      :Period,
      :BeginDate,
      :tmpEndDate,
      :Completed,

      :IDTax,
      :TaxName,

      :IDAccountant,
      :AccountantName
      
  DO BEGIN
    tmpDays = 0;
    FOR SELECT FromDate, ToDate FROM Suspension
    WHERE Suspension.SuspensionCaseID = :IDSuspensionCase
    INTO :tmpFromDate, :tmpToDate
    DO BEGIN
      IF ((EndDate < tmpFromDate) OR (BeginDate > tmpToDate)) THEN
      BEGIN
        tmpDays = tmpDays + 0;
      END ELSE
      IF (BeginDate BETWEEN tmpFromDate AND tmpToDate) THEN
      BEGIN
        tmpDays = tmpDays + DATEDIFF(DAY, BeginDate, tmpToDate);
      END ELSE
      IF (tmpEndDate BETWEEN tmpFromDate AND tmpToDate) THEN
      BEGIN
        tmpDays = tmpDays + DATEDIFF(DAY, tmpEndDate, tmpToDate);
      END ELSE
      BEGIN
        tmpDays = tmpDays + DATEDIFF(DAY, tmpFromDate, tmpToDate);
      END
    END
      EndDate = DATEADD(tmpDays+1 DAY TO tmpEndDate);
      SUSPEND;
  END
END

Ostatnie skompilowanie i działa. Misiek, naprawdę dziękuję.

1 użytkowników online, w tym zalogowanych: 0, gości: 1