PDA

Visualizza la versione completa : [delphi] round in stored procedure


123delphi321
20-05-2004, 00:51
Salve a tutti, ciao Alka,

dovrei effettuare dei calcoli in una stored procedure,..ed avrei bisogno di arrotondare a 3 cifre decimali dei valori numerici nella stored procedure.

credo di aver bisogno di una funzione UDF che effettua il ROUND.

mi sapete indicare quale libreria posso utilizzare e da dove la posso scaricare, o e meglio che me la costruisca io?

grazie

alka
20-05-2004, 09:28
Non arrotondare all'interno della stored procedure con il rischio di perdere precisione, bensì usa l'arrotondamento all'interno di Delphi come semplice rappresentazione del valore.

Inoltre, mi sono dimenticato di farti un appunto riguardo ad un altra discussione in cui hai postato parte del codice della stored procedure: evita l'uso del tipo double precision quando lavori con degli importi, poichè si tratta di un valore a virgola mobile che ha difetti di precisione e può portarti a risultati errati nell'esecuzione di calcoli fiscali. Al suo posto, usa il tipo numeric, il quale invece memorizza il tuo valore, pur essendo decimale, usando registri interi e quindi non influenzati da alcuna perdita di precisione.

Ciao! :ciauz:

123delphi321
21-05-2004, 09:59
il problema e' proprio questo...la perdita di precisione.

io devo calcolare il totale di valori quali imponibile e imposta.

nel programma in delphi questi 2 fields sono fields calcolati, ed io per ogni record eseguo il calcolo:

procedure TFrmMovimenti.IBDataSet1CalcFields(DataSet: TDataSet);
begin
with IBDataSet1 do
begin
FieldByName('IMPONIBILE').AsFloat := (FieldByName('QUANTITA').AsFloat * FieldByName('PREZZO').AsFloat) - ((FieldByName('QUANTITA').AsFloat * FieldByName('PREZZO').AsFloat) * FieldByName('SCONTO').AsFloat /100);
FieldByName('IMPOSTA').AsFloat := FieldByName('IMPONIBILE').AsFloat * FieldByName('IVA').AsFloat /100 ;
FieldByName('TOTALE').AsFloat := FieldByName('IMPONIBILE').AsFloat + FieldByName('IMPOSTA').AsFloat;
end;
end;

ad esempio, il fields imposta e' definito numeric 9,3 e quindi viene automaticamente arrotondato alla terza cifra decimale, e questo arrotondamento avviene singolarmente per ogni record di dettaglio_movimento.

nella stored procedure calcolo i totali in questo modo:

CREATE PROCEDURE TOTALI_DET (
IDMOVIMENTI INTEGER)
RETURNS (
IMPONIBILE NUMERIC(15,3),
IMPOSTA NUMERIC(15,3),
TOTALE NUMERIC(15,3))
AS
begin
/* Procedure Text */
SELECT
SUM((quantita*prezzo)-(quantita*prezzo*sconto/100)),
SUM((quantita*prezzo)-(quantita*prezzo*sconto/100)*iva/100),
SUM(((quantita*prezzo)-(quantita*prezzo*sconto/100))+((quantita*prezzo)-(quantita*prezzo*sconto/100))*iva/100)
FROM movimenti_dettaglio
where id_movimenti = :idmovimenti
INTO :imponibile, :imposta, :totale;
suspend;
end
questo fa si, che l'arrotondamento non avviene singolarmente per ogni record, bensi solo alla fine della SUM, e quindi ottengo dei totali che possono essere diversi dalla somma delle cifre che effettivamente visualizzo.

cosa mi consigli di fare?

ps.: quando mi hai detto di utilizzare fields numeric invece di double intendevi come dovevo dichiarare le variabili di RETURNS della stored NUMERIC?

grazie

alka
21-05-2004, 10:06
Quando ti dicevo di sostituire NUMERIC a DOUBLE PRECISION, mi riferivo a tutti gli ambiti in cui devi memorizzare o eseguire calcoli con valute, quindi campi del database, variabili nelle stored procedure e così via.

Comunque, non comprendo bene come hai organizzato il lavoro: il totale lo calcoli in Delphi o nella stored procedure?

Inoltre, il listato della stored procedure mi sembra molto contorto: tutto viene eseguito con una istruzione SELECT; ricorda che è possibile utilizzare l'istruzione FOR SELECT per poter eseguire un blocco di istruzioni tante volte quanti sono i record restituiti dalla SELECT stessa: questo è utile per calcolare totali scandendo ogni record, eseguendo il calcolo per il singolo record e aggiungendo il valore ottenuto al totale sommario.

Il valore da visualizzare in Delphi deve poi essere formattato alle cifre decimali desiderate usando la proprietà DisplayFormat, se queste cifre sono diverse da quelle utilizzate nel campo stesso del database.

Non userei la proprietà AsFloat, bensì la proprietà AsCurrency (se possibile), visto che il tipo Double di Delphi ha lo stesso problema del tipo DOUBLE PRECISION di InterBase: sono valori a virgola mobile con perdita di precisione.

Se la proprietà AsFloat viene usata per ottenere il valore di un campo valuta da arrotondare e visualizzare, grossi problemi non sussistono comunque, ma se inizi a sommarli tra loro per fare calcoli, allora alla fine è facile che tu ottenga un risultato diverso da quello corretto per via della perdita di precisione che si propaga ad ogni operazione di somma ripetuta.

Spero di averti dato sufficienti indicazioni.

Ciao! :ciauz:

123delphi321
21-05-2004, 11:11
scusa per favore, stavo scrivendo una stored con cui vorrei far calcolare il totale dell'imponibile scandendo ogni singolo record...

quindi, io seleziono tutti i record di dettaglio e per oguno di essi sommerei l'imponibile.
dapprima ho impostato IMPONIBILE=0, poi eseguo la SELECT...poi si ferma sull'istruzione DO con questo messagio:

================================================== ==============================
********* Statement:
REATE PROCEDURE TOTALI_DETDO (
IDMOVIMENTI INTEGER)
RETURNS (
IMPONIBILE NUMERIC(15,3))
AS
begin
/* Procedure Text */
IMPONIBILE=0;
FOR SELECT * FROM movimenti_dettaglio where id_movimenti LIKE :idmovimenti
DO
BEGIN
IMPONIBILE = IMPONIBILE + ((quantita*prezzo)-(quantita*prezzo*sconto/100))
suspend;
END
end

********* Error:
Invalid token.
Dynamic SQL Error.
SQL error code = -104.
Token unknown - line 10, char -1.
DO.
da un esempio che ho trovato sul manuale di interbase/firebird la mia stored differisce solo x la clausa INTO a seguito della select.

ma la mia select non mi restituisce un parametro da inserire in RETURNS....

dove sbaglio?

alka
21-05-2004, 14:39
La clausola INTO è obbligatoria nell'istruzione FOR SELECT, poichè i valori dei campi devono essere memorizzati all'interno di variabili locali che verranno poi usate per eseguire i calcoli dovuti: il codice compreso tra DO BEGIN...END non può accedere ai campi usando i nomi corrispondenti.

123delphi321
21-05-2004, 15:25
ecco fatto:

CREATE PROCEDURE TOTALI_DETDO (
IDMOVIMENTI INTEGER)
RETURNS (
IMPONIBILE NUMERIC(15,3),
IMPOSTA NUMERIC(15,3),
TOTALE NUMERIC(15,3)
)
AS
DECLARE VARIABLE QUANTITA_ NUMERIC(15,3);
DECLARE VARIABLE PREZZO_ NUMERIC(15,3);
DECLARE VARIABLE SCONTO_ NUMERIC(15,3);
DECLARE VARIABLE IVA_ NUMERIC(15,3);
DECLARE VARIABLE IMPON_ NUMERIC(15,3);
DECLARE VARIABLE IMPOS_ NUMERIC(15,3);
begin
/* Procedure Text */
IMPONIBILE=0;
IMPOSTA=0;
TOTALE=0;
FOR SELECT quantita,prezzo,sconto, iva FROM movimenti_dettaglio
where id_movimenti LIKE :idmovimenti
into :quantita_, :prezzo_, :sconto_, :iva_
DO
BEGIN
IMPON_ = ((:quantita_* :prezzo_)-(:quantita_ * :prezzo_ *:sconto_ /100));
IMPONIBILE = IMPONIBILE + IMPON_;
IMPOS_ = IMPON_ * :IVA_ /100;
IMPOSTA = IMPOSTA + IMPOS_;
END
TOTALE = IMPONIBILE +IMPOSTA;
suspend;
end
in questa maniera ottengo i risultati giusti....

resta una cosa da fare, e cioe controllare se i valori :quantita_ :prezzo_ :sconto_ :iva_ sono null.

come mi consigli di fare? controllare nella stored o direttamente inserire l'opzione NOT NULL nelle proprieta del field della tabella del database?

grazie

ps....scusami ma nn so xke sono uscite tutti queste faccine che ridono... forse per una semplice consecutivita di caratteri. :D

alka
21-05-2004, 15:30
Se si tratta di dati che devono essere obbligatoriamente inseriti, la clausola NOT NULL va inserita nel campo, all'interno della struttura della tabella.

123delphi321
21-05-2004, 15:58
io l'ho definito cosi....

QUANTITA NUMERIC(9,3) DEFAULT '0' NOT NULL,

succede che se inserisco tramite IBExpert un record, nel momento che eseguo il Post se il valore della quantita e' null viene automaticamente settato a 0., invece in delphi mi viene sollevata un'eccezzione che indica che il field quantita non puo' essere Null costringendomi a dover scrivere un valore.

non si puo avere l'assegnazione automatica x default?

alka
21-05-2004, 16:08
Originariamente inviato da 123delphi321
non si puo avere l'assegnazione automatica x default?
Il valore di default viene attribuito da InterBase quando, per un determinato campo, si tenta di impostare un valore nullo.

Questo avviene anche in Delphi, ma siccome la struttura interna (oggetti TFields) della tabella prevede il valore True per la proprietà Required del campo, Delphi pretende di ottenere un valore da inviare per quel campo prima di eseguire la Post.

La soluzione è semplice: intercetta l'evento OnNewRecord sul DataSet e, in tale evento, attribuisci valori iniziali ai campi.

Ciao! :ciauz:

Loading