sqlinsight-pro/SQL Scripts/SQLI-AX-005.sql
Søren Eggert Lundsteen Olsen 3e1cc74f0e Dynamics AX rules SQLI-AX-001..010 and their SQL Scripts
Batch jobs: failed (001), longer than their own history (002), late or never
started (003), stuck in Executing (004). Number sequences: running out with a
forecast from SQLInsight's own samples (005), format shorter than HIGHEST
(006), blocked but still referenced (007), continuous and not cleaned up
(008). RecId running out, flagged far earlier for AX 3.0's 32-bit RecId
(009). AX user behind blocking from CONTEXT_INFO, honest where AX 3.0 cannot
tell (010). Subjects carry the database, so findings follow the hub opt-in.

SQL Scripts/<rule>.sql (and _collectors/) for every AX rule added or changed
in 1.4, incl. COM-AX-001..007: a Danish header plus the exact SQL SQLInsight
sends, generated from the code and checked by SqlScriptTests (rewrite with
SQLINSIGHT_UPDATE_SQL_SCRIPTS=1).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 13:54:23 +02:00

96 lines
9 KiB
Transact-SQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
Regel: SQLI-AX-005 – Dynamics AX-nummerserie er ved at løbe tør
Alvorlighed: Medium; Høj (High) over den kritiske procent eller inden for de kritiske dage; Kritisk (Critical), når serien er opbrugt
Hvad og hvorfor
Når en nummerserie er brugt op, kan AX ikke tildele flere numre: oprettelse eller bogføring af fx ordrer, fakturaer
eller posteringer, der bruger serien, fejler for alle brugere.
Tærskler (agentens konfiguration SQLInsight:DynamicsAx)
NumberSequenceWarnPercent = 80 brugt mindst så mange procent: Medium
NumberSequenceCriticalPercent = 95 …: Høj
ForecastWarnDays = 90 løber tør inden for så mange dage efter prognosen: Medium
ForecastCriticalDays = 30 …: Høj
ForecastBasisDays = 30 prognosen bruger den ældste daglige måling inden for så mange dage (mindst 1 dag gammel)
Vurdering (C#: src/SQLInsight.Rules/DynamicsAx/DynamicsAxRules.cs – AxNumberSequenceExhaustionRule; tal i
src/SQLInsight.Core/DynamicsAx/AxSchemaAdapter.cs – Interpret og AxForecast.From)
Grænse = HIGHEST, eller formatets kapacitet (10^antal # − 1), hvis den er lavere. Brugt % = (NEXTREC − LOWEST) /
(grænse − LOWEST + 1). Tilbage = grænse − NEXTREC + 1. Prognose: forbrug pr. dag = (NEXTREC nu − NEXTREC i den ældste
daglige måling i vinduet) / dage imellem; dage tilbage = (grænse − NEXTREC) / forbrug pr. dag. Målingerne er SQLInsights
egne (insight.AxSequenceSamples, én pr. serie pr. dag, gemt i HourlyDays = 400 dage). Manuelle, cykliske og spærrede
serier springes over. Højst 25 fund pr. database.
Collector: src/SQLInsight.Collectors/DynamicsAx/AxSequenceCollector.cs – AxSequenceCollector.CollectAsync (hver time).
SQL: src/SQLInsight.Core/DynamicsAx/AxSchemaAdapter.cs – NumberSequences.
Rettigheder: Valgfri AX-læseadgang, kun SELECT (db/permissions/grant-dynamics-ax-read.sql):
Axapta 3.0: GRANT SELECT ON [bmssa].[NUMBERSEQUENCETABLE] TO [<agentens bruger>];
GRANT SELECT ON [bmssa].[NUMBERSEQUENCEREFERENCE] TO [<agentens bruger>]; (valgfri)
AX 4.0/2009: samme tabeller i [dbo]
AX 2012/F&O: GRANT SELECT ON [dbo].[NUMBERSEQUENCETABLE] TO [<agentens bruger>];
GRANT SELECT ON [dbo].[NUMBERSEQUENCESCOPE] TO [<agentens bruger>]; (valgfri: firma)
GRANT SELECT ON [dbo].[NUMBERSEQUENCEREFERENCE] TO [<agentens bruger>]; (valgfri)
Mangler NUMBERSEQUENCETABLE, er reglen "ikke målbar" med den nøjagtige GRANT for databasen.
Versioner: SQL Server 2005 og nyere (ikke 2000). Axapta 3.0: verificeret mod en kundes skema (schema-ax30.tsv).
AX 4.0, AX 2009, AX 2012 og F&O: baseret på Microsofts dokumenterede skema – ikke verificeret mod kundedata.
Læsning: Kun SELECT med TOP (de mest brugte først), READ COMMITTED med SET LOCK_TIMEOUT 2000 (SNAPSHOT, når databasen
tillader det), aldrig NOLOCK, i AX-databasen via [db].sys.sp_executesql. Kun i databaser genkendt som AX og
valgt til overvågning.
*/
-- >>> SQL, som SQLInsight kører – genereret fra koden (tests/SQLInsight.Collectors.Tests/SqlScriptTests.cs). Ret ikke herunder i hånden.
-- Variant: Axapta 3.0 (skema bmssa): nummerserier (AxSchemaAdapter.NumberSequences)
DECLARE @Database sysname;
SET @Database = N'AXDB'; -- <== ret til AX-databasen
DECLARE @Schema sysname;
SET @Schema = N'bmssa'; -- <== ejeren af AX-tabellerne: bmssa i Axapta 3.0, ellers dbo
DECLARE @Sql nvarchar(max);
SET @Sql = N'SET NOCOUNT ON; SET LOCK_TIMEOUT 2000; SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SELECT TOP (5000) CAST(n.[RECID] AS bigint), n.[NUMBERSEQUENCE], n.[DATAAREAID], n.[TXT], CAST(n.[LOWEST] AS bigint), CAST(n.[HIGHEST] AS bigint), CAST(n.[NEXTREC] AS bigint), n.[FORMAT], ISNULL(n.[BLOCKED], 0), ISNULL(n.[CONTINUOUS], 0), ISNULL(n.[CYCLIC], 0), ISNULL(n.[MANUAL], 0), CASE WHEN EXISTS (SELECT 1 FROM [bmssa].[NUMBERSEQUENCEREFERENCE] r WHERE r.[NUMBERSEQUENCE] = n.[NUMBERSEQUENCE] AND r.[DATAAREAID] = n.[DATAAREAID]) THEN 1 ELSE 0 END, n.[LATESTCLEANDATE], n.[LATESTCLEANTIME], CAST(n.[CLEANINTERVAL] AS float), DATEDIFF(minute, GETDATE(), GETUTCDATE()) FROM [bmssa].[NUMBERSEQUENCETABLE] n ORDER BY (CAST(n.[NEXTREC] AS float) - n.[LOWEST]) / NULLIF(CAST(n.[HIGHEST] AS float) - n.[LOWEST], 0) DESC';
SET @Sql = REPLACE(@Sql, N'[bmssa].', QUOTENAME(@Schema) + N'.');
DECLARE @Procedure nvarchar(300);
SET @Procedure = QUOTENAME(@Database) + N'.sys.sp_executesql';
EXEC @Procedure @Sql;
SET LOCK_TIMEOUT -1; SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
GO
-- Variant: AX 4.0 (skema dbo) – ikke verificeret mod kundedata: nummerserier (AxSchemaAdapter.NumberSequences)
DECLARE @Database sysname;
SET @Database = N'AXDB'; -- <== ret til AX-databasen
DECLARE @Schema sysname;
SET @Schema = N'dbo'; -- <== ejeren af AX-tabellerne: bmssa i Axapta 3.0, ellers dbo
DECLARE @Sql nvarchar(max);
SET @Sql = N'SET NOCOUNT ON; SET LOCK_TIMEOUT 2000; SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SELECT TOP (5000) CAST(n.[RECID] AS bigint), n.[NUMBERSEQUENCE], n.[DATAAREAID], n.[TXT], CAST(n.[LOWEST] AS bigint), CAST(n.[HIGHEST] AS bigint), CAST(n.[NEXTREC] AS bigint), n.[FORMAT], ISNULL(n.[BLOCKED], 0), ISNULL(n.[CONTINUOUS], 0), ISNULL(n.[CYCLIC], 0), ISNULL(n.[MANUAL], 0), CASE WHEN EXISTS (SELECT 1 FROM [dbo].[NUMBERSEQUENCEREFERENCE] r WHERE r.[NUMBERSEQUENCE] = n.[NUMBERSEQUENCE] AND r.[DATAAREAID] = n.[DATAAREAID]) THEN 1 ELSE 0 END, n.[LATESTCLEANDATE], n.[LATESTCLEANTIME], CAST(n.[CLEANINTERVAL] AS float), DATEDIFF(minute, GETDATE(), GETUTCDATE()) FROM [dbo].[NUMBERSEQUENCETABLE] n ORDER BY (CAST(n.[NEXTREC] AS float) - n.[LOWEST]) / NULLIF(CAST(n.[HIGHEST] AS float) - n.[LOWEST], 0) DESC';
SET @Sql = REPLACE(@Sql, N'[dbo].', QUOTENAME(@Schema) + N'.');
DECLARE @Procedure nvarchar(300);
SET @Procedure = QUOTENAME(@Database) + N'.sys.sp_executesql';
EXEC @Procedure @Sql;
SET LOCK_TIMEOUT -1; SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
GO
-- Variant: AX 2009 – baseret på Microsofts dokumenterede skema, ikke verificeret mod kundedata: nummerserier (AxSchemaAdapter.NumberSequences)
DECLARE @Database sysname;
SET @Database = N'AXDB'; -- <== ret til AX-databasen
DECLARE @Schema sysname;
SET @Schema = N'dbo'; -- <== ejeren af AX-tabellerne: bmssa i Axapta 3.0, ellers dbo
DECLARE @Sql nvarchar(max);
SET @Sql = N'SET NOCOUNT ON; SET LOCK_TIMEOUT 2000; SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SELECT TOP (5000) CAST(n.[RECID] AS bigint), n.[NUMBERSEQUENCE], n.[DATAAREAID], n.[TXT], CAST(n.[LOWEST] AS bigint), CAST(n.[HIGHEST] AS bigint), CAST(n.[NEXTREC] AS bigint), n.[FORMAT], ISNULL(n.[BLOCKED], 0), ISNULL(n.[CONTINUOUS], 0), ISNULL(n.[CYCLIC], 0), ISNULL(n.[MANUAL], 0), CASE WHEN EXISTS (SELECT 1 FROM [dbo].[NUMBERSEQUENCEREFERENCE] r WHERE r.[NUMBERSEQUENCE] = n.[NUMBERSEQUENCE] AND r.[DATAAREAID] = n.[DATAAREAID]) THEN 1 ELSE 0 END, n.[LATESTCLEANDATETIME], CAST(NULL AS int), CAST(n.[CLEANINTERVAL] AS float), DATEDIFF(minute, GETDATE(), GETUTCDATE()) FROM [dbo].[NUMBERSEQUENCETABLE] n ORDER BY (CAST(n.[NEXTREC] AS float) - n.[LOWEST]) / NULLIF(CAST(n.[HIGHEST] AS float) - n.[LOWEST], 0) DESC';
SET @Sql = REPLACE(@Sql, N'[dbo].', QUOTENAME(@Schema) + N'.');
DECLARE @Procedure nvarchar(300);
SET @Procedure = QUOTENAME(@Database) + N'.sys.sp_executesql';
EXEC @Procedure @Sql;
SET LOCK_TIMEOUT -1; SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
GO
-- Variant: AX 2012 RTM/R2/R3 og F&O – baseret på Microsofts dokumenterede skema, ikke verificeret mod kundedata: nummerserier (AxSchemaAdapter.NumberSequences)
DECLARE @Database sysname;
SET @Database = N'AXDB'; -- <== ret til AX-databasen
DECLARE @Schema sysname;
SET @Schema = N'dbo'; -- <== ejeren af AX-tabellerne: bmssa i Axapta 3.0, ellers dbo
DECLARE @Sql nvarchar(max);
SET @Sql = N'SET NOCOUNT ON; SET LOCK_TIMEOUT 2000; SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SELECT TOP (5000) CAST(n.[RECID] AS bigint), n.[NUMBERSEQUENCE], NULLIF(sc.[DATAAREA], N''''), n.[TXT], CAST(n.[LOWEST] AS bigint), CAST(n.[HIGHEST] AS bigint), CAST(n.[NEXTREC] AS bigint), n.[FORMAT], ISNULL(n.[BLOCKED], 0), ISNULL(n.[CONTINUOUS], 0), ISNULL(n.[CYCLIC], 0), ISNULL(n.[MANUAL], 0), CASE WHEN EXISTS (SELECT 1 FROM [dbo].[NUMBERSEQUENCEREFERENCE] r WHERE r.[NUMBERSEQUENCEID] = n.[RECID]) THEN 1 ELSE 0 END, n.[LATESTCLEANDATETIME], CAST(NULL AS int), CAST(n.[CLEANINTERVAL] AS float), DATEDIFF(minute, GETDATE(), GETUTCDATE()) FROM [dbo].[NUMBERSEQUENCETABLE] n LEFT JOIN [dbo].[NUMBERSEQUENCESCOPE] sc ON sc.[RECID] = n.[NUMBERSEQUENCESCOPE] ORDER BY (CAST(n.[NEXTREC] AS float) - n.[LOWEST]) / NULLIF(CAST(n.[HIGHEST] AS float) - n.[LOWEST], 0) DESC';
SET @Sql = REPLACE(@Sql, N'[dbo].', QUOTENAME(@Schema) + N'.');
DECLARE @Procedure nvarchar(300);
SET @Procedure = QUOTENAME(@Database) + N'.sys.sp_executesql';
EXEC @Procedure @Sql;
SET LOCK_TIMEOUT -1; SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
GO
-- <<< slut på genereret SQL