Datenbanken

Datenbanken

Posts 1-3 of 3
  • Christoph Ingenhaag
    Christoph Ingenhaag    Premium Member
    The company name is only visible to registered members.
    Datenbank Allerlei
    Hallo zusammen,

    mit diesem Artikel verfolge ich die Absicht, Andere hier zu ermuntern eine entsprechende Lösung für andere Datenbanksystem zu schreiben. Es geht mir hier im Vordergrund nicht um das Detail, welches der Grund für diese Implementierung ist, sondern darum zu sehen, wie das mit Oracle, Firebird, MySQL, PostgreSQL, ... umgesetzt würde. Das nachfolgende Skript läuft unter MS SQL Server 2008. Also als Gegenüberstellung der verschiedenen Syntax, Objekte und Möglichkeiten usw. "delete ..." wäre natürlich eine Alternative, aber in diesem Zusammenhang dann eher langweilig.
    Wenn also dieser Lösungsweg für das eine oder andere der Datenbanksysteme absurd ist, interessiert mich
    trotzdem, wie man die nachfolgenden Funktionen und Prozeduren schreiben würde.
    Ach ja, die Datentypen sind eher aus Faulheit gewählt.
    Es wäre schön, wenn ohne Kindereien wie "Mein Datenbanksystem ist besser als deins" ausgekommen werden
    könnte.

    Vielleicht finden sich ja mal Interessierte und nicht nur die "Karteileichen", die sich in dieser
    Gruppe nur so anmelden...

    So, worum geht es:
    Mittels eine Stored Procedure werden alte Logeinträge aus einer Logtabelle gelöscht.
    Es soll immer eine bestimmte Anzahl der ältesten Datensätze aus dem Log entfernt werden.

    Der Hintergrund für die Art der Implementierung ist dieser Blogeintrag
    http://blogs.msdn.com/sqlcat/archive/2009/05/21/fast-ordered...
    Hierdurch wird verhindert, dass einmal ein Scan ausgeführt wird um die zu löschenden Daten
    zu identifizieren (Top) und noch einmal ein Scan bzw. Seek (je nach Anzahl der Datensätze)
    zum Löschen der Daten ausgeführt wird.
    Es erfolgt also nur e i n Scan. (siehe dazu auch Ausführungspläne am Ende)

    /*Skriptanfang*/
    use Test -- Datenbank Test verwenden
    go

    --von einem Tetslauf noch vorhandene Objekte löschen
    if object_id('dbo.fnClearLog') is not NULL drop function dbo.fnClearLog
    if object_id('dbo.prClearLog') is not NULL drop proc dbo.prClearLog
    if object_id('dbo.Log') is not NULL drop table dbo.Log
    go

    --Tabelle anlegen
    --LogEntry mit max. 2 GB Länge, hier um sich nicht um die Länge kümmern zu müssen
    --Timestamp als Zeitstempel mit 100 Nanosekunden Genauigkeit als PK
    --(Damit für dieses Beispiel das Einfügen von Datensätzen schneller geht)
    create table dbo.Log
    (
    LogEntry varchar(max)
    ,Timestamp datetime2(7) default sysdatetime() not NULL --JJJJ-MM-DD HH:MM:SS.1234567
    ,constraint pk_log primary key clustered(Timestamp) -- clustered = "Tabelle als Index"
    )
    go

    --Eine "Inline Table-Valued" Funktion anlegen
    --vgl. mit "parametriesierbare View"
    --Gibt soviele Datensätze zurück, wie mit @top übergeben
    create function dbo.fnClearLog
    (
    @top int
    )
    returns table
    with schemabinding
    as
    return
    select top (@top) Timestamp
    from dbo.Log
    order by
    timeStamp asc
    go

    --Eine Stored Procedure anlegen
    --Löscht über die Funktion die mit @RowsToDelete übergebene
    --Anzahl von Datensätzen (wenn vorhanden) und
    --gibt die Anzahl der gelöschten Datensätze in einer
    --Variablen zurück
    create proc prClearLog
    @RowsToDelete int
    ,@RowsDeleted int output
    as
    set xact_abort on
    set nocount on
    delete dbo.fnClearLog(@RowsToDelete)
    set @RowsDeleted = @@rowcount
    return @@error
    go

    --Einfügen von Testdaten
    declare @i as int = 1
    while @i < 52
    begin

    insert into dbo.Log(LogEntry)
    select 'was ist denn hier passiert?'

    waitfor delay '00:00:00.010'

    set @i += 1

    end
    go


    --Aufruf der Prozedur
    declare
    @rows int,
    @err int

    exec @err = prClearLog 50, @rows output

    select
    @err as Fehler,
    @rows as [Gelöschte Zeilen]

    --Zum Vergleich
    delete
    from dbo.log
    where
    timestamp in
    (
    select top(50) timestamp
    from dbo.Log
    order by
    timeStamp asc
    )

    --Der Plan beim Löschen über die Funktion
    |--Clustered Index Delete(OBJECT:([Test].[dbo].[Log].[pk_log]))
    |--Top(ROWCOUNT est 0)
    |--Top(TOP EXPRESSION:(CONVERT_IMPLICIT(bigint,[@RowsToDelete],0)))
    |--Clustered Index Scan(OBJECT:([Test].[dbo].[Log].[pk_log]), ORDERED FORWARD)



    --Der Plan beim Löschen über die Tabelle
    |--Clustered Index Delete(OBJECT:([Test].[dbo].[Log].[pk_log]))
    |--Table Spool
    |--Top(ROWCOUNT est 0)
    |--Nested Loops(Inner Join, OUTER REFERENCES:([Test].[dbo].[Log].[Timestamp]))
    |--Top(TOP EXPRESSION:((50)))
    | |--Clustered Index Scan(OBJECT:([Test].[dbo].[Log].[pk_log]), ORDERED FORWARD)
    |--Clustered Index Seek(OBJECT:([Test].[dbo].[Log].[pk_log])

    Viele Grüße
    Christoph Ingenhaag
  • Lukas Gradl
    Lukas Gradl
    The company name is only visible to registered members.
    Re: Datenbank Allerlei
    Hmmm - also wenn ich das richtig verstanden habe, dann sollen aus einer Log-Tabelle soviele der ältesten Zeilen gelöscht werden, daß nur noch eine gewisse Anzahl Einträge da ist, damit z.B. Max. 1000 Zeilen im Log stehen.

    Wenn ich das richtig verstanden habe, dann finde ich den Ansatz reichlich kompliziert.

    Mit Firebird würde ich das ganz einfach lösen:

    Die Tabelle schaut so aus:
    CREATE TABLE "LogTest"
    (
    TS Timestamp,
    "Text" Varchar(50)
    );

    Füllen kann ich sie so:

    SET TERM ^ ;
    CREATE PROCEDURE "FillLogTest"
    AS
    DECLARE VARIABLE Counter INTEGER;
    BEGIN
    Counter=0;
    WHILE (Counter<10000) DO BEGIN
    INSERT INTO "LogTest" ("TS","Text") VALUES ('now','Text '||CAST(:Counter AS VARCHAR(10)));
    Counter=Counter+1;
    END
    END^
    SET TERM ; ^

    Wenn ich nun 1000 Einträge haben will und alle älteren löschen, dann wär das folgendes Statement:

    DELETE FROM "LogTest" WHERE "TS">(SELECT FIRST 1 SKIP 1000 "TS" FROM "LogTest" ORDER BY "TS")

    Einziges Manko: Wenn mehrere Einträge mit dem selben Timestamp existieren, dann werden erst die gelöscht, die noch älter sind, es sind daher nicht ganz sicher nur 1000 Zeilen, sonder eventuell ein paar mehr.
    Je nach Anwendungszweck aber leicht zu verschmerzen...

    Gruß
    Luggi
    (PS: Ich HASSE es daß hier keine Einrückungen funktionieren - oder bin ich nur zu dumm dazu?)
    This post was modified on 07 Feb 2010 at 05:46 pm.
  • Christoph Ingenhaag
    Christoph Ingenhaag    Premium Member
    The company name is only visible to registered members.
    Re^2: Datenbank Allerlei
    Hallo Luggi!

    Lukas Gradl schrieb:

    Hmmm - also wenn ich das richtig verstanden habe, dann sollen aus einer Log-Tabelle soviele der ältesten Zeilen gelöscht werden, daß nur noch eine gewisse Anzahl Einträge da ist, damit z.B. Max. 1000 Zeilen im Log stehen.
    Fast. Einfach eine bestimmte Anzahl der ältesten Datensätze löschen.

     
    Wenn ich das richtig verstanden habe, dann finde ich den Ansatz reichlich kompliziert.

    Das ist er auch. Der einfach Ansatz stand unten

    delete
    from dbo.log
    where
    timestamp in
    (
    select top(50) timestamp
    from dbo.Log
    order by
    timeStamp asc
    )

    und ist

    DELETE FROM "LogTest" WHERE "TS">(SELECT FIRST 1 SKIP 1000 "TS" FROM "LogTest" ORDER BY "TS")
    ähnlich.

    Die Kompliziertheit der Lösung mit der Funktion und der Procedure ergibt sich daraus, eine physikalische Operation einzusparen. Das ein solches Performancetuning generell nicht notwendig ist ist klar, insofern hat das Ganze experimentellen Charakter.


    Einziges Manko: Wenn mehrere Einträge mit dem selben Timestamp existieren, dann werden erst die gelöscht, die noch älter sind, es sind daher nicht ganz sicher nur 1000 Zeilen, sonder eventuell ein paar mehr.
    Je nach Anwendungszweck aber leicht zu verschmerzen...

    Hier hatte ich einfach einen PK auf die Timestamp Spalte gelegt (daher auch waitfor beim Einfügen der Testdaten).

    (PS: Ich HASSE es daß hier keine Einrückungen funktionieren - oder bin ich nur zu dumm dazu?)
    Das hasse ich auch. XING entfernt alle führenden Leerzeichen.

    Viele Grüße
    Christoph