Prüfung über alle Tabellen: Int-Felder nah am Maximalwert?

28. Oktober 2020 11:06

Vor ein paar Tagen hatten wir das unschöne Problem, dass wir spontan ein "Entry No." - Primärschlüsselfeld in einer Tabelle in unserer Branchenlösung von Integer auf Biginteger erweitern mussten (nicht weil wir wirklich über 2.147.483.647 Datensätze in der Tabelle hatten, sondern irgendjemand irgendwann Mist gebaut hat und die neuesten lfd. Nummern leider schon so hoch waren - es gibt riesige Lücken zwischen einzelnen lfd. Nummern).

Jetzt würden wir gerne einmal prüfen, ob uns das in einer anderen Tabelle möglicherweise ebenfalls droht. Kennt da evtl. jemand ein (SQL-)Skript, das für jedes Tabellenfeld mit dem Datentyp Integer prüft, ob da schon ein Wert > 2 Milliarden drin steht? Im Zweifel würde es auch erstmal reichen, das für Integer-Felder zu prüfen, die im Primärschlüssel vorkommen.

Vielen Dank!

Gefunden habe ich bisher das hier: https://stackoverflow.com/a/38614661/13260204

Allerdings fehlen da Tabellen, die auf jeden Fall ein Integer-Feld als Primärschlüssel haben, z.B. die Cust. Ledger Entry werden da nicht aufgeführt im Ergebnis.

Re: Prüfung über alle Tabellen: Int-Felder nah am Maximalwer

28. Oktober 2020 12:00

Wie man das als SQL-Script anstellt kann ich dir nicht sagen, jedoch wie man es in C/AL abbilden würde:

Ausgangsbasis ist die Tabelle "Field", welche du auf "Type=Integer" filterst.
Nun hast du alle Integer-Felder inklusive der dazugehörigen Tabellen-ID und kannst sie der Reihe nach abarbeiten.
Mit RecordRef und FieldRef kannst du jetzt dynamisch durch die Tabellen laufen.

Hinweis:
Wenn ihr mehrere Mandanten in der Datenbank habt, dann musst du die mandantenabhängigen Tabellen jeweils pro Mandant durchlaufen.
Ob eine Tabelle mandantenabhängig oder mandantenübergreifen definiert ist, kannst du der Tabelle "Table MetaData" entnehmen.

Re: Prüfung über alle Tabellen: Int-Felder nah am Maximalwer

28. Oktober 2020 12:06

Hallo,

als Basis kannst du mal das Fieldcheck- Tool verwenden, das ist zwar für was ganz anders gedacht, der grundlegende Ablauf dürfte allerdings ähnlich sein.
fieldcheck (Mibuso)

Das Tool ist allerdings für den CC, dürfte sich aber einfach auf RTC bringen lassen.

Gruß Fiddi

Re: Prüfung über alle Tabellen: Int-Felder nah am Maximalwer

28. Oktober 2020 12:39

InfoWissler hat geschrieben:Allerdings fehlen da Tabellen, die auf jeden Fall ein Integer-Feld als Primärschlüssel haben, z.B. die Cust. Ledger Entry werden da nicht aufgeführt im Ergebnis.

--> ich vermute mal ganz stark, weil es kein AutoIncrement ist! -> da müsstest du dann mit
Code:
SELECT MAX(column_name)
FROM TableXYZ


ran

Re: Prüfung über alle Tabellen: Int-Felder nah am Maximalwer

28. Oktober 2020 16:05

Danke euch!

Habe einmal folgendes Skript gebastelt, eher der Art schnell & schmutzig.

Wichtige Hinweise dazu:
* Ich mache nur ein RecRef.FINDLAST; es sind also eigentlich nur die Feldwerte aussagekräftig, bei denen der Primärschlüssel so aufgebaut ist, dass die höchsten Werte durch das Findlast gefunden werden und dann halt auch nur für Primärschlüsselfelder á la "Entry No." interessant. Gibt es da noch einen Weg, um quasi ein "Max-Lookup" zu machen, um alle Fälle erkennen zu können?
* Tabellennamen, die mit CRM beginnen, habe ich aufgrund von folgendem Fehler rausgenommen: "Die Tabellenverbindung für den Tabellentyp CRM muss mithilfe von RegisterTableConnection oder des Cmdlet New-NAVTableConnection registriert werden, bevor sie verwendet werden kann."


Code:
NewFile.CREATE('\\Netzlaufwerk\IntegerValues.csv');
lim := ';';
NewFile.TEXTMODE(TRUE);

Field.SETRANGE(Type, Field.Type::Integer);
Field.SETRANGE(Class, Field.Class::Normal);
Field.SETRANGE(Enabled, TRUE);
Field.SETFILTER(TableName, '<>CRM*');
Functions.Progressbar('INIT', 'Feldnr.', Field.COUNT, 0, '');
Field.FIND('-');
REPEAT
  Functions.Progressbar('UPDATE', '', 0, 100, COPYSTR(Field.TableName + ' ' + Field.FieldName,1,20));
  IF OldTableNo <> Field.TableNo THEN BEGIN
    RecRef.CLOSE;
    RecRef.OPEN(Field.TableNo);
  END;
  IF RecRef.READPERMISSION THEN BEGIN
    IF NOT RecRef.ISEMPTY THEN BEGIN
      RecRef.FINDLAST;
      FRef := RecRef.FIELD(Field."No.");
      NewFile.WRITE(Field.TableName + lim + Field.FieldName + lim + FORMAT(FRef.VALUE));
    END;
  END;
  OldTableNo := Field.TableNo;
 
UNTIL Field.NEXT = 0;
Functions.Progressbar('CLOSE', '', 0, 0, '');
NewFile.CLOSE;


Variablen:
Name DataType Subtype Length
Field Record Field
RecRef RecordRef
FRef FieldRef
NewFile File
lim Text
OldTableNo Integer

Functions ist eine Codeunit bei uns mit der Funktion Progressbar, die mit ein paar Parametern sehr universal funktioniert, sodass man nicht jeden Fortschrittsbalken neu programmieren muss.

Zu dem SQL-Max-Befehl: da bin ich leider nicht versiert genug, um ein SQL-Skript zu bauen, dass das für alle Integer-Felder aller Tabellen macht.



Hier ein SQL-Skript:
Code:
USE <Database>
GO

DECLARE @DatabaseName VARCHAR(100)
DECLARE @SchemaName VARCHAR(100)
DECLARE @TableName VARCHAR(100)
DECLARE @ColumnName VARCHAR(100)
DECLARE @FullyQualifiedTableName VARCHAR(500)
DECLARE @DataType VARCHAR(50)

--Create Temp Table to Save Results
IF OBJECT_ID('tempdb..#Results') IS NOT NULL
    DROP TABLE #Results

CREATE TABLE #Results (
    DatabaseName VARCHAR(100)
    ,SchemaName VARCHAR(100)
    ,TableName VARCHAR(100)
    ,ColumnName VARCHAR(100)
    ,ColumnDataType VARCHAR(50)
    ,MaxValue VARCHAR(50)
    ,MinValue VARCHAR(50)
    )

DECLARE Cur CURSOR
FOR
SELECT DB_Name() AS DatabaseName
    ,s.[name] AS SchemaName
    ,t.[name] AS TableName
    ,c.[name] AS ColumnName
    ,'[' + DB_Name() + ']' + '.[' + s.name + '].' + '[' + t.name + ']' AS FullQualifiedTableName
    ,d.[name] AS DataType
FROM sys.schemas s
INNER JOIN sys.tables t ON s.schema_id = t.schema_id
INNER JOIN sys.columns c ON t.object_id = c.object_id
INNER JOIN sys.types d ON c.user_type_id = d.user_type_id
WHERE d.name LIKE '%int%'
    OR d.name LIKE '%float%'
    OR d.name LIKE '%decimal%'
    OR d.name LIKE '%numeric%'
    OR d.name LIKE '%real%'
    OR d.name LIKE '%money%'
    AND is_identity = 0

OPEN Cur

FETCH NEXT
FROM Cur
INTO @DatabaseName
    ,@SchemaName
    ,@TableName
    ,@ColumnName
    ,@FullyQualifiedTableName
    ,@DataType

WHILE @@FETCH_STATUS = 0
BEGIN
    DECLARE @SQL VARCHAR(MAX) = NULL

    SET @SQL = ' Select ''' + @DatabaseName + ''' AS DatabaseName, ''' + @SchemaName + ''' AS TableName,
      ''' + @TableName + ''' AS SchemaName,
      ''' + @ColumnName + ''' AS ColumnName,
      ''' + @DataType + ''' AS ColumnName,
      (Select MAX([' + @ColumnName + ']) from ' + @FullyQualifiedTableName + ' with (nolock))
      AS MaxValue,
      (Select MIN([' + @ColumnName + ']) from ' + @FullyQualifiedTableName + ' with (nolock))
      AS MaxValue'

    PRINT @SQL

    INSERT INTO #Results
    EXEC (@SQL)

    FETCH NEXT
    FROM Cur
    INTO @DatabaseName
        ,@SchemaName
        ,@TableName
        ,@ColumnName
        ,@FullyQualifiedTableName
        ,@DataType
END

CLOSE Cur

DEALLOCATE Cur

SELECT *
FROM #Results
    --drop table #Results


(Angepasst für Groß-/Kleinschreibung von hier: http://www.techbrothersit.com/2016/03/h ... r-all.html)