Run Programs as a Domain User from None Domain Account

Following on from my blog post for running Microsoft Dynamics Nav as a different user here, I have done some further investigation of the runas command.

Typically the RunAs command is used for local authentication, however there is a little known switch which bypasses local authentication and uses it for network authentication only.

This switch allows you to run the Microsoft Dynamics NAV Client, Development Environment, or any other tools as a domain user, from a none domain user pc.

Example syntax:

runas.exe /netonly /user:<DOMAIN>\<USER> "C:\Program Files (x86)\Microsoft Dynamics NAV\71\RoleTailored Client\finsql.exe"

For ease of access, it’s also possible to update your shortcuts as appropriate:

Firstly, within the shortcut, amend the target by prefixing with:

runas.exe /netonly /user:<DOMAIN>\<USER>

Also ensure you “Change Icon”, otherwise you will update the icon to a square (not good for users):

After running the application, it now asks you for the credentials, then runs the application as normal:

Delete Duplicate Records – Leave Only 1 Behind MS SQL

Recently I’ve had to delete duplicate records from the Record Link table in Microsoft Dynamics Nav.

Using the following SQL, (which uses the OVER clause for partitioning) this deletes all duplicate records, leaving the first unique item behind.

The SQL can be updated easily to function for other tables / scenarios. Currently it uses the following fields to identify duplicates:

  • Description
  • URL 1

The SQL does not look at the Notes, which is stored as a BLOB.

DELETE
FROM [Record Link]
WHERE [Link ID] IN (
		SELECT [Link ID]
		FROM (
			SELECT *
				,ROW_NUMBER() OVER (
					PARTITION BY [Record ID]
					,[Description]
					,[URL1] ORDER BY [Link ID]
					) AS [ItemNumber]
			FROM [Record Link]
			WHERE [Note] IS NULL
			) a
		WHERE ItemNumber > 1
		)

Suggesting MS SQL Index Creations Using sys.dm_db_missing_index_details

Within SQL Server, sys.dm_db_missing_index_details returns indexes which it believes are required by the Query Optimiser. Basically as queries are run in the background, the Query Optimiser makes a record of any optimisations it feels are necessary. Restarting the SQL server resets all these stats.

Using some SQL it is possible to identify potentially missing indexes, create sample SQL to build these indexes. I’ve found the following SQL useful:

SELECT mid.statement
	,migs.avg_total_user_cost * (migs.avg_user_impact / 100.0) * (migs.user_seeks + migs.user_scans) AS improvement_measure
	,'CREATE INDEX [missing_index_' + CONVERT(VARCHAR, mig.index_group_handle) + '_' + CONVERT(VARCHAR, mid.index_handle) + '_' + LEFT(PARSENAME(mid.statement, 1), 32) + ']' + ' ON ' + mid.statement + ' (' + ISNULL(mid.equality_columns, '') + CASE 
		WHEN mid.equality_columns IS NOT NULL
			AND mid.inequality_columns IS NOT NULL
			THEN ','
		ELSE ''
		END + ISNULL(mid.inequality_columns, '') + ')' + ISNULL(' INCLUDE (' + mid.included_columns + ')', '') AS create_index_statement
	,migs.*
	,mid.database_id
	,mid.[object_id]
FROM sys.dm_db_missing_index_groups mig
INNER JOIN sys.dm_db_missing_index_group_stats migs ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details mid ON mig.index_handle = mid.index_handle
WHERE migs.avg_total_user_cost * (migs.avg_user_impact / 100.0) * (migs.user_seeks + migs.user_scans) > 10
ORDER BY migs.avg_total_user_cost * migs.avg_user_impact * (migs.user_seeks + migs.user_scans) DESC

This produces results as follows:

Columns include:

  • statement – The table affected
  • improvement_measure – A generated measure of improvement (based on user cost, impact, seeks etc)
  • create_index_statement – A cut and paste friendly statement to add the index
  • group_handle – Identifies a group of missing indexes. This identifier is unique across the server.
  • unique_compiles – Number of compilations and recompilations that would benefit from this missing index group
  • user_seeks – Number of seeks caused by user queries that the recommended index in the group could have been used for
  • user_scans – Number of scans caused by user queries that the recommended index in the group could have been used for.
  • last_user_seek – Date and time of last seek caused by user queries that the recommended index in the group could have been used for.
  • last_user_scan – Date and time of last scan caused by user queries that the recommended index in the group could have been used for.
  • avg_total_user_cost – Average cost of the user queries that could be reduced by the index in the group.
  • avg_user_impact – Average percentage benefit that user queries could experience if this missing index group was implemented.
  • system_seeks – Number of seeks caused by system queries, such as auto stats queries, that the recommended index in the group could have been used for.
  • last_system_seek – Date and time of last system seek caused by system queries that the recommended index in the group could have been used for.
  • last_system_scan – Date and time of last system scan caused by system queries that the recommended index in the group could have been used for.
  • avg_total_system_cost – Average cost of the system queries that could be reduced by the index in the group.
  • avg_system_impact – Average percentage benefit that system queries could experience if this missing index group was implemented.
  • database_id – The ID number of the database
  • object_id – The ID number of the object

Microsoft Dynamics NAV – View All Active Sessions

With Microsoft Dynamics Nav, there are various ways of viewing all the active sessions within the system. The easiest of which is the “Sessions” page within the software itself:

sessions-menu-option

sessions-window

The downside of this, is that it only shows active sessions on the tier which the user is connected. This is ok for solutions where the system has a single tier, however it is common to have multiple tiers in order to spread load. Checking this way is time consuming as you need to check multiple tiers in order to get a session count.

However using SQL can be an easy way to get the number of active sessions:

SELECT [User ID]
	,[Server Instance Name]
	,[Server Computer Name]
	,[Database Name]
	,[Client Computer Name]
	,[Login Datetime]
FROM [dbo].[Active Session]

It is worth nothing that Nav tidies up this table automatically, but in some cases it may be incorrect. For example if a middle tier crashes out, it could be left with orphaned records until the tier starts up again (or another tier prunes the records down).

Convert MS SQL Query to MySQL Automatically

With a recent project, I’ve had to reproduce a table from a Microsoft SQL Database into a MySQL Database.

The table had lots of fields and would be very time consuming to write the SQL to create the table, but luckily I came across this tool to convert MSSQL to MySQL

MS SQL:

CREATE TABLE [dbo].[CRONUS UK Ltd_$Customer](
	[timestamp] [timestamp] NOT NULL,
	[No_] [varchar](20) NOT NULL,
	[Name] [varchar](50) NOT NULL,
	[Search Name] [varchar](50) NOT NULL,
	[Name 2] [varchar](50) NOT NULL,
	[Address] [varchar](50) NOT NULL,
	[Address 2] [varchar](50) NOT NULL,
	[City] [varchar](30) NOT NULL,
	[Contact] [varchar](50) NOT NULL,
	[Phone No_] [varchar](30) NOT NULL,
	[Telex No_] [varchar](20) NOT NULL,
	[Our Account No_] [varchar](20) NOT NULL,
	[Territory Code] [varchar](10) NOT NULL,
	[Global Dimension 1 Code] [varchar](20) NOT NULL,
	[Global Dimension 2 Code] [varchar](20) NOT NULL,
	[Chain Name] [varchar](10) NOT NULL,
	[Budgeted Amount] [decimal](38, 20) NOT NULL,
	[Credit Limit (LCY)] [decimal](38, 20) NOT NULL,
	[Customer Posting Group] [varchar](10) NOT NULL,
	[Currency Code] [varchar](10) NOT NULL,
	[Customer Price Group] [varchar](10) NOT NULL,
	[Language Code] [varchar](10) NOT NULL,
	[Statistics Group] [int] NOT NULL,
	[Payment Terms Code] [varchar](10) NOT NULL,
	[Fin_ Charge Terms Code] [varchar](10) NOT NULL,
	[Salesperson Code] [varchar](10) NOT NULL,
	[Shipment Method Code] [varchar](10) NOT NULL,
	[Shipping Agent Code] [varchar](10) NOT NULL,
	[Place of Export] [varchar](20) NOT NULL,
	[Invoice Disc_ Code] [varchar](20) NOT NULL,
	[Customer Disc_ Group] [varchar](10) NOT NULL,
	[Country_Region Code] [varchar](10) NOT NULL,
	[Collection Method] [varchar](20) NOT NULL,
	[Amount] [decimal](38, 20) NOT NULL,
	[Blocked] [int] NOT NULL,
	[Invoice Copies] [int] NOT NULL,
	[Last Statement No_] [int] NOT NULL,
	[Print Statements] [tinyint] NOT NULL,
	[Bill-to Customer No_] [varchar](20) NOT NULL,
	[Priority] [int] NOT NULL,
	[Payment Method Code] [varchar](10) NOT NULL,
	[Last Date Modified] [datetime] NOT NULL,
	[Application Method] [int] NOT NULL,
	[Prices Including VAT] [tinyint] NOT NULL,
	[Location Code] [varchar](10) NOT NULL,
	[Fax No_] [varchar](30) NOT NULL,
	[Telex Answer Back] [varchar](20) NOT NULL,
	[VAT Registration No_] [varchar](20) NOT NULL,
	[Combine Shipments] [tinyint] NOT NULL,
	[Gen_ Bus_ Posting Group] [varchar](10) NOT NULL,
	[Picture] [image] NULL,
	[Post Code] [varchar](20) NOT NULL,
	[County] [varchar](30) NOT NULL,
	[E-Mail] [varchar](80) NOT NULL,
	[Home Page] [varchar](80) NOT NULL,
	[Reminder Terms Code] [varchar](10) NOT NULL,
	[No_ Series] [varchar](10) NOT NULL,
	[Tax Area Code] [varchar](20) NOT NULL,
	[Tax Liable] [tinyint] NOT NULL,
	[VAT Bus_ Posting Group] [varchar](10) NOT NULL,
	[Reserve] [int] NOT NULL,
	[Block Payment Tolerance] [tinyint] NOT NULL,
	[IC Partner Code] [varchar](20) NOT NULL,
	[Prepayment %] [decimal](38, 20) NOT NULL,
	[Primary Contact No_] [varchar](20) NOT NULL,
	[Responsibility Center] [varchar](10) NOT NULL,
	[Shipping Advice] [int] NOT NULL,
	[Shipping Time] [varchar](32) NOT NULL,
	[Shipping Agent Service Code] [varchar](10) NOT NULL,
	[Service Zone Code] [varchar](10) NOT NULL,
	[Allow Line Disc_] [tinyint] NOT NULL,
	[Base Calendar Code] [varchar](10) NOT NULL,
	[Copy Sell-to Addr_ to Qte From] [int] NOT NULL,
	[Type of Supply Code] [varchar](10) NOT NULL,
	[Liq_ Payment Terms Code] [varchar](10) NOT NULL,
 CONSTRAINT [CRONUS UK Ltd_$Customer$0] PRIMARY KEY CLUSTERED 
(
	[No_] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO

MySQL:

CREATE TABLE `CRONUS UK Ltd_$Customer`(
	`timestamp` `timestamp` NOT NULL,
	`No_` varchar(20) NOT NULL,
	`Name` varchar(50) NOT NULL,
	`Search Name` varchar(50) NOT NULL,
	`Name 2` varchar(50) NOT NULL,
	`Address` varchar(50) NOT NULL,
	`Address 2` varchar(50) NOT NULL,
	`City` varchar(30) NOT NULL,
	`Contact` varchar(50) NOT NULL,
	`Phone No_` varchar(30) NOT NULL,
	`Telex No_` varchar(20) NOT NULL,
	`Our Account No_` varchar(20) NOT NULL,
	`Territory Code` varchar(10) NOT NULL,
	`Global Dimension 1 Code` varchar(20) NOT NULL,
	`Global Dimension 2 Code` varchar(20) NOT NULL,
	`Chain Name` varchar(10) NOT NULL,
	`Budgeted Amount` decimal(38, 20) NOT NULL,
	`Credit Limit (LCY)` decimal(38, 20) NOT NULL,
	`Customer Posting Group` varchar(10) NOT NULL,
	`Currency Code` varchar(10) NOT NULL,
	`Customer Price Group` varchar(10) NOT NULL,
	`Language Code` varchar(10) NOT NULL,
	`Statistics Group` int NOT NULL,
	`Payment Terms Code` varchar(10) NOT NULL,
	`Fin_ Charge Terms Code` varchar(10) NOT NULL,
	`Salesperson Code` varchar(10) NOT NULL,
	`Shipment Method Code` varchar(10) NOT NULL,
	`Shipping Agent Code` varchar(10) NOT NULL,
	`Place of Export` varchar(20) NOT NULL,
	`Invoice Disc_ Code` varchar(20) NOT NULL,
	`Customer Disc_ Group` varchar(10) NOT NULL,
	`Country_Region Code` varchar(10) NOT NULL,
	`Collection Method` varchar(20) NOT NULL,
	`Amount` decimal(38, 20) NOT NULL,
	`Blocked` int NOT NULL,
	`Invoice Copies` int NOT NULL,
	`Last Statement No_` int NOT NULL,
	`Print Statements` `tinyint` NOT NULL,
	`Bill-to Customer No_` varchar(20) NOT NULL,
	`Priority` int NOT NULL,
	`Payment Method Code` varchar(10) NOT NULL,
	`Last Date Modified` datetime NOT NULL,
	`Application Method` int NOT NULL,
	`Prices Including VAT` `tinyint` NOT NULL,
	`Location Code` varchar(10) NOT NULL,
	`Fax No_` varchar(30) NOT NULL,
	`Telex Answer Back` varchar(20) NOT NULL,
	`VAT Registration No_` varchar(20) NOT NULL,
	`Combine Shipments` `tinyint` NOT NULL,
	`Gen_ Bus_ Posting Group` varchar(10) NOT NULL,
	`Picture` blob NULL,
	`Post Code` varchar(20) NOT NULL,
	`County` varchar(30) NOT NULL,
	`E-Mail` varchar(80) NOT NULL,
	`Home Page` varchar(80) NOT NULL,
	`Reminder Terms Code` varchar(10) NOT NULL,
	`No_ Series` varchar(10) NOT NULL,
	`Tax Area Code` varchar(20) NOT NULL,
	`Tax Liable` `tinyint` NOT NULL,
	`VAT Bus_ Posting Group` varchar(10) NOT NULL,
	`Reserve` int NOT NULL,
	`Block Payment Tolerance` `tinyint` NOT NULL,
	`IC Partner Code` varchar(20) NOT NULL,
	`Prepayment %` decimal(38, 20) NOT NULL,
	`Primary Contact No_` varchar(20) NOT NULL,
	`Responsibility Center` varchar(10) NOT NULL,
	`Shipping Advice` int NOT NULL,
	`Shipping Time` varchar(32) NOT NULL,
	`Shipping Agent Service Code` varchar(10) NOT NULL,
	`Service Zone Code` varchar(10) NOT NULL,
	`Allow Line Disc_` `tinyint` NOT NULL,
	`Base Calendar Code` varchar(10) NOT NULL,
	`Copy Sell-to Addr_ to Qte From` int NOT NULL,
	`Type of Supply Code` varchar(10) NOT NULL,
	`Liq_ Payment Terms Code` varchar(10) NOT NULL,
 CONSTRAINT `CRONUS UK Ltd_$Customer$0` PRIMARY KEY 
(
	`No_` ASC
)
)

;

A simple find and replace in the new SQL to correct tinyint, and then the table can be easily created in the MySQL database.

SQL Remove Trailing Decimal Places

Within a recent project, I’ve been extracting data from Microsoft Dynamics NAV using Taskcentre.

Within NAV, decimals are stored as decimal(38,20), complete will all 20 decimal places..!

Dynamics NAV 20 Decimal Places

The following SQL query is very handy at removing the un-required decimal places (but keeping the accuracy of the data)

SELECT CONVERT(DOUBLE PRECISION, 1.99990000000000000000)
	,CONVERT(DOUBLE PRECISION, 2.00000000000000000000)
	,CONVERT(DOUBLE PRECISION, 3.00005077000000000000)

The results of the above query is:

1.9999
2
3.00005077

SQL Reduce Number of Decimal Places

SQL Server – Find Last Backup Date/Time

With SQL server and 3rd party backup software, it can sometimes be difficult to tell when the last SQL backup has been taken and if the backup software has actually performed the backup or not.

There are 3 main backup types in SQL Server:

  • Full
  • Incremental
  • Transaction Log

The following SQL query will show vital statistics, including when the last backup took place for each backup type. Handy for debugging and checking backup strategies..!

This post is not going to go into the details of how each one works, or recommendations for strategies etc. There are lots of details on guides on the internet, such as this one – Microsoft Technet – Understanding SQL Backups.

 SELECT  name ,
            recovery_model_desc ,
            state_desc ,
            d AS 'Last Full Backup' ,
            i AS 'Last Differential Backup' ,
            l AS 'Last log Backup'
    FROM    ( SELECT    db.name ,
                        db.state_desc ,
                        db.recovery_model_desc ,
                        type ,
                        backup_finish_date
              FROM      master.sys.databases db
                        LEFT OUTER JOIN msdb.dbo.backupset a ON a.database_name = db.name
            ) AS Sourcetable 
        PIVOT 
            ( MAX(backup_finish_date) FOR type IN ( D, I, L ) ) AS MostRecentBackup

For example:

SQL Last Backup