Inside Microsoft Dynamics AX 2012 R3 is out--Get your copy!
Duplicate consolidation transactions in AX 2012
When we run a consolidation for a period that has already been consolidated, AX looks to see if the transaction exists already in the consolidation company’s consolidation history, and if it does the original transaction is removed and the new one is posted for the new consolidation. AX will not look at the actual ledger transactions themselves, but rather the consolidation history which is contained in the LEDGERCONSOLIDATEHISTREF table.
If you run the consolidation process and you notice transactions for a previous period are being duplicated, the likely cause of the issue is a separation between the ledger transaction (GENERALJOURNALACCOUNTENTRY) and the consolidation history reference (LEDGERCONSOLIDATEHISTREF). The consolidation history reference can be seen in General ledger | Inquiries | Consolidations | Transactions | Actuals. The problematic consolidation duplication occurs when we run a consolidation for a period that has already been consolidated, and does not have the transactions in the consolidation history reference. Since the source that AX looks at is the consolidation history reference rather than the ledger transactions, the new consolidation will post transactions that already exist again if there's no reference to the transaction in LEDGERCONSOLIDATEHISTREF.
To illustrate what this looks like, we'll post a single transaction in the subsidiary company, consolidate the transaction, delete the LEDGERCONSOLIDATEHISTREF (to recreate the data condition), show the duplication, and then how to fix it.
First, here's our original transaction in the subsidiary company:
This transaction is then consolidated, which can be seen in General ledger | Inquiries | Consolidations | Transactions | Actuals:
To show what is supposed to happen when we reconsolidate, let's run the consolidation again and examine the results. We can see that original consolidation's transactions have been removed, and the same transactions have been posted to the new consolidation:
In earlier versions of AX, this form contained a "Delete" button. The most common cause of consolidation duplicate transactions was a user deleting the transaction from this form, which leaves the transaction in the ledger but removes it from the consolidation history. As mentioned earlier, AX uses the LEDGERCONSOLIDATEHISTREF table to identify if a consolidation transaction already exists. By removing the transaction from this form, we're separating the ledger transaction from the consolidation history and the transactions will be duplicated upon reconsolidation. To simulate recreating this issue, we'll delete these records in SQL:
DELETE FROM LEDGERCONSOLIDATEHISTREF WHERE DATAAREAID = 'CEC'
We can see this removed the transactions from General ledger | Inquiries | Consolidations | Transactions | Actuals form, but the transactions still exist in the ledger:
The problem occurs when the data is in this state and we consolidate the same period. AX will look the LEDGERCONSOLIDATEHISTREF table, and since the transaction does not exist there a new one is posted even though it already exists in the system. We'll consolidate again to see the balances have doubled:
This is an issue of damaged data, and the only way to correct this will be in SQL. What we will do to isolate these records is look for any transaction that exists in the consolidation company that does not have a related record in consolidation history reference. This will include all transactions (revaluations, general journals, opening/closing transactions, etc.) so we'll need to restrict it further. Consolidation transactions do not have a voucher associated with them, so we query based on the voucher being blank and the accounting date. If you're not using a voucher on the opening/closing transactions, the following will not work as those transactions will also be removed. Here is what the select statement would look like to isolate these orphaned records, where the accounting date is the date of the subsidiary transaction to be consolidated and "CEC" is the legal entity name:
SELECT * FROM GENERALJOURNALACCOUNTENTRY GJAE
INNER JOIN GENERALJOURNALENTRY GJE ON GJAE.GENERALJOURNALENTRY = GJE.RECID
INNER JOIN LEDGER L ON GJE.LEDGER = L.RECID
WHERE L.NAME='CEC'
AND ACCOUNTINGDATE BETWEEN '8-1-2014' AND '8-31-2014'
AND GJE.SUBLEDGERVOUCHER = ''
AND GJAE.RECID NOT IN (SELECT TRANSRECID FROM LEDGERCONSOLIDATEHISTREF WHERE DATAAREAID = 'CEC')
We can see the two orphaned records are returned:
If these transactions that are returned are the same records that are duplicated and you've verified that no other transaction types are included in the data, you can delete these transactions from the system. The query is similar to the select, but deleting from GENERALJOURNALACCOUNTENTRY:
DELETE GJAEFROM GENERALJOURNALACCOUNTENTRY GJAE
INNER JOIN GENERALJOURNALENTRY GJE ONG JAE.GENERALJOURNALENTRY = GJE.RECID
INNER JOIN LEDGER L ON GJE.LEDGER = L.RECID
WHERE L.NAME='CEC'
AND ACCOUNTINGDATE BETWEEN '8-1-2014' AND '8-31-2014'
AND GJE.SUBLEDGERVOUCHER = ''
AND GJAE.RECID NOT IN (SELECT TRANSRECID FROM LEDGERCONSOLIDATEHISTREF WHERE DATAAREAID = 'CEC')
Since we are deleting records from GENERALJOURNALACCOUNTENTRY, you will need to rebuild your financial dimension set balances to ensure accurate information throughout the system. Once the rebuild is complete, we can see the trial balance is once again correct:
To ensure the fix is complete, we'll run a final consolidation and show the trial balance remains the same.
We can see future consolidations will process correctly, and the issue of duplicate transactions is resolved. Please note that these queries are not something that should be run in a production system, and these are provided for reference only, and will need to be modified for every environment.
Tyler Lewin
Senior Support Engineer - Dynamics AX
AX Content: Search for all things Microsoft Dynamics AX
I do it. And I’m sure you do it too. We get into a habit of only using the search tools that we’ve always used. You might use Google, I use Bing…but, did you ever wonder if there were other ways to search for AX-specific information? Wonder no longer. Listed below are a few of the additional tools or methods that you can use to search for all things AX.
WebSearchAX
If you’ve been around the AX world for a while, I would hope that you already know about this fantastic tool. WebSearchAX gives you advanced search capabilities specific to AX. Use this tool to search for TechNet and MSDN topics, code examples, white papers, and blog entries related to AX. You can filter your search results by user role, AX version, AX module, and even by language.
Issue searches
Did you know that Microsoft Dynamics Lifecycle Services has built-in search functionality that lets you search for hotfixes, KB articles, regulatory features, and workarounds for reported issues in AX? Read more about it in the following TechNet topic: ttp://technet.microsoft.com/EN-US/library/dn268610.aspx.
Searching within TechNet and MSDN topics
So, you found the topic that you want on TechNet or MSDN, but what if that topic is super long? Most browsers have a “find on page” feature that lets you search for words or phrases on a web page. In Internet Explorer, use the CTRL+F shortcut key to open the Find” toolbar. You can enter a search term in this toolbar when you’re viewing a TechNet or MSDN topic to locate a specific word or phrase in that topic.
Microsoft Dynamics Community search
When you initially search the Microsoft Dynamics Community site, you get a results list that is not filtered. You can narrow your search results down by using the built-in site filters on the left side of the search results list. You can filter by type of content (Forums, logs, Comments, Files, Users, etc.) and you can filter by product, author, or tags. I suggest that you always filter your results to show only the Microsoft Dynamics AX product.
Search engines
Listed below are my favorite search string “tricks” that I use to help narrow down my search results in standard search engines.
- OR, NOT
You can use OR and NOT in your search string to help you get better results. For example, to search for information about AX 2012, but not AX 2009, you would enter the following search string: AX 2012 NOT 2009. To search for organizational hierarchy or organization structure in AX 2012, you would enter the following search string: AX 2012 organization hierarchy OR AX 2012 organization structure.
- Quotation
marks “”
To search for an exact phrase, put quotation marks around your search string. Without quotation marks, when you enter a complete sentence or phrase as a search string, most search engines will ignore articles such as “the” and “and”.
- Image
size (Bing)
When I’m searching for an image or diagram, I get frustrated when small, unreadable thumbnails are returned in my image search results. To fix this, I search for images that are larger than 200 pixels by adding imagesize:large to the end of my search string. For example: AX 2012 imagesize:large or Organizational hierarchy imagesize:large.
- Site-specific
search
If you are trying to find a topic on a specific site, you can add site:[site URL] to the end of your search string. For example, to find a topic about AX shortcut keys that you know is published on TechNet, you would enter the following search string: AX shortcut keys site:technet.microsoft.com.
- The wildcard
For when you remember bits and pieces of a topic title, or a half sentence from a web page that you want to find again, you can use the asterisk (*) symbol as a wildcard in the search string. For example, I vaguely remember seeing a topic with a title that entioned configuring features in AX. To find this topic, I’d enter the following search string: Configure * features AX 2012.
Most common search engines publish a complete list of advanced search string syntax that users can use to enhance their search experience. Bing’s Advanced Operator Reference is published here: http://msdn.microsoft.com/en-us/library/ff795620.aspx (Click the topic link and then refer to the Table of Contents pane on the left side of MSDN to see the entire list of operators).
How do you search for AX-specific information?
Are you a search expert? Or, do you have a unique way to find the AX information that you need? If so, please share your AX search tips with the community by commenting on this post and sharing what you know.
AX Content: Microsoft Dynamics AX form list
Microsoft Dynamics AX has many form topics and searching for and finding help for the topic on TechNet can be a challenge. Help is here. The Information experience team has published a list that contains links to all form Help topics. You can find the Microsoft Dynamics AX form list here or if you are searching through the table of contents on TechNet, the topic is here:
There are hundreds of form topics available. To quickly locate the topic you’re looking for, open the Microsoft Dynamics AX form list topic on TechNet. You can use the “find on page” feature that lets you search for words or phrases on a web page. In Internet Explorer, use the CTRL+F shortcut key to open the “Find” toolbar. You can enter either the name or the AOT name of the form in this toolbar.
Here is an example of the type of information you’ll find in a form topic:
As always, comment on this post to let us know how these resources work for you, or send email to the Information Experience team at adocs@microsoft.com.
SEPA Direct Debit: Error message 'SEPA Customer:: The Mandate is not specified'
In some AX 2012 and AX 2012 R2 the error message „CUSTOM-EXCEPTION:: SEPA Customer:: The mandate is not
specified“ occurs trying to generate a SEPA Direct Debit payment.
To solve this issue, please have a look at the attached PDF.
Microsoft Dynamics AX general performance analysis scripts
Did you know there is a set of analysis scripts that come with the tool Performance Analyzer 1.20 and can be used to assess the health of your AX system and identify potential causes of performance issues?
After downloading the tool (see above link), you can find these scripts in the following folders:
- ..\DynamicsPerf1.20 RC0.zip\DynamicsPerf\DynamicsPerf - Analysis Scripts
- ..\DynamicsPerf1.20 RC0.zip\DynamicsPerf\Scripts - Dynamics AX
This series of blog posts also includes these scripts and a general performance troubleshooting checklist will follow in another series (linking into these) to assist with interpreting and acting on the results.
This is page 1 of 8. Use the links in the table below to navigate between pages.
- General analysis | |
---|---|
Analyse SQL Configuration | Page 1 |
Analyse SQL Indexes | Page 2 |
Analyse SQL Queries | Page 3 |
Analyse Blocking | Page 4 |
Baseline - benchmark queries | Page 5 |
- AX Specific | |
Analyse AX Configuration | Page 6 |
Analyse AX Indexes | Page 7 |
Analyse AX Queries | Page 8 |
Analyse SQL Configuration
SQLSERVER_INFO
WINDOWS_VERSION
SQL_SERVICES
DISK_VOLUMES
SQL_REGISTRY
SQL_CONFIGURATION
DATA_BUFFER_CACHE
SQL_DATABASES
SQL_DATABASEFILES
SQL_VLFS
SQL_JOBS
SQL_LOGS
SQL_TRACE_FLAGS
SQL_TRIGGERS
FILE_ALLOCATION_UNIT_SIZE
DISK_SECTOR_ALIGNMENT
---------------------------------------------------------------- --
-- SQLSERVER_INFO
-- SQL Server Installation
----------------------------------------------------------------
USE DynamicsPerf
SELECT *
FROM SERVERINFO
ORDER BY STATS_TIME DESC
----------------------------------------------------------------
--
-- WINDOWS_VERSION
--
-- Windows version information for this SQL Server instance
-- Current Service Pack?
-- NOTE: This will be blank if not SQL Server 2008R2 SP1 or later
----------------------------------------------------------------
SELECT *
FROM SERVER_OS_VERSION_VW
----------------------------------------------------------------
--
-- SQL_SERVICES
--
-- SQL Server Services
-- What account are the services running under?
-- NOTE: This will be blank if not SQL Server 2008R2 SP1 or later
----------------------------------------------------------------
SELECT servicename,
startup_type_desc,
status_desc,
process_id,
last_startup_time,
service_account,
is_clustered
FROM sys.dm_server_services;
----------------------------------------------------------------
--
-- DISK_VOLUMES
--
-- SQL Server Disk Volumes information for all drives that
-- has a database located on it.
-- Is free disk space low?
-- NOTE: This will be blank if not SQL Server 2008R2 SP1 or later
----------------------------------------------------------------
SELECT *
FROM SERVER_DISKVOLUMES
----------------------------------------------------------------
--
-- SQL_REGISTRY
--
-- SQL Server Registry values
-- What Trace flags are set?
-- NOTE: This will be blank if not SQL Server 2008R2 SP1 or later
----------------------------------------------------------------
SELECT *
FROM SERVER_REGISTRY
----------------------------------------------------------------
--
-- SQL_CONFIGURATION
--
-- SQL configuation issues
-- 1. Max Degree of Parallelism set to 1 ?
-- 2- Is the SQL Build the current build ?
-- 3- AWE enabled on large memory 32bit systems?
-- 4- Is Max Server Memory set to something less than total server memory?
-----------------------------------------------------------------
SELECT *
FROM SQL_CONFIGURATION_CURR_VW
----------------------------------------------------------------
--
-- DATA_BUFFER_CACHE
--
-- Data Buffer Cache
-- 1. Which database is consuming the largest amount of buffer cache ?
-- are we capturing perf data on that database?
----------------------------------------------------------------
-- By Database
SELECT *
FROM BUFFER_DETAIL_CURR_VW
ORDER BY SIZE_MB DESC
/*********************************************************************
--RUN this code in any database to get DB specific buffer information
SELECT
db.name, OBJ.NAME ,index_id ,
COUNT(*)AS CACHED_PAGES_COUNT
FROM sys.dm_os_buffer_descriptors AS BD
INNER JOIN
(
SELECT obj.name AS NAME
,index_id ,ALLOCATION_UNIT_ID
FROM sys.allocation_units AS AU
INNER JOIN sys.partitions AS P
ON AU.CONTAINER_ID = P.HOBT_ID
AND (AU.type = 1 OR AU.type = 3)
INNER JOIN sys.sysobjects AS obj
on obj.id = P.object_id
UNION ALL
SELECT obj.name AS NAME
,index_id, ALLOCATION_UNIT_ID
FROM sys.allocation_units as AU
INNER JOIN sys.partitions AS P
ON AU.CONTAINER_ID = P.PARTITION_ID
AND AU.type = 2
INNER JOIN sys.sysobjects AS obj
on obj.id = P.object_id
) AS OBJ
ON BD.allocation_unit_id = OBJ.ALLOCATION_UNIT_ID
INNER JOIN sys.databases db ON BD.database_id = db.database_id
WHERE db.name = DB_NAME() and db.state_desc = 'ONLINE'
GROUP BY db.database_id,db.name, OBJ.NAME, index_id
ORDER BY 4 DESC,db.database_id,db.name, OBJ.NAME, index_id
*********************************************************************/
----------------------------------------------------------------
--
-- SQL_DATABASES
--
-- Investigate databases on this SQL instance
--
-- Is there more then 1 production database
-- Are there multiple Dynamics production databases AX and CRM as an example
-- Is development or test databases on this SQL instance
----------------------------------------------------------------
SELECT *
FROM SQL_DATABASES_CURR_VW
----------------------------------------------------------------
--
-- SQL_DATABASEFILES
--
-- Investigate database files
--
-- Are the data and log files on the same drive
-- Is the database set to auto-grow
-- Is there 1 tempdb file per CPU core
----------------------------------------------------------------
SELECT *
FROM SQL_DATABASEFILES_CURR_VW
----------------------------------------------------------------
--
-- SQL_VLFS
--
-- Investigate Virtual Log files for each database LOG file
-- VLF_Count > 10k requires attention
----------------------------------------------------------------
SELECT DATABASE_NAME,FILEID,
COUNT(*) AS VLF_COUNT,
SUM(CASE WHEN STATUS = 0 THEN 1 ELSE 0 END) AS FREE,
SUM(CASE WHEN STATUS != 0 THEN 1 ELSE 0 END) AS INUSE
FROM LOGINFO
GROUP BY DATABASE_NAME,FILEID
ORDER BY DATABASE_NAME,FILEID
----------------------------------------------------------------
--
-- SQL_JOBS
--
-- Investigate SQL Jobs
--
-- Is there a database backup job
-- Is there a database maintenance job to rebuild indexes and update statistics
-- Are there jobs that could stress the server
----------------------------------------------------------------
SELECT *
FROM SQL_JOBS_CURR_VW
----------------------------------------------------------------
--
-- SQL_LOGS
--
-- Investigate SQL Error LOG
--
-- Is failed entries?
--
-- NOTE: If no data in this table, you need to install latest
-- SQL Server cumulative update
----------------------------------------------------------------
SELECT *
FROM SQLErrorLog
WHERE LOGTEXT LIKE '%error%'
----------------------------------------------------------------
--
-- SQL_TRACE_FLAGS
--
-- Investigate SQL Trace Flags that are configured
-- 1117 - Evenly grow database files
-- 1118 - Eliminate Mixed Extents (can increase performance at expense of disk space)
-- 1224 - Override lock escalation, only enable on large memory systems
-- 2371 - SQL 2008 R2 SP1 and later, auto-update statistics occurs more frequently
-- 4136 - Causes SQL Optimizer to use Density Vector instead of Histogram [not recommended - alternative solution for parameter sniffing here].
-- 4199 - Enable all optimizer changes implmented since RTM, should almost always have this on
-- 7646 - Trace Flag to reduce contention on Fulltext indexes
----------------------------------------------------------------
SELECT *
FROM TRACEFLAGS
----------------------------------------------------------------
--
-- SQL_TRIGGERS
--
-- Investigate Database Triggers
----------------------------------------------------------------
SELECT * FROM TRIGGER_TABLE
----------------------------------------------------------------
--
-- Run the following from a command line on the SQL Server
-- Bytes per Cluster should be 64k
--
-- fsutil fsinfo ntfsinfo f:
--
-- Run the following from a command line on the SQL Server
--
--
-- WMIC /OUTPUT:C:\SQLTRACE\PARTALIGN.html PARTITION GET DeviceID, StartingOffset /FORMAT:htable
--
----------------------------------------------------------------
Microsoft Dynamics AX general performance analysis scripts page 2
This is page 2 of 8 of the general performance analysis scripts online for the Performance Analyser 1.20 tool. See page 1 for the introduction. Use the links in the table below to navigate between pages.
- General analysis | |
---|---|
Analyse SQL Configuration | Page 1 |
Analyse SQL Indexes | Page 2 |
Analyse SQL Queries | Page 3 |
Analyse Blocking | Page 4 |
Baseline - benchmark queries | Page 5 |
- AX Specific | |
Analyse AX Configuration | Page 6 |
Analyse AX Indexes | Page 7 |
Analyse AX Queries | Page 8 |
Analyse SQL Indexes
INDEXES_BY_SIZE
INDEX_ACTIVITY
COMPRESSED_INDEXES
EXACT_DUPLICATE_INDEXES
SUBSET_DUPLICATE_INDEXES
INCLUDED_COLUMN_INDEXES
UNUSED_INDEXES
TABLES_WITHOUT_CLUSTERED_INDEX
ADJUST_CLUSTERED_INDEXES
INDEXES_BEING_SCANNED
SEARCH_QUERY_PLANS_FOR_INDEX_USAGE
-- --------------------------------------------------------------
--
-- INDEXES_BY_SIZE
--
-- List top 100 Largest Tables, Investigate for data retention
-- purposes or incorrect application configuration such as logging
----------------------------------------------------------------
USE DynamicsPerf
SELECT TOP 100 DATABASE_NAME,
TABLE_NAME,
Sum(CASE
WHEN INDEX_DESCRIPTION LIKE 'CLUSTERED%'
OR INDEX_DESCRIPTION LIKE 'HEAP%' THEN PAGE_COUNT * 8 / 1024
END) AS SIZEMB_DATA,
Sum(CASE
WHEN INDEX_DESCRIPTION LIKE 'NONCLUSTERED%' THEN PAGE_COUNT * 8 / 1024
END) AS SIZEMB_INDEXES,
Count(CASE
WHEN INDEX_DESCRIPTION LIKE 'NONCLUSTERED%' THEN TABLE_NAME
END) AS NO_OF_INDEXES,
Max(CASE
WHEN ( DATA_COMPRESSION > 0 )
AND ( INDEX_DESCRIPTION LIKE 'CLUSTERED%'
OR INDEX_DESCRIPTION LIKE 'HEAP%' ) THEN 'Y'
ELSE 'N'
END) AS DATA_COMPRESSED,
Max(CASE
WHEN ( DATA_COMPRESSION > 0 )
AND ( INDEX_DESCRIPTION LIKE 'NONCLUSTERED%' ) THEN 'Y'
ELSE 'N'
END) AS INDEXES_COMPRESSED
FROM INDEX_STATS_CURR_VW
GROUP BY DATABASE_NAME,
TABLE_NAME
ORDER BY 3 DESC
-- --------------------------------------------------------------
--
-- INDEX_ACTIVITY
--
-- List Activity by table, Investigate for activity
-- purposes or incorrect application configuration such as logging
----------------------------------------------------------------
SELECT DATABASE_NAME,
TABLE_NAME,
CASE
WHEN ( Sum(USER_UPDATES + USER_SEEKS + USER_SCANS
+ USER_LOOKUPS) = 0 ) THEN NULL
ELSE ( Cast(Sum(USER_SEEKS + USER_SCANS + USER_LOOKUPS) AS DECIMAL) / Cast(Sum(USER_UPDATES + USER_SEEKS + USER_SCANS
+ USER_LOOKUPS) AS DECIMAL) )
END AS RatioOfReads,
CASE
WHEN ( Sum(USER_UPDATES + USER_SEEKS + USER_SCANS
+ USER_LOOKUPS) = 0 ) THEN NULL
ELSE ( Cast(Sum(USER_UPDATES) AS DECIMAL) / Cast(Sum(USER_UPDATES + USER_SEEKS + USER_SCANS
+ USER_LOOKUPS) AS DECIMAL) )
END AS RatioOfWrites,
Sum(USER_SEEKS + USER_SCANS + USER_LOOKUPS) AS TotalReadOperations,
Sum(USER_UPDATES) AS TotalWriteOperations,
Sum(USER_UPDATES + USER_SEEKS + USER_SCANS
+ USER_LOOKUPS) AS TotalOperations
FROM INDEX_STATS_CURR_VW /*sys.dm_db_index_usage_stats*/
GROUP BY DATABASE_NAME,
TABLE_NAME
--order by TotalOperations desc
--order by TotalReadOperations desc
ORDER BY TotalWriteOperations DESC
-- --------------------------------------------------------------
--
-- COMPRESSED_INDEXES
--
-- Find indexes that are compressed.
----------------------------------------------------------------
SELECT *
FROM INDEX_STATS_CURR_VW
WHERE DATA_COMPRESSION > 0
ORDER BY USER_UPDATES DESC
-- --------------------------------------------------------------
--
-- EXACT_DUPLICATE_INDEXES
--
-- Tables that have 2 or more indexes with the exact same key
-- Trust me, this happens.
-- NOTE: If you are using included columns the indexes may be unique
----------------------------------------------------------------
SELECT DATABASE_NAME,
TABLE_NAME,
INDEX_KEYS,
Count(*)
FROM INDEX_STATS_CURR_VW
GROUP BY DATABASE_NAME,
TABLE_NAME,
INDEX_KEYS
HAVING Count(INDEX_KEYS) > 1
ORDER BY TABLE_NAME
-- --------------------------------------------------------------
--
-- SUBSET_DUPLICATE_INDEXES
--
-- Just as bad (and even more common) are indexes that are a left key
-- subset of another index on same table. Unless the subsset key is
-- unique, its usefulness is subsumed of the superset key.
-- NOTE: If you are using included columns the indexes may be unique
----------------------------------------------------------------
SELECT O.DATABASE_NAME,
O.TABLE_NAME,
O.INDEX_NAME AS SUBSET_INDEX,
O.INDEX_KEYS AS SUBSET_INDEX_KEYS,
O.INDEX_DESCRIPTION AS SUBSET_INDEX_DESCRIPTION,
O.PAGE_COUNT * 8 / 1024 AS SUBSET_SIZE_MB,
I.INDEX_NAME AS SUPERSET_INDEX,
I.INDEX_KEYS AS SUPERSET_KEYS
FROM INDEX_STATS_CURR_VW O
LEFT JOIN INDEX_STATS_CURR_VW I
ON I.RUN_NAME = O.RUN_NAME
AND I.DATABASE_NAME = O.DATABASE_NAME
AND I.TABLE_NAME = O.TABLE_NAME
AND I.INDEX_KEYS <> O.INDEX_KEYS
AND I.INDEX_KEYS LIKE O.INDEX_KEYS + ',%'
WHERE O.INDEX_DESCRIPTION NOT LIKE '%UNIQUE%'
AND I.INDEX_NAME IS NOT NULL
ORDER BY O.DATABASE_NAME,
I.TABLE_NAME,
I.INDEX_KEYS
-- --------------------------------------------------------------
--
-- INCLUDED_COLUMN_INDEXES
--
-- Find indexes with high number of include columns
-- This can be indication of table that needs a different clustered index
-- or poorly designed query. Will cause table size BLOAT and
-- potential blocking issues as SQL updates the included columns
----------------------------------------------------------------
SELECT TOP 100 *
FROM INDEX_STATS_CURR_VW
WHERE INCLUDED_COLUMNS <> 'N/A'
ORDER BY LEN(INCLUDED_COLUMNS) DESC
-- --------------------------------------------------------------
--
-- UNUSED_INDEXES
--
-- Find indexes that are not being used. If an index enforces
-- a uniqueness constraint, we must retain it.
--
--**************************************************************
-- DO NOT DELETE THESE INDEXES UNLESS YOU ARE SURE YOU HAVE RUN
-- EVERY PROCESS IN YOUR DYNAMICS DATABASE INCLUDING YEAR END !!
--**************************************************************
----------------------------------------------------------------
SELECT PAGE_COUNT * 8 / 1024 AS SIZE_MB,
*
FROM INDEX_HISTORICAL_VW
WHERE
-- criteria for never been used indexes
USER_SEEKS = 0
AND USER_SCANS = 0
-- uncomment next 2 lines if you want to see indexes with very low usages
--AND USER_SEEKS < 100
--AND USER_SCANS < 100
AND INDEX_DESCRIPTION NOT LIKE '%UNIQUE%'
AND INDEX_DESCRIPTION NOT LIKE '%HEAP%'
AND (PAGE_COUNT * 8 / 1024) > 0 -- only show indexes consuming space
ORDER BY 1 DESC
-- --------------------------------------------------------------
--
-- TABLES_WITHOUT_CLUSTERED_INDEX
--
-- Tables missing clustered indexes
-- Heaps with multiple non-clustered indexes.
-- Use the following script to identify a good clustered index
-- based solely on user activity
----------------------------------------------------------------
SELECT CLUS.TABLE_NAME,
CLUS.INDEX_NAME AS HEAP_TABLE,
CLUS.INDEX_KEYS AS CLUSTERED_KEYS,
NONCLUS.INDEX_NAME AS NONCLUSTERED_INDEX,
NONCLUS.INDEX_KEYS,
( NONCLUS.RANGE_SCAN_COUNT - CLUS.RANGE_SCAN_COUNT ) AS NONCLUSTERED_VS_CLUSTERED_RANGE_COUNT,
CLUS.USER_SEEKS AS CLUSTERED_USER_SEEKS,
CLUS.USER_SCANS AS CLUSTERED_USER_SCANS,
CLUS.SINGLETON_LOOKUP_COUNT AS CLUSTERED_SINGLE_LOOKUPS,
CLUS.RANGE_SCAN_COUNT AS CLUSTERED_RANGE_SCAN,
NONCLUS.USER_SEEKS AS NONCLUSTERED_USER_SEEKS,
NONCLUS.USER_SCANS AS NONCLUSTERED_USER_SCANS,
NONCLUS.SINGLETON_LOOKUP_COUNT AS NONCLUSTERED_SINGLE_LOOKUPS,
NONCLUS.RANGE_SCAN_COUNT AS NONCLUSTERED_RANGE_SCANS,
NONCLUS.USER_UPDATES AS NONCLUSTERED_USER_UPDATES
FROM INDEX_STATS_CURR_VW CLUS
INNER JOIN INDEX_STATS_CURR_VW NONCLUS
ON CLUS.TABLE_NAME = NONCLUS.TABLE_NAME
AND CLUS.DATABASE_NAME = NONCLUS.DATABASE_NAME
AND CLUS.INDEX_NAME <> NONCLUS.INDEX_NAME
WHERE CLUS.INDEX_DESCRIPTION LIKE 'HEAP%'
AND ( ( NONCLUS.RANGE_SCAN_COUNT > CLUS.RANGE_SCAN_COUNT )
OR ( NONCLUS.SINGLETON_LOOKUP_COUNT > CLUS.SINGLETON_LOOKUP_COUNT ) )
ORDER BY CLUS.USER_LOOKUPS DESC,
CLUS.TABLE_NAME,
( NONCLUS.RANGE_SCAN_COUNT - CLUS.RANGE_SCAN_COUNT ) DESC
-- ----------------------------------------------------------------------------------------
--
-- ADJUST_CLUSTERED_INDEXES
--
--
-- Find clustered indexes that could be changed
-- to 1 of the non-clustered indexes that has more usage than the clustered index
-- Use the following script to identify the non-clustered index
-- that could be the clustered index based solely on user activity
-- This should be the LAST activty done in a performance tuning session
--
--NOTE - CHANGING CLUSTERED INDEXES WILL TAKE LONG TIME TO DO
-- AND REQUIRES DOWNTIME TO IMPLEMENT
--------------------------------------------------------------------------------------------
SELECT CLUS.TABLE_NAME,
CLUS.INDEX_NAME AS CLUSTERED_INDEX,
CLUS.INDEX_KEYS AS CLUSTERED_KEYS,
NONCLUS.INDEX_NAME AS NONCLUSTERED_INDEX,
NONCLUS.INDEX_KEYS,
( NONCLUS.RANGE_SCAN_COUNT - CLUS.RANGE_SCAN_COUNT ) AS NONCLUSTERED_VS_CLUSTERED_RANGE_COUNT,
CLUS.USER_SEEKS AS CLUSTERED_USER_SEEKS,
CLUS.USER_SCANS AS CLUSTERED_USER_SCANS,
CLUS.SINGLETON_LOOKUP_COUNT AS CLUSTERED_SINGLE_LOOKUPS,
CLUS.RANGE_SCAN_COUNT AS CLUSTERED_RANGE_SCAN,
NONCLUS.USER_SEEKS AS NONCLUSTERED_USER_SEEKS,
NONCLUS.USER_SCANS AS NONCLUSTERED_USER_SCANS,
NONCLUS.SINGLETON_LOOKUP_COUNT AS NONCLUSTERED_SINGLE_LOOKUPS,
NONCLUS.RANGE_SCAN_COUNT AS NONCLUSTERED_RANGE_SCANS,
NONCLUS.USER_UPDATES AS NONCLUSTERED_USER_UPDATES
FROM INDEX_STATS_CURR_VW CLUS
INNER JOIN INDEX_STATS_CURR_VW NONCLUS
ON CLUS.TABLE_NAME = NONCLUS.TABLE_NAME
AND CLUS.DATABASE_NAME = NONCLUS.DATABASE_NAME
AND CLUS.INDEX_NAME <> NONCLUS.INDEX_NAME
WHERE CLUS.INDEX_DESCRIPTION LIKE 'CLUSTERED%' AND (( NONCLUS.RANGE_SCAN_COUNT > CLUS.RANGE_SCAN_COUNT )
OR ( NONCLUS.SINGLETON_LOOKUP_COUNT > CLUS.SINGLETON_LOOKUP_COUNT ))
ORDER BY CLUS.USER_LOOKUPS DESC, CLUS.TABLE_NAME,
( NONCLUS.RANGE_SCAN_COUNT - CLUS.RANGE_SCAN_COUNT ) DESC
-- --------------------------------------------------------------
--
-- INDEXES_BEING_SCANNED
--
-- Find non-clustered indexes that are being scanned. Generally
-- this will indicate that key columns are out of order compared
-- to query predicates
--
----------------------------------------------------------------
SELECT TOP 100 *
FROM INDEX_STATS_CURR_VW
WHERE USER_SCANS > 0
AND INDEX_DESCRIPTION LIKE 'NONCLUSTERED%'
ORDER BY USER_SCANS DESC
-- --------------------------------------------------------------
--
-- SEARCH_QUERY_PLANS_FOR_INDEX_USAGE
--
-- Using indexes identifies in the previous query, list queries
-- whose execution plan references a specific index; order by
-- most expensive (logical reads)
--
----------------------------------------------------------------
SELECT TOP 100 *
FROM QUERY_STATS_CURR_VW
WHERE QUERY_PLAN_TEXT LIKE '%Index_Name%'
ORDER BY TOTAL_LOGICAL_READS DESC
Microsoft Dynamics AX general performance analysis scripts page 3
This is page 3 of 8 of the general performance analysis scripts online for the Performance Analyser 1.20 tool. See page 1 for the introduction. Use the links in the table below to navigate between pages.
- General analysis | |
---|---|
Analyse SQL Configuration | Page 1 |
Analyse SQL Indexes | Page 2 |
Analyse SQL Queries | Page 3 |
Analyse Blocking | Page 4 |
Baseline - benchmark queries | Page 5 |
- AX Specific | |
Analyse AX Configuration | Page 6 |
Analyse AX Indexes | Page 7 |
Analyse AX Queries | Page 8 |
Analyse SQL Queries
EXPENSIVE_QUERIES
MISSING_INDEX_QUERIES
QUERIES_WITH_MULTIPLE_EXECUTION_PLANS
QUERIES_SCANNING_TABLES
USE DynamicsPerf
----------------------------------------------------------------
--
-- EXPENSIVE_QUERIES
--
-- List top 100 most expensive queries
----------------------------------------------------------------
SELECT TOP 100 *
FROM QUERY_STATS_CURR_VW QS -- Queries from last data collection only
--FROM QUERY_STATS_VW QS -- Review queries for all data collections
WHERE
--Remove queries with comments
NOT EXISTS (SELECT QUERY_HASH
FROM COMMENTS C
WHERE C.QUERY_HASH = QS.QUERY_HASH) -- Remove queries that have comments
-- AND QUERY_HASH = 0x0000000000 -- find a specific query
-- AND SQL_TEXT LIKE '%VALUE%' -- find all SQL statements that contain a specific text i.e. table name
-- AND QUERY_PLAN_TEXT LIKE '%VALUE%' -- find all SQL Plans that contain a specific text i.e. index name
-- AND LAST_EXECUTION_TIME > 'XXXXXXX' -- find all queries that have executed after a specific time
-- AND DATABASE_NAME = 'XXXXXXXXX' -- find all queries for a specific database
-- AND MAX_ELAPSED_TIME /10 > AVG_ELAPSED_TIME -- Find all queries potentially getting blocked or paramater sniffing issues
ORDER BY TOTAL_ELAPSED_TIME DESC -- Queries consuming most TOTAL time on SQL Server
-- ORDER BY AVG_LOGICAL_READS DESC -- Queries potentially causing large disk i/o
-- ORDER BY EXECUTION_COUNT DESC -- High execution count could be loops in application code
-- ORDER BY TOTAL_LOGICAL_READS DESC -- Queries to review to potentially reduce disk i/o
----------------------------------------------------------------
--
-- MISSING_INDEX_QUERIES
--
-- Identify queries that the optimizer suspects can be optimized
-- by new or changed indexes:
--
-- NOTE: DO NOT add these indexes verbatim without
-- deep analysis. Large INCLUDED lists are NOT recommended
-- for ERP solutions
----------------------------------------------------------------
SELECT TOP 100 *
FROM MISSING_INDEXES_CURR_VW
WHERE NOT EXISTS (SELECT QUERY_HASH
FROM COMMENTS C
WHERE C.QUERY_HASH = MISSING_INDEXES_CURR_VW.QUERY_HASH) -- Remove queries that have comments
AND INDEX_IMPACT > 75
AND EXECUTION_COUNT > 100
AND AVG_ELAPSED_TIME > 20
AND AVG_LOGICAL_READS > 1000
ORDER BY TOTAL_LOGICAL_READS DESC
----------------------------------------------------------------
--
-- QUERIES_WITH_MULTIPLE_EXECUTION_PLANS
--
-- List queries that have more than 1 execution plan
-- NOTE: Only useful on SQL2008 or greater
-- This is for potential parameter sniffing issues
----------------------------------------------------------------
SELECT DATABASE_NAME,
QUERY_HASH,
COUNT(QUERY_PLAN_HASH) AS CNT
FROM QUERY_STATS_CURR_VW
GROUP BY DATABASE_NAME,
QUERY_HASH
HAVING COUNT(QUERY_PLAN_HASH) > 1
ORDER BY CNT DESC
----------------------------------------------------------------
--
-- QUERIES_SCANNING_TABLES
--
-- Find queries scanning a table
----------------------------------------------------------------
SELECT TOP 100 *
FROM QUERY_STATS_CURR_VW
WHERE ( QUERY_PLAN_TEXT LIKE '%TABLE SCAN%'
OR QUERY_PLAN_TEXT LIKE '%INDEX SCAN%' )
--AND QUERY_PLAN_TEXT LIKE '%<Table Name>%' -- Comment this line to return all tables
ORDER BY TOTAL_LOGICAL_READS DESC
Microsoft Dynamics AX general performance analysis scripts page 4
This is page 4 of 8 of the general performance analysis scripts online for the Performance Analyser 1.20 tool. See page 1 for the introduction. Use the links in the table below to navigate between pages.
- General analysis | |
---|---|
Analyse SQL Configuration | Page 1 |
Analyse SQL Indexes | Page 2 |
Analyse SQL Queries | Page 3 |
Analyse Blocking | Page 4 |
Baseline - benchmark queries | Page 5 |
- AX Specific | |
Analyse AX Configuration | Page 6 |
Analyse AX Indexes | Page 7 |
Analyse AX Queries | Page 8 |
Analyse Blocking
SQLTRACE_BLOCKING
OPTIONAL_BLOCKING_QUERIES
/***************** SQLTRACE_BLOCKING **********************************
*
*
* NOTE:DYNPERF_Default_Trace_Start Job MUST be setup and running
*
*
**************************************************************************/
/********* This view reads the Trace files direct
*** from the @PATH parameter on the DYNPERF_Default_Trace_Start job *******/
SELECT *
FROM [BLOCKED_PROCESS_VW]
ORDER BY END_TIME DESC
--Run in the database to find a key lock
USE DYNAMICSDB --<-----------------PUT YOUR DBNAME HERE
GO
SELECT o.name,
i.name
FROM sys.partitions p
JOIN sys.objects o
ON p.object_id = o.object_id
JOIN sys.indexes i
ON p.object_id = i.object_id
AND p.index_id = i.index_id
WHERE p.hobt_id = 72057709223149568 -- key: 15:72057709223149568(aldk9nn887) as example
--Run in the database to find an object lock
USE DYNAMICSDB --<-----------------PUT YOUR DBNAME HERE
GO
SELECT o.name,
i.name
FROM sys.partitions p
JOIN sys.objects o
ON p.object_id = o.object_id
JOIN sys.indexes i
ON p.object_id = i.object_id
AND p.index_id = i.index_id
WHERE o.object_id = 72057709223149568 -- object: 15:72057709223149568 as example
--Summarize blocks by resource
SELECT WAIT_RESOURCE,
COUNT(WAIT_RESOURCE)
FROM (SELECT WAIT_RESOURCE
FROM [BLOCKED_PROCESS_VW]) AS A
GROUP BY WAIT_RESOURCE
ORDER BY 2 DESC
/******** Read the trace directly *************/
SELECT E.name,
F.*
--FROM fn_trace_gettable('C:\SQLTRACE\DYNAMICS_DEFAULT.trc', DEFAULT) F,
--sys.trace_events EXECUTE
FROM fn_trace_gettable(isnull((SELECT TRACE_FULL_PATH_NAME
FROM DYNAMICSPERF_SETUP), (SELECT TOP 1 path
FROM sys.traces
WHERE path LIKE '%DYNAMICS_DEFAULT%')), DEFAULT) F,
sys.trace_events E
WHERE EventClass = trace_event_id
ORDER BY StartTime DESC
/**********************************************************************************************************
*
* OPTIONAL_BLOCKING_QUERIES
*
* NOTE: The DYNPERF_Optional_Polling_for_Blocking must be run for any of the following queries to
* have data. This job is not intended to run full time but only optionally when more
* information is needed for api_cursorfetch sql statements in blocking conditions.
*
*
*
**********************************************************************************************************/
/*************************************************************************
Find all lead blockers with a wait time > 2 seconds from most recent to oldest
*************************************************************************/
SELECT *
FROM BLOCKS_VW
WHERE BLOCKER_STATUS = 'Lead Blocker'
AND WAIT_TIME > 2000
ORDER BY BLOCKED_DTTM DESC
/*************************************************************************
Find all lead blockers from most recent to oldest
*************************************************************************/
SELECT *
FROM BLOCKS_VW
--WHERE BLOCKER_STATUS = 'Lead Blocker'
ORDER BY BLOCKED_DTTM DESC
/*************************************************************************
Find all lead blockers with a wait time > 2 seconds on a specific date
from most recent to oldest
*************************************************************************/
SELECT *
FROM BLOCKS_VW
WHERE BLOCKER_STATUS = 'Lead Blocker'
AND WAIT_TIME > 2000
AND BLOCKED_DTTM BETWEEN '5/20/2008' AND '5/21/2008'
ORDER BY BLOCKED_DTTM DESC
/*************************************************************************
Which applications are the Lead Blocker the most
*************************************************************************/
SELECT BLOCKER_PROGRAM,
COUNT(*) AS NUMBER
FROM BLOCKS_VW
WHERE BLOCKER_STATUS = 'Lead Blocker'
GROUP BY BLOCKER_PROGRAM
ORDER BY NUMBER DESC
/*************************************************************************
Which applications are Blocked the most
*************************************************************************/
SELECT BLOCKED_PROGRAM,
COUNT(*) AS NUMBER
FROM BLOCKS_VW
GROUP BY BLOCKED_PROGRAM
ORDER BY NUMBER DESC
/*************************************************************************
Which applications are causing the most waiting
*************************************************************************/
SELECT BLOCKER_PROGRAM,
SUM(WAIT_TIME) AS WAITTIME
FROM BLOCKS_VW
GROUP BY BLOCKER_PROGRAM
ORDER BY WAITTIME DESC
/*************************************************************************
Which applications are waiting the most
*************************************************************************/
SELECT BLOCKED_PROGRAM,
SUM(WAIT_TIME) AS WAITTIME
FROM BLOCKS_VW
GROUP BY BLOCKED_PROGRAM
ORDER BY WAITTIME DESC
/*************************************************************************
Which objects are waiting the most
*************************************************************************/
SELECT OBJECT_NAME,
SUM(WAIT_TIME) AS WAITTIME
FROM BLOCKS_VW
GROUP BY OBJECT_NAME
ORDER BY WAITTIME DESC
/*************************************************************************
Which databases are waiting the most
*************************************************************************/
SELECT DATABASE_NAME,
SUM(WAIT_TIME) AS WAITTIME
FROM BLOCKS_VW
GROUP BY DATABASE_NAME
ORDER BY WAITTIME DESC
Microsoft Dynamics AX general performance analysis scripts page 5
This is page 5 of 8 of the general performance analysis scripts online for the Performance Analyser 1.20 tool. See page 1 for the introduction. Use the links in the table below to navigate between pages.
- General analysis | |
---|---|
Analyse SQL Configuration | Page 1 |
Analyse SQL Indexes | Page 2 |
Analyse SQL Queries | Page 3 |
Analyse Blocking | Page 4 |
Baseline - benchmark queries | Page 5 |
- AX Specific | |
Analyse AX Configuration | Page 6 |
Analyse AX Indexes | Page 7 |
Analyse AX Queries | Page 8 |
Baseline - benchmark queries
INDEX_CHANGES_SINCE_BASELINE
QUERIES_SLOWER_THAN_BASELINE
QUERIES_FASTER_THAN_BASELINE
NEW_QUERIES_NOT_IN_BASELINE
QUERIES_IN_BASELINE_BUT_NOT_IN_CURRENT
TRANSACTION_VOLUME_BY_HOUR
TRANSACTION_VOLUME_BY_HOUR_DETAIL
DISK_IO_BY_HOUR
BAD_SQL_WAIT_STATS
DB_GROWTH
TABLE_ACTIVITY
ACTIVITY_COMPARISON_BETWEEN_RUNS
USE [DynamicsPerf]
GO
SELECT *
FROM STATS_COLLECTION_SUMMARY
ORDER BY STATS_TIME DESC
GO
----------------------------------------------------------------
--
-- INDEX_CHANGES_SINCE_BASELINE
--
-- show index changes from BASELINE
----------------------------------------------------------------
EXEC SP_INDEX_CHANGES
@BASELINE = 'BASE_to_compare_to',
@COMPARISON_RUN_NAME = 'Feb_26_2020_804AM'
----------------------------------------------------------------
--
-- QUERIES_SLOWER_THAN_BASELINE
--
-- queries that got worse from BASELINE
----------------------------------------------------------------
SELECT A.QUERY_HASH,
A.EXECUTION_COUNT,
A.BEFORE_AVG_TIME,
A.CURRENT_AVG_TIME,
A.[TIME_DIFF(ms)],
A.[%DECREASE],
A.SQL_TEXT,
B.QUERY_PLAN AS BEFORE_PLAN,
C.QUERY_PLAN AS AFTER_PLAN
FROM (SELECT DISTINCT V1.QUERY_HASH,
V1.EXECUTION_COUNT,
V1.AVG_ELAPSED_TIME AS BEFORE_AVG_TIME,
V2.AVG_ELAPSED_TIME AS CURRENT_AVG_TIME,
V2.AVG_ELAPSED_TIME - V1.AVG_ELAPSED_TIME AS 'TIME_DIFF(ms)',
Cast(( V2.AVG_ELAPSED_TIME - V1.AVG_ELAPSED_TIME ) / CASE V1.AVG_ELAPSED_TIME
WHEN 0 THEN 1
ELSE V1.AVG_ELAPSED_TIME
END * 100 AS DECIMAL(14, 3)) AS '%DECREASE',
V1.SQL_TEXT,
V1.QUERY_PLAN_HASH AS BEFORE_PLAN_HASH,
V2.QUERY_PLAN_HASH AS AFTER_PLAN_HASH
FROM QUERY_STATS_HASH_VW V1
INNER JOIN QUERY_STATS_HASH_VW V2
ON V1.QUERY_HASH = V2.QUERY_HASH
WHERE V1.RUN_NAME = 'BASE_to_compare_to'
AND V2.RUN_NAME = 'Feb_26_2020_804AM'
AND V1.AVG_ELAPSED_TIME < V2.AVG_ELAPSED_TIME
AND V1.QUERY_HASH <> 0x0000000000000000) AS A
CROSS APPLY (SELECT TOP 1 QUERY_PLAN
FROM QUERY_PLANS W1
WHERE W1.QUERY_PLAN_HASH = A.BEFORE_PLAN_HASH) AS B
CROSS APPLY (SELECT TOP 1 QUERY_PLAN
FROM QUERY_PLANS W2
WHERE W2.QUERY_PLAN_HASH = A.AFTER_PLAN_HASH) AS C
ORDER BY 6 DESC
----------------------------------------------------------------
--
-- QUERIES_FASTER_THAN_BASELINE
--
-- queries that got faster from BASELINE
----------------------------------------------------------------
SELECT A.QUERY_HASH,
A.EXECUTION_COUNT,
A.BEFORE_AVG_TIME,
A.CURRENT_AVG_TIME,
A.[TIME_DIFF(ms)],
A.[%IMPROVEMENT],
A.SQL_TEXT,
B.QUERY_PLAN AS BEFORE_PLAN,
C.QUERY_PLAN AS AFTER_PLAN
FROM (SELECT DISTINCT V1.QUERY_HASH,
V1.EXECUTION_COUNT,
V1.AVG_ELAPSED_TIME AS BEFORE_AVG_TIME,
V2.AVG_ELAPSED_TIME AS CURRENT_AVG_TIME,
V1.AVG_ELAPSED_TIME - V2.AVG_ELAPSED_TIME AS 'TIME_DIFF(ms)',
Cast(( V1.AVG_ELAPSED_TIME - V2.AVG_ELAPSED_TIME ) / CASE V2.AVG_ELAPSED_TIME
WHEN 0 THEN 1
ELSE V2.AVG_ELAPSED_TIME
END * 100 AS DECIMAL(14, 3)) AS '%IMPROVEMENT',
V1.SQL_TEXT,
V1.QUERY_PLAN_HASH AS BEFORE_PLAN_HASH,
V2.QUERY_PLAN_HASH AS AFTER_PLAN_HASH
FROM QUERY_STATS_HASH_VW V1
INNER JOIN QUERY_STATS_HASH_VW V2
ON V1.QUERY_HASH = V2.QUERY_HASH
WHERE V1.RUN_NAME = 'BASE_to_compare_to'
AND V2.RUN_NAME = 'Feb_26_2020_804AM'
AND V1.AVG_ELAPSED_TIME > V2.AVG_ELAPSED_TIME
AND V1.QUERY_HASH <> 0x0000000000000000) AS A
CROSS APPLY (SELECT TOP 1 QUERY_PLAN
FROM QUERY_PLANS W1
WHERE W1.QUERY_PLAN_HASH = A.BEFORE_PLAN_HASH) AS B
CROSS APPLY (SELECT TOP 1 QUERY_PLAN
FROM QUERY_PLANS W2
WHERE W2.QUERY_PLAN_HASH = A.AFTER_PLAN_HASH) AS C
ORDER BY 6 DESC
----------------------------------------------------------------
--
-- NEW_QUERIES_NOT_IN_BASELINE
--
-- NEW queries that are not in the BASELINE
----------------------------------------------------------------
SELECT A.QUERY_HASH,
A.BEFORE_AVG_TIME,
A.SQL_TEXT,
B.QUERY_PLAN AS BEFORE_PLAN
FROM (SELECT DISTINCT V1.QUERY_HASH,
V1.AVG_ELAPSED_TIME AS BEFORE_AVG_TIME,
V1.SQL_TEXT,
V1.QUERY_PLAN_HASH AS BEFORE_PLAN_HASH
FROM QUERY_STATS_HASH_VW V1
WHERE V1.RUN_NAME = 'Feb_26_2020_804AM'
AND NOT EXISTS (SELECT QUERY_HASH
FROM QUERY_STATS_HASH_VW V2
WHERE V1.QUERY_HASH = V2.QUERY_HASH
AND V2.RUN_NAME = 'BASE_to_compare_to')
AND V1.QUERY_HASH <> 0x0000000000000000) AS A
CROSS APPLY (SELECT TOP 1 QUERY_PLAN
FROM QUERY_PLANS W1
WHERE W1.QUERY_PLAN_HASH = A.BEFORE_PLAN_HASH) AS B
ORDER BY 2 DESC
------------------------------------------------------------------------------
--
-- QUERIES_IN_BASELINE_BUT_NOT_IN_CURRENT
--
-- queries that were in the BASELINE but not in the comparison capture
-------------------------------------------------------------------------------
SELECT A.QUERY_HASH,
A.BEFORE_AVG_TIME,
A.SQL_TEXT,
B.QUERY_PLAN AS BEFORE_PLAN
FROM (SELECT DISTINCT V1.QUERY_HASH,
V1.AVG_ELAPSED_TIME AS BEFORE_AVG_TIME,
V1.SQL_TEXT,
V1.QUERY_PLAN_HASH AS BEFORE_PLAN_HASH
FROM QUERY_STATS_HASH_VW V1
WHERE V1.RUN_NAME = 'BASE_to_compare_to'
AND NOT EXISTS (SELECT QUERY_HASH
FROM QUERY_STATS_HASH_VW V2
WHERE V1.QUERY_HASH = V2.QUERY_HASH
AND V2.RUN_NAME = 'Feb_26_2020_804AM')
AND V1.QUERY_HASH <> 0x0000000000000000) AS A
CROSS APPLY (SELECT TOP 1 QUERY_PLAN
FROM QUERY_PLANS W1
WHERE W1.QUERY_PLAN_HASH = A.BEFORE_PLAN_HASH) AS B
ORDER BY 2 DESC
----------------------------------------------------------------
--
-- TRANSACTION_VOLUME_BY_HOUR
--
-- Show change in row counts by hour
----------------------------------------------------------------
USE [DynamicsPerf]
--Hourly Totals
SELECT *
FROM PERF_HOURLY_ROWDATA_VW
WHERE ROWRANK = 9999
AND DATABASE_NAME <> 'NULL'
ORDER BY STATS_TIME DESC
----------------------------------------------------------------
--
-- TRANSACTION_VOLUME_BY_HOUR_DETAIL
--
-- Show details of change in row counts by hour
----------------------------------------------------------------
SELECT *
FROM PERF_HOURLY_ROWDATA_VW
WHERE STATS_TIME = 'ENTER_STATS_TIME_HERE_FROM_PREVIOUS_QUERY'
AND TABLE_NAME <> 'NULL'
ORDER BY ROWRANK
----------------------------------------------------------------
--
-- DISK_IO_BY_HOUR
--
-- Hourly Change in Disk IO Stats by File
----------------------------------------------------------------
SELECT *
FROM PERF_HOURLY_IOSTATS_VW
WHERE DATABASE_NAME= 'Dynamics'
ORDER BY STATS_TIME DESC, DATABASE_NAME, FILE_ID
----------------------------------------------------------------
--
-- BAD_SQL_WAIT_STATS
--
-- IO bottleneck : If Top 2 values for wait stats include IO, (ASYNCH_IO_COMPLETION,IO_COMPLETION,LOGMGR,,WRITELOG,PAGEIOLATCH_x_xxx) there is an IO bottleneck.
-- Blocking bottleneck: If top 2 wait_stats values include locking (LCK_M_BU, LCK_M_IS, LCK_M_IU, LCK_% …), there is a blocking bottleneck
-- Parallelism: Cxpacket waits > 5%
----------------------------------------------------------------
/*********************************************************************************************
************************************************************************************************/
SELECT STATS_TIME,
RANK,
WAIT_TYPE,
WAITING_TASKS_LAST_HOUR,
WAIT_TIME_MS_LAST_HOUR
FROM PERF_HOURLY_WAITSTATS_VW
WHERE ( WAIT_TYPE LIKE 'PAGEIOLATCH_%'
OR WAIT_TYPE LIKE 'ASYNCH_IO_COMPLETION%'
OR WAIT_TYPE LIKE 'IO_COMPLETION%'
OR WAIT_TYPE LIKE 'LOGMGR%'
OR WAIT_TYPE LIKE 'WRITELOG%' )
AND RANK < 3
AND WAIT_TIME_MS_LAST_HOUR > 0
--Activity between 2 data collections to look at comparisons over a longer time period
--Find all run_names
SELECT RUN_NAME
FROM STATS_COLLECTION_SUMMARY
ORDER BY STATS_TIME DESC
----------------------------------------------------------------
--
-- DB_GROWTH
--
--Find record count and table size differences between the runs
--Can use this to accurately predict database growth
--NOTE only TOP 1000 tables are returned
--------------------------------------------------------------------------------
SELECT *
FROM fn_dbstats('STARTING_RUN_NAME', 'ENDING_RUN_NAME')
ORDER BY DELTA_SIZEMB DESC
----------------------------------------------------------------
--
-- TABLE_ACTIVITY
--
--Find record read/write and row count differences between the runs
-------------------------------------------------------------------
SELECT A.TABLE_NAME,
B.ROW_COUNT - A.ROW_COUNT AS DELTA_IN_ROWS,
B.TOTALREADOPERATIONS - A.TOTALREADOPERATIONS AS DELTA_IN_READS,
B.TOTALWRITEOPERATIONS - A.TOTALWRITEOPERATIONS AS DELTA_IN_WRITES
FROM INDEX_OPS_VW A
INNER JOIN INDEX_OPS_VW B
ON A.TABLE_NAME = B.TABLE_NAME
AND A.DATABASE_NAME = B.DATABASE_NAME
AND A.RUN_NAME = 'STARTING_RUN_NAME'
AND B.RUN_NAME = 'ENDING_RUN_NAME'
ORDER BY 2 DESC
----------------------------------------------------------------
--
-- SQL_WAIT_STATS_BY_HOUR
--- Hourly Change in SQL Server Wait Stats
----------------------------------------------------------------
SELECT *
FROM PERF_HOURLY_WAITSTATS_VW
ORDER BY STATS_TIME DESC, RANK
----------------------------------------------------------------
--
-- ACTIVITY_COMPARISON_BETWEEN_RUNS
--
--
-- Comparison queries between different data captures
-----------------------------------------------------------------
SELECT D1.RUN_NAME AS RUN1,
D2.RUN_NAME AS RUN2,
D1.SQL_TEXT,
D1.QUERY_PLAN,
D1.AVG_ELAPSED_TIME AS RUN1_AVG_TIME,
D2.AVG_ELAPSED_TIME AS RUN2_AVG_TIME,
D2.AVG_ELAPSED_TIME-D1.AVG_ELAPSED_TIME AS TIME_DIFF,
D1.AVG_LOGICAL_READS AS RUN1_READS,
D2.AVG_LOGICAL_READS AS RUN2_READS,
D2.AVG_LOGICAL_READS-D1.AVG_LOGICAL_READS AS READS_DIFF,
D1.AVG_LOGICAL_WRITES AS RUN1_WRITES,
D2.AVG_LOGICAL_WRITES AS RUN2_WRITES,
D2.AVG_LOGICAL_WRITES-D1.AVG_LOGICAL_WRITES AS WRITES_DIFF,
D1.QUERY_HASH
FROM QUERY_STATS_VW D1
INNER JOIN QUERY_STATS_VW D2
ON D1.QUERY_HASH = D2.QUERY_HASH
AND D1.DATABASE_NAME = D2.DATABASE_NAME
WHERE D1.QUERY_HASH <> 0x0000000000000000
AND D1.RUN_NAME = 'STARTING_RUN_NAME'
AND D2.RUN_NAME = 'ENDING_RUN_NAME'
ORDER BY D2.AVG_ELAPSED_TIME - D1.AVG_ELAPSED_TIME
Microsoft Dynamics AX general performance analysis scripts page 6
This is page 6 of 8 of the general performance analysis scripts online for the Performance Analyser 1.20 tool. See page 1 for the introduction. Use the links in the table below to navigate between pages.
- General analysis | |
---|---|
Analyse SQL Configuration | Page 1 |
Analyse SQL Indexes | Page 2 |
Analyse SQL Queries | Page 3 |
Analyse Blocking | Page 4 |
Baseline - benchmark queries | Page 5 |
- AX Specific | |
Analyse AX Configuration | Page 6 |
Analyse AX Indexes | Page 7 |
Analyse AX Queries | Page 8 |
Analyse AX Configuration
AOS_DEBUG
CONNECTION_CONTEXT
TOO_BIG_FOR_ENTIRE_TABLE_CACHE
TABLES_THAT_COULD_BE_ENTIRE_TABLE_CACHE
ENTIRE_TABLE_CACHE_WITH_UPDATES
OCC_DISABLED
AX_DATABASE_LOGGING
AX_ALERTS_ON_TABLE
AX_BATCH_CONFIGURATION
AOS_CLUSTER_CONFIG
AX_DB_LOGGING_BY_TABLE
NUMBER_SEQUENCE_USAGE
USE DynamicsPerf
--AOS Configuration issues
--
-- AOS_DEBUG
-- --------------------------------------------------------------
-- Is Enable X++ Debug enabled on any AOS Servers.
-- 20% decline in transactions processed on the AOS instances with this enabled
-----------------------------------------------------------------
SELECT SERVER_NAME,
AOS_INSTANCE_NAME,
SETTING_NAME,
SETTING_VALUE
FROM AOS_REGISTRY
WHERE IS_CONFIGURATION_ACTIVE = 'Y'
AND SETTING_NAME = 'xppdebug'
AND SETTING_VALUE <> '0'
-- --------------------------------------------------------------
-- CONNECTION_CONTEXT
-- Is Context_Info enabled on any AOS Servers.
--
-----------------------------------------------------------------
SELECT SERVER_NAME,
AOS_INSTANCE_NAME,
SETTING_NAME,
SETTING_VALUE
FROM AOS_REGISTRY
WHERE IS_CONFIGURATION_ACTIVE = 'Y'
AND SETTING_NAME = 'connectioncontext'
AND SETTING_VALUE <> '0'
--AOT configuration issues
-- TOO_BIG_FOR_ENTIRE_TABLE_CACHE
-- --------------------------------------------------------------
-- Find tables that have entire table cache enabled that are larger than 128K
-- Causes the cache to overflow to disk on the AOS Server
-----------------------------------------------------------------
SELECT A.TABLE_NAME,
APPLICATION_LAYER,
CACHE_LOOKUP,
PAGE_COUNT
FROM AX_TABLE_DETAIL_CURR_VW A,
INDEX_STATS_CURR_VW I
WHERE A.DATABASE_NAME = I.DATABASE_NAME
AND A.TABLE_NAME = I.TABLE_NAME
AND CACHE_LOOKUP = 'EntireTable'
AND ( INDEX_DESCRIPTION = 'HEAP'
OR INDEX_DESCRIPTION LIKE 'CLUSTERED%' )
AND PAGE_COUNT > 16 -- 128kb
--AND PAGE_COUNT> 4 --32KB AX2012RTM
--AND PAGE_COUNT> 12 --96KB AX2012R2
ORDER BY PAGE_COUNT DESC
-- TABLES_THAT_COULD_BE_ENTIRE_TABLE_CACHE
-- --------------------------------------------------------------
-- Find tables that have no cache enabled that are smaller than 128K
-- These could cause lots of roundtrips between AOS and SQL
--
-- NOTE:
-- Table should be static and not updated much before changing
-- cache to Entiretable
-----------------------------------------------------------------
SELECT A.TABLE_NAME,
APPLICATION_LAYER,
CACHE_LOOKUP,
PAGE_COUNT
FROM AX_TABLE_DETAIL_CURR_VW A,
INDEX_STATS_CURR_VW I
WHERE A.DATABASE_NAME = I.DATABASE_NAME
AND A.TABLE_NAME = I.TABLE_NAME
AND CACHE_LOOKUP = 'None'
AND ( INDEX_DESCRIPTION = 'HEAP'
OR INDEX_DESCRIPTION LIKE 'CLUSTERED%' )
AND PAGE_COUNT < 16 -- 128kb
--AND PAGE_COUNT> 4 --32KB AX2012RTM
--AND PAGE_COUNT> 12 --96KB AX2012R2
AND PAGE_COUNT > 0
ORDER BY TABLE_NAME DESC
--
-- ENTIRE_TABLE_CACHE_WITH_UPDATES
--
-- --------------------------------------------------------------
-- Find tables that have entire table cache and show update rate
-- Causes the cache to be refreshed on all AOS instances
-----------------------------------------------------------------
SELECT A.TABLE_NAME,
APPLICATION_LAYER,
CACHE_LOOKUP,
USER_UPDATES
FROM AX_TABLE_DETAIL_CURR_VW A,
INDEX_STATS_CURR_VW I
WHERE A.DATABASE_NAME = I.DATABASE_NAME
AND A.TABLE_NAME = I.TABLE_NAME
AND CACHE_LOOKUP = 'EntireTable'
AND ( INDEX_DESCRIPTION = 'HEAP'
OR INDEX_DESCRIPTION LIKE 'CLUSTERED%' )
ORDER BY USER_UPDATES DESC
--
-- OCC_DISABLED
--
-- --------------------------------------------------------------
-- Find tables above SYS layer that do not have OCC enabled:
--
-----------------------------------------------------------------
SELECT TABLE_NAME
FROM AX_TABLE_DETAIL_CURR_VW
WHERE APPLICATION_LAYER NOT IN ( 'SYS', 'System Table' )
AND OCC_ENABLED = 0
ORDER BY TABLE_NAME
--
-- AX_DATABASE_LOGGING
--
-- --------------------------------------------------------------
-- Find tables above SYS layer that have logging enabled
--
-----------------------------------------------------------------
SELECT *
FROM AX_TABLE_DETAIL_CURR_VW
WHERE APPLICATION_LAYER NOT IN ( 'SYS', 'System Table' )
AND ( DATABASELOG_INSERT = 1
OR DATABASELOG_DELETE = 1
OR DATABASELOG_UPDATE = 1
OR DATABASELOG_RENAME_KEY = 1 )
ORDER BY TABLE_NAME
--
-- AX_ALERTS_ON_TABLE
--
-- --------------------------------------------------------------
-- Find tables above SYS layer that have events enabled
--
-----------------------------------------------------------------
SELECT *
FROM AX_TABLE_DETAIL_CURR_VW
WHERE APPLICATION_LAYER NOT IN ( 'SYS', 'System Table' )
AND ( EVENT_INSERT = 1
OR EVENT_DELETE = 1
OR EVENT_UPDATE = 1
OR EVENT_RENAME_KEY = 1 )
ORDER BY TABLE_NAME
-- SELECT * FROM EVENTRULE -- DO THIS IN THE AX DATABASE TO DISCOVER ABOVE DATA
--AX Application configuration issues
--
-- AX_BATCH_CONFIGURATION
--
-- -----------------------------------------------------------------------------
-- List BATCHGROUP configuration in Dynamics AX
--------------------------------------------------------------------------------
SELECT *
FROM AX_BATCH_CONFIGURATION_VW
--
-- AOS_CLUSTER_CONFIG
--
-- -----------------------------------------------------------------------------
-- List AOS cluster configuration in Dynamics AX
--------------------------------------------------------------------------------
SELECT *
FROM AX_SERVER_CONFIGURATION_VW
--
-- AX_DB_LOGGING_BY_TABLE
--
-- --------------------------------------------------------------
-- List top 200 tables be logged in Dynamics AX
-- NOTE: if this query returns zero rows
-- the AOTEXPORT class has not been run
-----------------------------------------------------------------
SELECT [TABLE_NAME],
[ROWS_LOGGED],
[DATABASELOG_UPDATE],
[DATABASELOG_DELETE],
[DATABASELOG_INSERT]
FROM [AX_DATABASELOGGING_VW]
ORDER BY [ROWS_LOGGED] DESC
--
-- NUMBER_SEQUENCE_USAGE
--
-- -----------------------------------------------------------------------------
-- List NUMBERSEQUENCE table configuration in Dynamics AX
-- Are sequences marked as Coninuous? If so why?
-- Is FETCHAHEADQTY > 0, if not preallocation is not setup for this sequence
-- Pre-allocation requires knowledge of the avg. number of numbers consumed
-- per user process to determine a good value.
--------------------------------------------------------------------------------
SELECT RUN2.[DATABASE_NAME],
RUN2.[COMPANYID],
RUN2.[NUMBERSEQUENCE],
RUN2.[TEXT],
Datediff(hh, RUN1.STATS_TIME, RUN2.STATS_TIME) AS ELAPSED_HOURS,
RUN2.NEXTREC - RUN1.NEXTREC AS TOTAL_NUMBERS_CONSUMED,
( RUN2.NEXTREC - RUN1.NEXTREC ) / ( Datediff(hh, RUN1.STATS_TIME, RUN2.STATS_TIME) ) AS HOURLY_CONSUMPTION_RATE,
RUN2.HIGHEST - RUN2.NEXTREC AS [NUMBERSREMAINING],
RUN2.[CONTINUOUS],
RUN2.[FETCHAHEAD],
RUN2.[FETCHAHEADQTY]
FROM AX_NUM_SEQUENCES_VW RUN1
INNER JOIN AX_NUM_SEQUENCES_VW RUN2
ON RUN1.NUMBERSEQUENCE = RUN2.NUMBERSEQUENCE
AND RUN1.COMPANYID = RUN2.COMPANYID
WHERE RUN1.RUN_NAME = 'BASE_to_compare_to'
AND RUN2.RUN_NAME = 'Feb_26_2020_804AM'
ORDER BY 6 DESC
--To find run_name run the following query
SELECT *
FROM STATS_COLLECTION_SUMMARY
ORDER BY STATS_TIME DESC
-- --------------------------------------------------------------
-- Review number sequence configuration in Dynamics AX
-----------------------------------------------------------------
SELECT *
FROM AX_NUM_SEQUENCES_CURR_VW
WHERE CONTINUOUS = 'Yes'
Microsoft Dynamics AX general performance analysis scripts page 7
This is page 7 of 8 of the general performance analysis scripts online for the Performance Analyser 1.20 tool. See page 1 for the introduction. Use the links in the table below to navigate between pages.
- General analysis | |
---|---|
Analyse SQL Configuration | Page 1 |
Analyse SQL Indexes | Page 2 |
Analyse SQL Queries | Page 3 |
Analyse Blocking | Page 4 |
Baseline - benchmark queries | Page 5 |
- AX Specific | |
Analyse AX Configuration | Page 6 |
Analyse AX Indexes | Page 7 |
Analyse AX Queries | Page 8 |
Analyse AX Indexes
INDEXES_IN_DB_NOT_IN_AOT
INDEXES_WITH_RECVERSION
--
-- INDEXES_IN_DB_NOT_IN_AOT
--
-- --------------------------------------------------------------
-- Find INDEXES that are not defined in the AOT
--
-----------------------------------------------------------------
SELECT *
FROM INDEX_STATS_CURR_VW I
WHERE INDEX_DESCRIPTION <> 'HEAP' AND INDEX_DESCRIPTION NOT LIKE '%FILTERED%'
AND NOT EXISTS (SELECT *
FROM AX_INDEX_DETAIL_CURR_VW A
WHERE A.DATABASE_NAME = I.DATABASE_NAME
AND A.TABLE_NAME = I.TABLE_NAME
AND A.INDEX_NAME = I.INDEX_NAME)
ORDER BY TABLE_NAME,
INDEX_NAME
--
-- INDEXES_WITH_RECVERSION
--
-- --------------------------------------------------------------
-- Find INDEXES that have RECVERSION in the Key or Included list
-- RECVERSION should NOT be apart of AX Indexes do to the
-- frequency of updates
-----------------------------------------------------------------
SELECT *
FROM INDEX_STATS_CURR_VW I
WHERE INDEX_KEYS LIKE '%RECVERSION%'
OR INCLUDED_COLUMNS LIKE '%RECVERSION%'
ORDER BY TABLE_NAME,
INDEX_NAME
.
Microsoft Dynamics AX general performance analysis scripts page 8
This is page 8 of 8 of the general performance analysis scripts online for the Performance Analyser 1.20 tool. See page 1 for the introduction. Use the links in the table below to navigate between pages.
- General analysis | |
---|---|
Analyse SQL Configuration | Page 1 |
Analyse SQL Indexes | Page 2 |
Analyse SQL Queries | Page 3 |
Analyse Blocking | Page 4 |
Baseline - benchmark queries | Page 5 |
- AX Specific | |
Analyse AX Configuration | Page 6 |
Analyse AX Indexes | Page 7 |
Analyse AX Queries | Page 8 |
Analyse AX Queries
AX_LONG_RUNNING_QUERY_TRACE
HIDDEN_SCANS_QUERIES
OPTION_FAST_QUERIES
USER_SCANS_QUERY
--
-- AX_LONG_RUNNING_QUERY_TRACE
--
-- --------------------------------------------------------------
-- Find long running queries from Dynamics AX with source code
-- requires client tracing being enabled on the AOS configuration
----------------------------------------------------------------
SELECT TOP 100 [CREATED_DATETIME],[DATABASE_NAME],[ROW_NUM], [AX_USER_ID], [SQL_DURATION], [SQL_TEXT], [CALL_STACK], [TRACE_CATEGORY], [TRACE_EVENT_CODE], [TRACE_EVENT_DESC], [TRACE_EVENT_DETAILS], [CONNECTION_TYPE], [SQL_SESSION_ID], [AX_CONNECTION_ID], [IS_LOBS_INCLUDED], [IS_MORE_DATA_PENDING], [ROWS_AFFECTED], [ROW_SIZE], [ROWS_PER_FETCH], [IS_SELECTED_FOR_UPDATE], [IS_STARTED_WITHIN_TRANSACTION], [SQL_TYPE], [STATEMENT_ID], [STATEMENT_REUSE_COUNT], [DETAIL_TYPE], [STATS_TIME], [COMMENT]
FROM [AX_SQLTRACE]
ORDER BY [CREATED_DATETIME] DESC
--
-- HIDDEN_SCANS_QUERIES
--
-- --------------------------------------------------------------
-- Find Dynamics AX queries that only seek on DataAreaId
-- NOT USEFUL for other products
-----------------------------------------------------------------
SELECT TOP 100 *
FROM HIDDEN_SCANS_CURR_VW
ORDER BY TOTAL_ELAPSED_TIME DESC
--
-- OPTION_FAST_QUERIES
--
-------------------------------------------------------------------------
-- Find queries option(fast) set that have sort operations
-- Dynamics AX only query
--
-- Either we don't have an index to match the order by clause
-- or the query is potentially to complex for SQL to pick that index
--------------------------------------------------------------------------
SELECT TOP 100 *
FROM QUERY_STATS_CURR_VW
WHERE SQL_TEXT LIKE '%OPTION(FAST%'
AND QUERY_PLAN_TEXT LIKE '%PhysicalOp="Sort"%'
ORDER BY TOTAL_ELAPSED_TIME DESC
--
-- USER_SCANS_QUERY
--
-- --------------------------------------------------------------
-- Find Dynamics queries that are scanning
-----------------------------------------------------------------
SELECT TOP 100 *
FROM USER_SCANS_CURR_VW
ORDER BY TOTAL_ELAPSED_TIME DESC
AX Content: Working together on Labor Day
Permit us once more to vary from the technical nature of our weekly posts. In keeping with an approach we've taken over the past eight or nine month in the articles we post close to holidays, we're pausing to think about what we're doing, check our own goals and invite your response to the thinking that's behind our actions.
So as I thought about this post on the Labor Day holiday in the United States, it occurs to me that this is something of a "free" holiday for many of us. There's generally no particular shopping or gift-giving required, and no elaborate meals or special rituals that we observe, although some people certainly do one or more of each of those activities.
If anything, I wonder if there might be a sense of hesitation, almost ambivalence about this day. Labor Day is a day set aside to honor members of the organized labor movement who struggled for better, safer, more humane working conditions for the people we refer to as "blue collar" workers. If that movement has fallen out of favor with some, it’s not my task here to argue for or against any side of that debate. Rather than staking out a debate position, I'll use the annual commemoration of progress through conflict to make a statement of the obvious and then offer a brief comment on it.
Any genuine conflict – whether between workers and managers, between co-workers, or between a business and its customers – indicates the vulnerability of our relationships. There are myriad reasons for the fragility of our relationships and for our purposes here those reasons don't really matter. But where there's real conflict – not a difference of opinion or a struggle between competing priorities but conflict that breeds enmity – there lies beneath, a damaged or broken relationship.
As I draft this blog post in mid-August 2014 the news seems flooded by this brokenness: in Gaza, in the Ukraine, in Ferguson, Missouri in the United States to cite a few glaring, painful examples. It’s nowhere close to a complete list. Of course, an obvious rejoinder to that statement is that relationships can also be resilient. People who've endured awful suffering from terribly damaged relationships can rebound, recover, and ultimately heal. There's hope in that and a reason to keep working.
But if relationships are more important to workplace success than software, then where does that leave those of us who are brought together through the work of creating and delivering software? We can't provide or deliver or fix relationships in our products, whatever advertising tries to promise. Relationships have to be through care and time and nurturing.
Instead, we try to provide tools that facilitate specific work that needs to be done, and if we’re successful, those tools become invisible as our customers gain competence in using them to complete their work. If that sounds so simple that it's not worth blogging about, I submit that most of our software tools are "in our faces" a bit too much. They can be more noticeable than they should be because of design flaws or because they contain needed complexity that's challenging to install, configure and use.
Our work is to try to make all of that better. On Tuesday after the Labor Day holiday, as you go about your work, I invite you to let us know where we're getting it right and where something needs to be fixed. I invite you to a relationship that's open to confronting what doesn't work, willing to acknowledge what does work, and that thrives within the vulnerability that comes from recognizing that what we're trying hard to do might fail. And then committing to the resilience to make it better for both of us. For all of us.
Have a great Labor Day!
Enabling the new Call Center Channel User Functionality in AX 2012 R3
The release of AX 2012 R3 added a new Call Center module which utilizes Channel Users. These users are set up in the Call Center Channel in the Retail module (Retail >> Common >> Retail channels >> Call centers). The purpose of these specified users is to enable certain feature sets within the sales orders form that enable functionality such as ordering from catalogs and extending customer payment options. If a user is not listed in the Channel User list, they can still access the Call Center module, but the additional sales order functionality will not be available when creating new sales orders. It is important to note that a non-channel user can access a sales order created by a channel user and be able to see those extra options. Whether or not the extra call center functionality exists depend on the user creating the sales order.
Channel users
To access this list go to: Retail >> Common >> Retail channels >> Call centers >> Channel users
The channel user list is a simple list comprised of users who would have access to certain call center features (discussed in the next section below) on the sales order form. If a user is on this list, and when they create a sales order from any module, they will receive the extra functionality associated to the Call Center Channel.
Call center channel
There are 3 options in the Call Center form that enable the feature sets in the Sales Order form. Each one of these is described below and can also be found in the TechNet article linked below.
Set up a call center
http://technet.microsoft.com/EN-US/library/dn497725.aspx
Enable order completion
Select this check box to enable the following features for call center users in the Sales order form:
◦ The payment process – Multiple payment buttons in the Sales order form are enabled when the Enable order completion check box is selected.
◦ The Complete button – This button controls access to the Sales order summary form, where the user can enter payment and submit the order. If the Enable order completion check box is not selected, users can create and save orders, but cannot complete them.
◦ The Recap button – This button controls access to the Sales order summary form, where the user can review the order, and optionally enter payment and submit the order.
◦ The Coupons button – Coupon calculation requires that the order be completed.
Enable directed selling
Select this check box to enable the following directed selling options for call center users in the Sales order form:
◦ Source codes
◦ Scripts
◦ Additional item information
◦ Catalog requests
Enable order price control
Select this check box to enable the following price control options for call center users in the Sales order form:
◦ Price matching
◦ Price details
◦ Price overrides
◦ Margin alerts
Here is a view of the Call Center form with the 3 Call Center options marked that will enable these features.
Sales orders
When these features are selected and a Call center channel user creates a sales order, the sales order form will display different buttons and fields as seen below with a few highlights of the newly available options:
A non-call center channel user would see the following:
AX Performance Troubleshooting Checklist Part 1B [Application and AOS Configuration]
This is the second page of Part 1, in a two part troubleshooting check list for general performance on Microsoft Dynamics AX. This page (Part 1B) covers AX application and AOS configuration settings.
Please see the previous page for the introduction and Part 1A of the check list, which covers SQL Server and Storage Settings. Part 2 (following on later) will cover hardware, indexing, queries, blocking and code (at a high level).
Below there is also a link to a script you can use to gather some of the required information from your system. It requires the Performance Analyser 1.20 to be installed first.
Part 1B: AX application and AOS configuration settings Analysis script: http://blogs.msdn.com/b/axsupport/archive/2014/09/01/microsoft-dynamics-ax-general-performance-analysis-scripts-page-6.aspx Some settings can be checked manually in the application (steps given in the links below) or AX Server Configuration Utility (Start> Administrative Tools> Microsoft Dynamics AX Server Configuration). | |
Application settings | |
Kernel & Application Builds
| |
Number sequences:
*Further details: Number sequence overview [AX 2012]
| |
Database logging and alerts:
| |
Extensible Data Security (XDS) / Record Level Security (RLS):
| |
http://blogs.msdn.com/b/axsupport/archive/2012/04/19/turn-off-keep-update-objects-in-ax-2012-for-new-installations.aspx -Turn off any other configuration keys which are not required (for similar reasons to those above and it is also potentially skipping additional logic in the code which is not required).
| |
Memo fields
| |
Entire table caching:
| |
Record caching
| |
[AX 2012 only]
| |
[AX 2012 only]
| |
Server Configuration - Batch server schedule:
| |
OCC Enabled
| |
AOS Configuration settings | |
Overview:
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
Processor affinity
| |
| |
|
AX Performance Troubleshooting Checklist Part 1A [Introduction and SQL Configuration]
Introduction
This check list is primarily aimed at troubleshooting general performance issues in Microsoft Dynamics AX 2009 and 2012. 'General' here typically means a set of unidentified issues across one or more modules, or indeed the entire application. However it is good practice to have at least a quick check of set up and settings when working with any performance issue. It is important to always have a good foundation to diagnose issues from.
It is based on issues encountered on support and best practice documentation. As always, you should be sure that you fully understand the impact of any changes you make and implement them in your test environment first.
Part 1 covers SQL and AX configuration and is intended to give you the foundation. This in turn is split into 2 parts:
- SQL Server and Storage Settings (this page).
- AX application and AOS configuration (page 2).
Part 2 (following on later) will cover hardware, indexing, queries, blocking and code (at a high level). This naturally involves deeper analysis, which will be iterative and is probably where the vast majority of the time will be spent - but it requires a solid foundation (i.e. part 1).
It is suggested that you analyse/implement changes in three phases - Part 1, Part 2 (except code), code review, however each phase may overlap. You should also plan to spread out your deployments (of any remediation activities for this) as much as possible, for easier diagnosis and reversibility in case of any problems.
Within each section below, the links under 'Recommendation' relate to further details, while the links under 'How to...' relate to implementing the changes. You should ensure you have the information relevant to your software versions.
With each part, there will be a link to an analysis script which you can use to help gather the information from your system. These scripts require the Performance Analyser to be installed:
References:
- SQL Optimization for Microsoft Dynamics AX 2012: Course Number 80428 (training materials), e.g.: https://mbs.microsoft.com/customersource/northamerica/AX/learning/student-training-materials/course80428
- Microsoft Dynamics AX 2012 Installation Guide: http://www.microsoft.com/en-us/download/details.aspx?id=12687
- Microsoft Dynamics AX 2009 White Paper: Planning Database Configuration: http://www.microsoft.com/en-us/download/details.aspx?id=13647
- Dynamics AX Performance team Blog: http://blogs.msdn.com/b/axperf/
- Dynamics AX 2012 System Requirements: http://www.microsoft.com/en-gb/download/details.aspx?id=11094
- Dynamics AX 2009 System Requirements: http://www.microsoft.com/en-us/download/details.aspx?id=26568
- Inside Microsoft Dynamics AX (books/e-books, various AX versions): Microsoft Press
- Technet & MSDN (links below).
Part 1A: SQL Server and storage settings Analysis script: http://blogs.msdn.com/b/axsupport/archive/2014/09/01/microsoft-dynamics-ax-general-performance-analysis-scripts.aspx |
Infrastructure / assumptions:
|
Virtualisation
|
OS
|
SQL Server Instance
|
SQL Server Service
|
TempDB storage
|
AX Database Configuration:
|
Configuring physical storage
|
Trace flags
|
Parameter sniffing fix (dataareaid & partition literals)
|
SQL Server build
|
Triggers
|
SQL Server Agent jobs
|
Data archiving/purging
Database Maintenance Strategies for Dynamics AX: How to accurately predict database growth in Dynamics AX:
|
AX Performance Troubleshooting Checklist Part 2
This is Part 2 of a two part troubleshooting check list for general performance on Microsoft Dynamics AX. This part includes hardware, indexing, queries, blocking and code - at a high level. Please note this is just an outline of key areas for general guidance and not an exhaustive list.
As you have landed here, you will probably be interested in this blog too:
Top 10 issues discovered from Dynamics AX Health Check*
http://blogs.msdn.com/b/axinthefield/archive/2013/06/18/top-10-issues-discovered-from-premier-field-engineer-dynamics-ax-health-check.aspx
*Available through Premier Support Services.
Part 2 naturally involves deeper analysis, which will be iterative and is probably where the vast majority of the time will be spent - but it requires a solid foundation.
It is therefore recommended that you review Part 1 first, which is split over 2 pages:
- Introduction and SQL Server/storage settings.
- AX application and AOS configuration settings.
As with Part 1, each area (below) includes a link to a SQL script (or perfmon templates in the case of hardware analysis), which can assist you with data collection.
- Part 2A: SQL Server and AOS Logs
- Part 2B: Hardware
- Part 2C: Index analysis
- Part 2D: Query analysis
- Part 2E: Blocking
- Part 2F: BI Performance (SSRS/SSAS general tips)
- Part 2G: Network
- Part 2H: Code review
Part 2A: SQL Server and AOS Logs Subsets of both of the following sets of logs can be found in the Performance Analyser. |
AOS: Windows Application and System Event Logs
|
SQL Server Logs
|
Analysis script:There is no SQL script as with other areas, but perfmon templates can be found in the Performance Analyser (details below). |
Collect SQL Server perfmon logs for 24h
|
Collect AOS perfmon logs for 24h
|
PAL (Performance Analysis of Logs) tool
|
Analysis script: http://blogs.msdn.com/b/axsupport/archive/2014/09/01/microsoft-dynamics-ax-general-performance-analysis-scripts-page-2.aspx Note:
|
Indexes to disable
|
Non-clustered indexes
|
Clustered indexes
|
Review
|
Analysis scripts: |
Long queries from AX
|
Expensive queries
|
Missing index queries
|
Scanning queries
|
OPTION (FAST) queries
|
Review
|
Analysis script: http://blogs.msdn.com/b/axsupport/archive/2014/09/01/microsoft-dynamics-ax-general-performance-analysis-scripts-page-4.aspx |
Performance analyser setup
|
AX trace collection
|
Blocking analysis
|
TempDB
|
Real time monitoring
|
Deadlocks
|
General tips
|
Network
|
Subsets of both of the following sets of logs can be found in the Performance Analyser. |
Long query traces
|
AX traces
|
Hotfixes
|
Custom code tuning
|
AX Content: September release of Lifecycle Services (LCS)
It’s always fun to watch each month as each Microsoft Dynamics Lifecycle Services update comes out. Some months we have a lot of new features, and some months just a few, but the site continues to evolve rapidly.
The September release is now available and contains several new features to help you manage your AX 2012 projects.
New features
Here’s a list of the new features that have been added:
- The Cloud-hosted environments tool now has customizable settings for domains, virtual networks, and user accounts.
- The Issue search tool now shows planned and open regulatory features, and includes searchable resources for:
- Regulatory feature updates
- Regulatory white papers, registrations, and reports
- The Infrastructure estimatortool:
- Has become an independent tool that can be accessed outside of the Usage profiler tool
- Can provide sizing estimates for non-production environments, such as development, test, or training environments
- The Upgrade analysistool provides new summary information in the report that it generates.
- Lifecycle Services is now supported on Safari (v5.1.7) and Firefox (v32).
A little bit of background
Lifecycle Services is a cloud-based, collaborative workspace that customers and partners can use to manage Microsoft Dynamics AX projects from pre-sales to implementation and operations. Based on the phase of your project and the industry you are working in, the site provides checklists and tools that help you manage the project. It also provides a dashboard so that you have a single place to get up-to-date project information.
Lifecycle Services is available to customers and partners as part of their support plans. You can access it with your CustomerSource or PartnerSource credentials.
To get the latest Lifecycle Services news
Subscribe to the Lifecycle Services blog to get updates about new features, which are added on a monthly basis. You’ll also receive notifications about when the Lifecycle Services site is down for monthly maintenance.
Sending emails from Dynamics AX to external domains using Exchange Online
When using Exchange Online as your SMTP server in Dynamics AX 2012, you may have encountered this issue when sending emails, for example notifications, by batch:
Method 'send' in COM object of class 'CDO.Message' returned error code 0x8004020F (<unknown>) which means: <unknown>.
The error only occurs when sending emails outside of your organization’s domain, for example to an outlook.com email address.
In your test environment you can make a small change to the sysmailer class to get more details on the exception. Basically add a try/catch block as follows:
Compile CIL (incremental), restart your AOS and test again. This time you will get a more detailed error message:
The server rejected one or more recipient addresses. The server response was: 550 5.7.0 Relay Access Denied.
To resolve this issue you have to configure a connector in your Exchange Online.
You will find the details here: http://technet.microsoft.com/en-us/library/dn554323(v=exchg.150).aspx
Log in to https://portal.office.com with your Admin account, then go to Admin > Exchange > Mail flow > connectors.
In the Incoming connectors section click on the + sign and configure your connector in order to allow SMTP relay from your AOS. This is just an example, you should be more restrictive:
- Connector type: On premises
- Opportunistic TLS
- Domain restrictions: None
- Scope: *
- Sender IP Addresses: external IP address of the AOS
I hope this helps.
Bertrand