SyntaxHighlighter

Showing posts with label mssql. Show all posts
Showing posts with label mssql. Show all posts

Wednesday, 18 November 2015

SQL Server Remove Command from Replication

I managed to create a script on a replicated database that when run on the live server errored - oops! I've had this before and it stops any other transactions from being replicated so I needed to get rid of the failing command and deal with the script another way.

If you go into the distribution database there is a table called MSrepl_commands. This has all the commands that have been and need to be executed on subscribers. You can literally delete whichever commands you don't want!

If you go into the Replication Monitor and navigate your way through to where the error is, it kindly tells us what the error is and also which command it is. The transaction sequence number relates to
MSrepl_commands.xact_seqno and the command id to MSrepl_commands.command_id.


With this, you can create a DELETE statement to get rid of the command and within a minute it had sorted itself out. Obviously I had to rectify the problem with the original script!

This bit of SQL will do it...

DELETE
FROM MSrepl_commands
WHERE xact_seqno = 0x0002434800000DC8000100000000
AND command_id = 3

Be extra cautious here as deleting the wrong thing could have a catastrophic impact!!!

Friday, 21 December 2012

MSSQL: Create an Audit Structure in SQL Server

I was looking at creating an audit trail for data changes in a SQL Server database. There are many mechanisms for doing this and I decided on having a duplicate table to store the audit trail in. So for example:

Base Table: User.Person
    PersonID int
    Name varchar(100)

Audit Table: Audit.Person
    AuditID int IDENTITY(1,1)
    Action char(1)
    Date datetime
    PersonID int
    Name varchar(100)

So the idea is that for each action that occurs (INSERT, UPDATE, DELETE), the data as it was prior to the action is stored into the associated audit table. Obviously in the case of an INSERT the new ID is stored as there was no data prior to the action.

Each base table has a matching TRIGGER on it. This trigger looks like this:

CREATE TRIGGER [User].[TRG_Person_AuditHandle]
   ON  [User].[Person]
   AFTER INSERT, DELETE, UPDATE
AS 
BEGIN
	IF (SELECT COUNT(1) FROM deleted) > 0 AND (SELECT COUNT(1) FROM inserted) > 0
		INSERT INTO [Audit].[Person] ([Action], [PersonID], [Name])
		SELECT 'U', [PersonID], [Name] FROM deleted
	ELSE IF (SELECT COUNT(1) FROM inserted) > 0
		INSERT INTO [Audit].[Person] ([Action], [PersonID])
		SELECT 'I', [PersonID] FROM inserted
	ELSE IF (SELECT COUNT(1) FROM deleted) > 0
		INSERT INTO [Audit].[Person] ([Action], [PersonID], [Name])
		SELECT 'D', [PersonID], [Name] FROM deleted
END

This now gives a full audit trail to the Person table.

Here's a wicked script to create all the audit tables and the triggers for the base tables:

/*
	Creates the audit data structure for all requried tables
	+ Creates the audit table to hold the changes in
	+ Creates the trigger on the base table to add to the audit table
*/
DECLARE @SQL_DROP_AUDIT_TABLE varchar(MAX),
		@SQL_CREATE_AUDIT_TABLE varchar(MAX),
		@SQL_DROP_AUDIT_TRIGGER varchar(MAX),
		@SQL_CREATE_AUDIT_TRIGGER varchar(MAX)

SET @SQL_DROP_AUDIT_TABLE =
'IF  EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N''[DF_#TABLE_NAME#_Date]'') AND type = ''D'')
	ALTER TABLE [Audit].[#TABLE_NAME#] DROP CONSTRAINT [DF_#TABLE_NAME#_Date]
GO
IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N''[Audit].[#TABLE_NAME#]'') AND type in (N''U''))
	DROP TABLE [Audit].[#TABLE_NAME#]
GO'

SET @SQL_CREATE_AUDIT_TABLE = 
'CREATE TABLE [Audit].[#TABLE_NAME#](
	[AuditID] int IDENTITY(1,1) NOT NULL,
	[Action] char(1) NOT NULL,
	[Date] datetime NOT NULL,
	#COLUMNS#
 CONSTRAINT [PK_Audit_#TABLE_NAME#] PRIMARY KEY CLUSTERED 
(
	[AuditID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [Audit].[#TABLE_NAME#] ADD  CONSTRAINT [DF_Audit_#TABLE_NAME#_Date]  DEFAULT (getdate()) FOR [Date]
GO'

SET @SQL_DROP_AUDIT_TRIGGER = 
'IF  EXISTS (SELECT * FROM sys.triggers WHERE object_id = OBJECT_ID(N''[#SCHEMA_NAME#].[TRG_#TABLE_NAME#_AuditHandle]''))
	DROP TRIGGER [#SCHEMA_NAME#].[TRG_#TABLE_NAME#_AuditHandle]
GO'

SET @SQL_CREATE_AUDIT_TRIGGER =
'CREATE TRIGGER [#SCHEMA_NAME#].[TRG_#TABLE_NAME#_AuditHandle]
   ON  [#SCHEMA_NAME#].[#TABLE_NAME#]
   AFTER INSERT, DELETE, UPDATE
AS 
BEGIN
	IF (SELECT COUNT(1) FROM deleted) > 0 AND (SELECT COUNT(1) FROM inserted) > 0
		INSERT INTO [Audit].[#TABLE_NAME#] ([Action],#COLUMNS#)
		SELECT ''U'',#COLUMNS# FROM deleted
	ELSE IF (SELECT COUNT(1) FROM inserted) > 0
		INSERT INTO [Audit].[#TABLE_NAME#] ([Action], [#ID_COLUMN#])
		SELECT ''I'', [#ID_COLUMN#] FROM inserted
	ELSE IF (SELECT COUNT(1) FROM deleted) > 0
		INSERT INTO [Audit].[#TABLE_NAME#] ([Action],#COLUMNS#)
		SELECT ''D'',#COLUMNS# FROM deleted
END
GO'

-- Get all tables
DECLARE @SQL varchar(MAX), @SQLAuditTrigger varchar(MAX)
DECLARE @Schema varchar(255), @Table varchar(255), @SchemaTable varchar(255)
DECLARE cTables CURSOR FAST_FORWARD READ_ONLY
FOR
	SELECT TABLE_SCHEMA, TABLE_NAME, '[' + TABLE_SCHEMA + '].[' + TABLE_NAME + ']' AS [SCHEMA_TABLE]
	FROM INFORMATION_SCHEMA.TABLES
	WHERE TABLE_SCHEMA NOT IN ('PUT_SCHEMA_NAME_FILTERS_HERE_IF_REQUIRED')
	AND TABLE_NAME NOT IN ('PUT_TABLE_NAME_FILTERS_HERE_IF_REQUIRED')
	ORDER BY TABLE_SCHEMA, TABLE_NAME
OPEN cTables

-- Loop each table
FETCH NEXT FROM cTables INTO @Schema, @Table, @SchemaTable
WHILE (@@FETCH_STATUS <> -1)
BEGIN
	IF (@@FETCH_STATUS <> -2)
	BEGIN
		-- Used to hold the column SQL that gets built for the CREATE statement
		DECLARE @ColumnSQL varchar(MAX) = '', @TriggerColumnSQL varchar(MAX) = ''
		
		-- Get all columns for a table
		DECLARE @Column varchar(255), @Position int, @DataType varchar(255), @DataLength float
		DECLARE cColumns CURSOR FAST_FORWARD READ_ONLY
		FOR
			SELECT COLUMN_NAME, ORDINAL_POSITION, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH
			FROM INFORMATION_SCHEMA.COLUMNS
			WHERE TABLE_SCHEMA = @Schema
			AND TABLE_NAME = @Table
			ORDER BY ORDINAL_POSITION
		OPEN cColumns
		
		-- Loop each column
		FETCH NEXT FROM cColumns INTO @Column, @Position, @DataType, @DataLength
		WHILE (@@FETCH_STATUS <> -1)
		BEGIN
			IF (@@FETCH_STATUS <> -2)
			BEGIN
				-- Set up if it's the first column of a table
				IF @Position = 1
				BEGIN
					-- Compile the drop statments and execute
					SET @SQL = REPLACE(@SQL_DROP_AUDIT_TABLE, N'#TABLE_NAME#', @Table)
					PRINT @SQL
					--EXEC(@SQL)
					SET @SQL = REPLACE(REPLACE(@SQL_DROP_AUDIT_TRIGGER, N'#TABLE_NAME#', @Table), N'#SCHEMA_NAME#', @Schema)
					PRINT @SQL
					--EXEC(@SQL)
					
					-- Prepare the create statement (no columns in it at this point
					SET @SQL = REPLACE(@SQL_CREATE_AUDIT_TABLE, N'#TABLE_NAME#', @Table)
					--PRINT @SQL
					SET @SQLAuditTrigger = REPLACE(REPLACE(REPLACE(@SQL_CREATE_AUDIT_TRIGGER, N'#TABLE_NAME#', @Table), N'#SCHEMA_NAME#', @Schema), N'#ID_COLUMN#', @Column)
					--PRINT @SQLAuditTrigger
				END
				
				-- Build in the columns
				SET @TriggerColumnSQL = @TriggerColumnSQL + ' [' + @Column + '],'
				SET @ColumnSQL = @ColumnSQL + '[' + @Column + '] '
				SET @ColumnSQL = @ColumnSQL + 
					CASE
						WHEN @DataType = 'varchar' OR @DataType = 'nvarchar' OR @DataType = 'char' OR @DataType = 'nchar' THEN
							CASE
								WHEN (@DataType = 'varchar' OR @DataType = 'nvarchar') AND @DataLength = -1 THEN
									@DataType + '(MAX)'
								ELSE
									@DataType + '(' + CAST(@DataLength AS varchar(MAX)) + ')'
							END
						ELSE
							@DataType
					END
				SET @ColumnSQL = @ColumnSQL + ' NULL, '
				
				-- If it's the last column then compile and execute the CREATE statements
				IF (SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @Schema AND TABLE_NAME = @Table) = @Position
				BEGIN
					SET @SQL = REPLACE(@SQL, N'#COLUMNS#', @ColumnSQL)
					PRINT @SQL
					--EXEC(@SQL)
					
					SET @SQL = REPLACE(@SQLAuditTrigger, N'#COLUMNS#', SUBSTRING(@TriggerColumnSQL, 1, LEN(@TriggerColumnSQL) - 1))
					PRINT @SQL
					--EXEC(@SQL)
				END
			END
		
			FETCH NEXT FROM cColumns INTO @Column, @Position, @DataType, @DataLength
		END
	END

	 -- Tidy columns
	CLOSE cColumns
	DEALLOCATE cColumns
	
	FETCH NEXT FROM cTables INTO @Schema, @Table, @SchemaTable
END

 -- Tidy tables
CLOSE cTables
DEALLOCATE cTables

You'll see I'm using schemas - obviously if you are not, then change it ;) It assumes that the first column in the table is the unique identifier to the row.

Wednesday, 19 December 2012

Insert Into a Table With Only One Identity Column

If you have a table that as only one column and that column is an identity column, then in order to INSERT into it, you need to use this statement:

INSERT INTO [MyTable] DEFAULT VALUES

This works in SQL Server 2008 and I haven't tested in earlier versions.

In SQL Server 2012, we have the introduction of a SEQUENCE. You could consider using one of those as an alternative.

Tuesday, 4 December 2012

A Project With the Name ScriptComponent Already Exists

Rather annoyingly I recently had A project with the name 'ScriptComponent' already exists error when opening up a script component in an SSIS package. After some extensive searching I only found 1 article of any use, which didn't solve my problem.

This is the pop-up I was getting:


How did it happen?
This is truly stunning! Everything I found was related to installing SQL Server Service Pack 2. Not me! I copied a data flow task (containing a script component) from one dtsx in the solution and pasted it to another dtsx in the same solution. I then deleted the data flow task from the "old" dtsx file. Each time opened the "new" script component or in fact any script component in the solution (or any other solution I open), I got the error - brilliant!

Things I also tried...
I tried all these with no success:
  1. Whatever was suggested in the above mentioned article.
  2. Un-installed Integration Services and re-installed (latest service pack applied).
  3. Un-installed and then re-installed Visual Studio 2005 and SQL Server 2005 (latest service packs applied (including the SP1 update for Windows Vista)).
What happened in the end?
I gave up and after spending 2 days trying to fix it, I re-installed Windows!

Moral of this story:
DON'T COPY AND PASTE A SCRIPT COMPONENT IN AN SSIS PACKAGE

Tuesday, 20 November 2012

MSSQL: Date Last Data Change Occurred on a Table

I needed to find out when the data was changed on a table in SQL Server. Unfortunately, I don't have the article I found it on in the mighty Stackoverflow, but here it is:

SELECT TABLENAME, LASTUPDATED
FROM (
        SELECT B.NAME AS 'TABLENAME',
               MAX(STATS_DATE (ID,INDID)) AS LASTUPDATED
        FROM SYS.SYSINDEXES AS A
        INNER JOIN SYS.OBJECTS AS B ON
                A.ID = B.OBJECT_ID
        WHERE B.TYPE = 'U'
        AND STATS_DATE (ID, INDID) IS NOT NULL 
        GROUP BY B.NAME
) AS A
ORDER BY LASTUPDATED DESC

A really useful bit SQL!

Friday, 20 April 2012

How To MD5 in SQL Server / MSSQL

If you need to MD5 a string in MSSQL then use the HASHBYTES function. You can also use other formats too, such as SHA and SHA1. It's as simple as:

PRINT HASHBYTES('MD5', 'MY_STRING')


If you want to return back a string rather than the varbinary HASHBYTES does return, then try the following:


PRINT SUBSTRING(master.dbo.fn_varbintohexstr( HASHBYTES ('MD5', ' MY_STRING ')), 3, 32)

Tuesday, 4 October 2011

How To Reset an Identity Column in MSSQL / SQL Server

Something I often find myself needing to do is reset the identity column on auto incrementing field in MS SQL / SQL Server.

There are a couple of ways of doing it:
  1. If you are emptying the table, instead of using a DELETE, use a TRUNCATE. That will automatically reset the identity.
  2. The ID can be set to a specific number by using DBCC CHECKIDENT([TABLE_NAME], RESEED, n). Where n is the number that is to be set.
If you already have data in the table and you want to make sure that the sequence is followed, then use this:
DBCC CHECKIDENT([TABLE_NAME], RESEED, 0)
DBCC CHECKIDENT([TABLE_NAME], RESEED)

Wednesday, 22 June 2011

Auto Trimming - Whitespace Removal in SQL Server/MSSQL

I got really angry about this one yesterday as I can't see one possible reason why this "feature" would be required.

If you run this script, the whitespace padded string is also returned along with the unpadded string.

CREATE TABLE #t ([col1] varchar(100))

INSERT INTO #t ([col1]) VALUES ('hello')
INSERT INTO #t ([col1]) VALUES ('hello     ')

SELECT '''' + [col1] + '''', LEN([col1]), DATALENGTH([col1])
FROM #t

SELECT * FROM #t WHERE [col1] = 'hello'
SELECT * FROM #t WHERE [col1] = 'hello     '

DROP TABLE #t


How can this possibly be right? If I want 'hello     ' to be returned, then return 'hello     ' not 'hello'. These are two very different strings!

Peoples arguments are why would want to save that and other such silly statements. My philosophy is, if I can think of it, it will happen! I can think of many cases where it would be beneficial to have the padding - dealing with old systems or external systems over which control is not possible.

Now I know that SQL Server does this stupid thing, I can account for it - but seriously - why?? It's stupid!

Oh and ANSI_PADDING - makes no odds!

Tuesday, 14 June 2011

MSSQL Saving Change is not Permitted (Prevent)

This was a cheeky one I came across today. Where I was getting the following message from SQL Server 2008 in the table designer:
Saving change is not permitted. the changes you have made require the following tables to be dropped and re-created. You have either made changes to a table that cant be re-created or enabled the option Prevent Saving changes that require the table to be re-created.
The solution is to change the setting by using the menu: Tools > Options > Designers, then unchecking the "Prevent saving changes that require table re-creation" box.


 Guessing it's not a problem and more like a feature. Little bit odd, but I can understand why...

Wednesday, 20 April 2011

Format of the initialization string does not conform to specification starting at index

When I recently moved a site from the dev location to the live location, I got presented with Format of the initialization string does not conform to specification starting at index when trying to make a database connection.


This obviously wasn't happening on the live and the connection string, which was in the Web.Config file, was correct. I was using SQL authentication and the password had both semi-colons (;) and ampersands (&) in it. I wasn't sure which character was causing the problem. As I was able to, I changed the password for the user (removing the ;&). All worked a treat then :)

Friday, 28 January 2011

MSSQL ISDATE - Is It?

Another is it type one from MSSQL, but this time ISDATE. SQLServerCentral.com article by Kameswari Aravindh very kindly makes us aware that ISDATE('1973') returns a true response. So long as the number is between 1753 and 9999, it's a valid year and therefore a valid date.

Bear it in mind!! ;)

Wednesday, 1 December 2010

MSSQL ISNUMERIC - Is it?

I didn't realise that ISNUMERIC('123 £') returns 1 - did you? If you didn't either then I'd suggest you read this article Why doesn't ISNUMERIC work correctly? by Jeff Moden on SQLServerCentral.com.

Taken from the article, the easiest test is to use a regular expression:

SELECT *
FROM mytable
WHERE mytable.mycolumn NOT LIKE '%[^0-9]%'

There is also a useful function he has written that could prove to be handy.

Thanks Jeff. Think I'd better re-trace some steps to make sure scripts using ISNUMERIC are a bit tighter!

Friday, 26 November 2010

MSSQL Reporting Services: Invalid Object Name on Temporary Table

I had an Invalid Object Name error when I was using a temporary table inside a stored procedure when I was reporting services. It ran fine in management studio but errored in the report designer wizard.

Fixed it by using the SET FMTONLY OFF command. Happy days :)

Labels

.net (7) ajax (1) android (7) apache (1) asp.net (3) asus (2) blogger (2) blogspot (3) c# (16) compact framework (2) cron (1) css (1) data (1) data recovery (2) dns (1) eclipse (1) encryption (1) excel (1) font (1) ftp (1) gmail (5) google (4) gopro (1) html (1) iis (3) internet explorer IE (1) iphone (1) javascript (3) kinect (1) linux (1) macro (1) mail (9) mercurial (1) microsoft (3) microsoft office (3) monitoring (1) mootools (1) ms access (1) mssql (13) mysql (2) open source (1) openvpn (1) pear (2) permissions (1) php (12) plesk (4) proxy (1) qr codes (1) rant (4) reflection (3) regex (1) replication (1) reporting services (5) security (2) signalr (1) sql (11) sqlce (1) sqlexpress (1) ssis (1) ssl (1) stuff (1) svn (2) syntax (1) tablet (2) telnet (3) tools (1) twitter (1) unix (3) vb script (3) vb.net (9) vba (1) visual studio (2) vpc (2) vpn (1) windows (4) woff (1) xbox 360 (1)