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>
86 lines
8 KiB
Transact-SQL
86 lines
8 KiB
Transact-SQL
/*
|
||
Regel: SQLI-AX-008 – Kontinuerlig Dynamics AX-nummerserie er ikke ryddet op
|
||
Alvorlighed: Lav (Low)
|
||
|
||
Hvad og hvorfor
|
||
En kontinuerlig serie må ikke have huller. Numre reserveret af afbrudte sessioner bliver liggende som optagede, til
|
||
serien ryddes op; uden oprydning opstår huller og fejl, og serien bliver en flaskehals, fordi hver tildeling låser
|
||
rækken i NUMBERSEQUENCETABLE, til transaktionen er færdig.
|
||
|
||
Tærskler
|
||
7 dage (fast, AxNumberSequenceContinuousRule.MinimumDays), eller 2 × seriens eget CLEANINTERVAL (timer), hvis længere.
|
||
|
||
Vurdering (C#: src/SQLInsight.Rules/DynamicsAx/DynamicsAxRules.cs – AxNumberSequenceContinuousRule)
|
||
For kontinuerlige (CONTINUOUS = 1), ikke spærrede, ikke manuelle serier, der er taget i brug (NEXTREC > LOWEST): fund,
|
||
når seneste oprydning er ældre end tærsklen eller aldrig er sket (1900-01-01). Seneste oprydning er
|
||
LATESTCLEANDATETIME (UTC) i AX 2009+, og LATESTCLEANDATE + LATESTCLEANTIME (lokal tid, sekunder efter midnat) i
|
||
Axapta 3.0. Mangler kolonnen, gives intet fund. 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>];
|
||
AX 4.0/2009/2012/F&O: GRANT SELECT ON [dbo].[NUMBERSEQUENCETABLE] TO [<agentens bruger>];
|
||
Mangler den, 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. 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, 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
|