Dienstag, Januar 21, 2020

postgresql Runde Hälfte nach unten Funktion

die Runde(numeric,integer) – Funktion in PostgreSQL nur Runden bis:

round(cast (41.0255 as numeric),3) ==> 41.026

Da brauchen wir ein round-Funktion gibt 41.025 und (ganz überraschend) gibt es nicht so eine Funktion in PostgreSQL (wir verwenden 9.1.5), wir schrieben eine „wrapper“ – Funktion, die in der ersten version ist ziemlich naiv und rauh…aber das haben wir nicht etwas besseres finden, durch die fehlende native Unterstützung für diese Art von Problemen in plpgsql.

Wird der code gezeigt unten. Das problem ist, dass es zu langsam für unsere Zwecke.
Könnten Sie vorschlagen, eine bessere Möglichkeit zur Bewältigung dieser Aufgabe?

Hier der code:

    CREATE OR REPLACE FUNCTION round_half_down(numeric,integer) RETURNS numeric 
    AS $$
    DECLARE
      arg ALIAS FOR $1;
      rnd ALIAS FOR $2;
      tmp1 numeric;
      res numeric;
    BEGIN
      tmp1:=arg;
      IF cast(tmp1 as varchar) ~ '5$'  THEN res:=trunc(arg,rnd);
      ELSE res:=round(arg,rnd);
      END IF;

      RETURN res;
    END;
    $$ LANGUAGE plpgsql;

Muss ich cast den numerischen Wert und die Verwendung von regexp…das ist es, was (nehme ich an) tötet Leistungen.

Nur damit Sie wissen: wir brauchen das, weil wir haben einen Vergleich der zahlen, die gespeichert wurden, in zwei unterschiedlichen Spalten (auf zwei verschiedenen Tischen), aber mit verschiedenen numerischen Datentyp: eines mit Doppelbett und eines real ist. Das problem ist, dass beim einfügen in einen real-Datentyp-Spalte, PostgreSQL führt die RUNDE HÄLFTE nach UNTEN, während es keine solche option, über seine mathematische Funktionen!

EDIT:

Die Funktion ist tatsächlich verbuggt. War eine erste kurze umschreiben als ein Versuch zur Verbesserung der Leistungen in der Funktion arbeiten, aber sehr langsam.

Verhalten übereinstimmen muss, die folgenden Schritte aus:

IF der Nachkommastelle gesetzt aus der Rundung ist <=5 => trunc

ELSE Runden.

Einige Beispiele:

select round_half_down(cast (41.002555 as numeric),3) -- 41.002 
select round_half_down(cast (41.002555 as numeric),4) -- 41.0025 
select round_half_down(cast (41.002555 as numeric),5) -- 41.00255 

während der round-Funktion in PostgreSQL gibt:

select round(cast (41.002555 as numeric),3) -- 41.003
  • Möchten Sie -41.0255 gerundet auf -41.025 oder -41.026? Außerdem: Würden Sie wollen, dass 41.02551 gerundet auf 41.025 oder 41.026?
  • Hi @erwin-brandstetter, Die Funktion ist tatsächlich verbuggt. War ein erste-rewriting zur Verbesserung der Leistungen in der Funktion arbeiten, aber sehr langsam. Verhalten übereinstimmen muss, die folgende: WENN die Dezimalstelle wird put off von der Rundung ist <=5 => trunc ANDEREN in der Runde. Einige Beispiele: wählen Sie round_half_down(cast (41.002555 wie numerisch),3) — 41.002 wählen Sie round_half_down(cast (41.002555 wie numerisch),4) — 41.0025 wählen Sie round_half_down(cast (41.002555 wie numerisch),5) — 41.00255 während der round-Funktion in postgresql: select round(cast (41.002555 wie numerisch),3) — 41.003
  • Ich aktualisiert meine Antwort entsprechend Ihrer updates. BTW, diese Art von Informationen sollten in deiner Frage, nicht in einem Kommentar, wo es zu Lesen ist hart.
InformationsquelleAutor BangTheBank | 2012-09-21

6 Kommentare

  1. 2

    Haben wir einen Vergleich der zahlen, die gespeichert wurden, in zwei unterschiedlichen Spalten
    (auf zwei verschiedenen Tischen), aber mit verschiedenen numerischen Datentyp: man ist
    Doppel-und ein real ist.

    Dieser sollte extrem schnell und einfach:

    SELECT dp_col, real_col
    FROM   tbl
    WHERE  dp_col::real = real_col

    Grundsätzlich, nur Stimmen die double precision Nummer real für den Vergleich.


    Wenn das nicht für Sie arbeiten, diese SQL-Funktion, der sollte einen richtige job und Arbeit schneller:

    CREATE OR REPLACE FUNCTION round_half_down1(numeric, int)
      RETURNS numeric LANGUAGE sql AS
    $func$
    SELECT CASE WHEN abs($1%0.1^$2) < .6 * 0.1^$2 THEN
             trunc($1, $2)
        ELSE round($1, $2) END;
    $func$

    Nun fest für negative zahlen, mit input von @sufleR in die Kommentare.

    Sie können auch verwenden Sie einfach die enthaltenen CASE Ausdruck.

    % .. modulo-operator

    ^ .. Potenzierung


    Hier ist ein kurzer test, den Sie verwenden können, für benchmarking:

    SELECT n                                   -- Total runtime: 36.524 ms
          ,round_half_down1(n,3)               -- Total runtime: 70.493 ms
          ,round_down_to_decimal_places(n,3)   -- Total runtime: 74.690 ms
          ,round_half_down(n,3)                -- Total runtime: 82.191 ms
    FROM  (SELECT random()::numeric AS n FROM generate_series(1,10000)) x
    WHERE  round_down_to_decimal_places(n,3)
        <> round_half_down1(n,3)

    Es zeigt auch, wie @Parveen Funktion und Ihre bearbeitete version verrechnen – Sie trunc() wo Sie nicht sollten.

    • So hat das casting auf real für Sie arbeiten oder die Funktion?
    • vielen Dank für deine Antwort. Das casting, seltsam, funktioniert nicht perfekt. Ich mache mehr tests, um herauszufinden, warum, denn es klingt das Recht (und einfache) Antwort für mein problem. Seltsam, es passt nicht alle Zeilen, es sollte, und es dauert eine Ewigkeit, bis die nicht-matching-diejenigen zu erkennen, die problem. Im Grunde genommen,alle Werte in der REAL-Spalte muss mit dem „gegossen zu REAL“ – Wert in der DOUBLE-PRECISION-Spalte (‚Ursache es ist nur, was passiert, wenn Werte eingefügt wurden, in REAL-Spalte), aber es ist nicht der Fall! 73761 Zeilen aus 4267717 sind nicht aufeinander abgestimmt.
    • funktioniert nicht für negative Werte. CASE WHEN $1%0.1^$2 sollte CASE WHEN ABS($1%0.1^$2)
    • Guten Fang. Danke, ist jetzt behoben.
  2. 3

    Methode ist sehr schnell, ohne eine neue FUNKTION, die Runde Hälfte nach unten wie folgt:

    — Runde die Hälfte bis

    round($n, 3)

    — Runde Hälfte nach unten

    round($n-0.5, 3)
    • Dies würde nur Arbeit für keine Ziffer nach dem Punkt-Zeichen. Für 1 Ziffern verwenden 0,05 und 2 Ziffern, 0,005 und so weiter.
  3. 2

    Ist Es Ganz Einfach:

    Versuchen, diese Funktion zu verwenden

    CREATE OR REPLACE FUNCTION ROUND_HALF_DOWN(NUMERIC)
      RETURNS NUMERIC LANGUAGE SQL AS
    $FUNC$
      SELECT CASE WHEN ($1%1) < 0.6 THEN FLOOR($1) ELSE CEIL($1) END;
    $FUNC$
  4. 1

    Hier ist eine viel einfachere Ansatz

    CREATE OR REPLACE FUNCTION roundHalfDown(value NUMERIC, prec INTEGER)
    RETURNS NUMERIC AS $$
    BEGIN
      RETURN trunc(value * 10^prec + 0.5 - 0.000000001) / 10^prec;
    END
    $$ LANGUAGE 'plpgsql';
  5. 0
    create or replace function round_down_to_decimal_places(numeric,integer)
    returns numeric stable language sql as $$ 
    
    select
    case
    when $1 >= 0 then
    case when $1 - round($1, 3) < 0 then round($1, 3) - 0.001 else 
    round($1, 3) end
    else
    case when $1 - round($1, 3) > 0 then round($1, 3) + 0.001 else 
    round($1, 3) end
    end
    
    $$;

    Können generische, durch ändern (0.001 für 3 dezimal, 0.0001 für 4 dezimal usw.)

    HERAUSGEGEBEN von der OP:
    @Parveel: habe ich geändert, die Funktion in der Reihenfolge zu make es die Arbeit in einer Allgemeinen Weise.

    create or replace function round_half_down(numeric,integer)
    returns numeric stable language sql as $$ 
    
    select
    case
    when $1 >= 0 then
        case 
            when ($1 - round($1, $2)) < 0 then cast((round($1, $2) - (1.0/(10^$2))) as numeric) 
        else round($1, $2) end
    else
        case 
            when ($1 - round($1, $2)) > 0 then cast((round($1, $2) + (1.0/(10^$2))) as numeric)
        else round($1, $2) end
    end
    
    $$;
    • Sowohl Ihre Funktionen verrechnen. Ich habe eine demo zu meiner Antwort.
    • eigentlich die zweite war eine generalisierte version habe ich vorgeschlagen, der Parveen-Funktion 🙂
    • Besser wäre es, fügen Sie Ihre eigenen Antwort, wenn Sie etwas wesentlich anderes (wie hier). ALSO nicht davon abhält, die Beantwortung Ihrer eigenen Frage.
    • vielen Dank für den Hinweis. Ich sehe deinen Punkt, aber in diesem Fall habe ich die original Parveen, eine Idee und versuchte, Sie zu verallgemeinern.
    • Noch wesentlicher für ein edit, sollte in einer separaten Antwort. Und wenn Sie nicht Bearbeiten, sollten Sie deutlich machen, dass die zweite Hälfte von Ihnen (BangTheBank), nicht der ursprüngliche Autor (Parveen). Ein text wie „ich hab mich geändert…“, wird erst klar, die Leser nach dem Studium der edit-history. Nicht, wie es sein sollte.
    • ok, habe es. Dies ist das erste mal, dass ich mich aktiv mit einem stack-Website und wollen sich nicht belasten mit nicht wirklich Antworten, unabhängig von der Lesbarkeit.
    • Ich sehe deine guten Absichten. Sonst würde ich nicht verschwenden meine Zeit versuchen, zu erziehen. Willkommen ALSO! 🙂 Jetzt ist es fix die eine oder andere Weise.
    • Vielen Dank für Ihre Mühe und die Zeit, die Sie verbringen, es zu tun:) ich kann nicht Antwort auf meine Fragen, da ich don ‚ T haben eine ausreichende Menge der Rufpunkte (ich muss einen Tag warten, für, die). Musste es hier tun.

  6. 0
    CREATE OR REPLACE FUNCTION public.round_half_down (numeric,integer)
    RETURNS numeric AS
    $body$
    DECLARE
      arg ALIAS FOR $1;
      rnd ALIAS FOR $2;
      tmp1 numeric;
      res numeric;
      vra varchar;
      inta integer;
      ifa boolean;
     BEGIN
        tmp1:= arg;
        vra := substr(cast((arg - floor(arg)) as varchar),3);
        ifa := null;
        FOR i IN 1 .. length(vra) LOOP
            inta := CAST((substr(vra,i,1)) as integer);
            IF (i > rnd) THEN
                IF ((ifa is null) AND inta >= 6)THEN
                    ifa := true;
                ELSE
                    ifa := false;
                END IF;
            END IF;
        END LOOP;
    
        IF ifa THEN 
            res:=trunc(arg,rnd);
        ELSE 
            res:=round(arg,rnd);
        END IF;
        RETURN res;
    END;
    END;
    $body$
    LANGUAGE 'plpgsql'
    VOLATILE
    CALLED ON NULL INPUT
    SECURITY INVOKER
    COST 100;

    Versuchen, diese Hoffnung kann Ihnen helfen, u

Kostenlose Online-Tests