A base class which implements INotifyPropertyChanged « Josh Smith on WPF
Blog
-
Compare contents of two tables.
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_CompareTables]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[sp_CompareTables]
GO
CREATE PROCEDURE sp_CompareTables
@table1 sysname,
@table2 sysname,
@ColumnList varchar(8000) = '*',
@Summary bit = 0,
@Detail bit = 1,
@TableNameHeader varchar(30) = '[Table Name]',
@CounterHeader varchar(30) = '[Counter]',
@rc int = null out
AS
------------------------------------------------------------------------------
--Compare the contents of two tables
--@table1 and @table2 are the table names
--@ColumnList is the list of column names, by default all columns will be compared
--@Summary - set this to 1 it you want summary information
--@Detail - set this to 1 (the default) if you want the details of the differences
--@TableNameHeader - this is used as the column heading the a column that contains the table name
--@CounterHeader - this is used as the column heading the a column that contains a row count
--(this is used when there may be multiple identical rows
--@rc - the number of rows that are different
--Note: this SP can "live" in the master DB
------------------------------------------------------------------------------
DECLARE @err integer,
@SQL1 varchar(8000),
@SQL2 varchar(8000),
@SQL3 varchar(8000),
@SQL4 varchar(8000),
@SQLTop varchar(8000),
@SQLBot varchar(8000),
@SQLTop2 varchar(8000),
@SQLBot2 varchar(8000),
@ColumnGrouping varchar(8000),
@indexlist varchar(8000)
SET NOCOUNT ON
-- a bit of validation first
IF object_id(@table1) IS NULL
BEGIN
RAISERROR ('Table ''%s'' does not exist', 11, 1, @table1) WITH NOWAIT
RETURN
END
IF object_id(@table2) IS NULL
BEGIN
RAISERROR ('Table ''%s'' does not exist', 11, 1, @table2) WITH NOWAIT
RETURN
END
IF @table1 NOT LIKE '%.%'
BEGIN
SET @table1 = DB_NAME() + '..' + @table1
END
IF @table2 NOT LIKE '%.%'
BEGIN
SET @table2 = DB_NAME() + '..' + @table2
END
-- let's get a list of columns that have different collations ...
CREATE TABLE #CollationColumns (name sysname)
DECLARE @SQL varchar(8000)
set @SQL = 'INSERT #CollationColumns SELECT a.name
FROM %db1..syscolumns a
INNER JOIN %db2..syscolumns b
ON a.name = b.name COLLATE database_default
WHERE a.id = object_id(''%t1'')
AND b.id = object_id(''%t2'')
AND a.collationid b.collationid'
SET @SQL = replace(@SQL, '%db1', PARSENAME(@table1, 3))
SET @SQL = replace(@SQL, '%db2', PARSENAME(@table2, 3))
SET @SQL = replace(@SQL, '%t1', @table1)
SET @SQL = replace(@SQL, '%t2', @table2)
EXEC (@SQL)
-- If the first table is in the current database
-- we can look to see if there is a unique index
IF PARSENAME(@table1, 3) = DB_NAME()
BEGIN
SET @indexlist = NULL
SELECT @indexlist = COALESCE(@indexlist + ', ', '') + '[' + c.name + ']'
FROM syscolumns c
INNER JOIN sysindexkeys k ON c.colid = k.colid
AND k.id = OBJECT_ID(@table1)
AND c.id = k.id
INNER JOIN sysindexes i ON i.id = k.id
AND i.indid = k.indid
WHERE i.indid = ( SELECT TOP 1 i.indid -- get the first unique index (favouring the clustered one)
FROM sysindexes
WHERE id = OBJECT_ID(@table1)
AND INDEXPROPERTY(i.id,i.name, 'IsUnique') = 1
AND INDEXPROPERTY(i.id,i.name, 'IsStatistics') = 0
ORDER BY INDEXPROPERTY(i.id,i.name, 'IsClustered') DESC)
ORDER BY k.keyno
END
IF @indexlist IS NULL AND @Summary = 1
BEGIN
RAISERROR ('Summary is not availabe as table ''%s'' has no unique index', 10, 1, @table1) WITH NOWAIT
SET @Summary = 0
END
-- Let's generate a list of columns ...
IF @ColumnList = '*' or @ColumnList = ''
BEGIN
-- we can only do this in the current database (this could be enhanced though)
IF PARSENAME(@table1, 3) DB_NAME()
BEGIN
RAISERROR ('Table ''%s'' is not in the current database', 11, 1, @table1) WITH NOWAIT
RETURN
END
SET @ColumnList = NULL
SET @ColumnGrouping = NULL
-- get a comma delimited list
-- convert text and netxt to varchar(8000)
-- convert image to binary
SELECT @ColumnList = COALESCE(@ColumnList + ', ', '') + CASE
WHEN systypes.name IN ( 'text' ,'ntext')
THEN 'CONVERT(VARCHAR(8000),' + '[' + syscolumns.name + ']' + ') AS '
WHEN systypes.name ='image'
THEN 'CONVERT(BINARY,' + '[' + syscolumns.name + ']' + ') AS '
ELSE ''
END
+ '[' + syscolumns.name + ']'
-- if the column has different collation the just use a database default
+ CASE WHEN syscolumns.name IN (SELECT name COLLATE database_default FROM #CollationColumns)
THEN ' collate database_default AS [' + syscolumns.name + ']'
ELSE ''
END
FROM syscolumns INNER JOIN systypes ON syscolumns.xusertype = systypes.xusertype
WHERE ID = object_id(@table1)
ORDER BY colid
-- we need a similar list of columns for the GROUP BY clause
SELECT @ColumnGrouping = COALESCE(@ColumnGrouping + ', ', '') + CASE
WHEN systypes.name in ( 'text' ,'ntext')
THEN 'convert(varchar(8000),' + '[' + syscolumns.name + ']' + ')'
WHEN systypes.name = 'image'
THEN 'convert(binary,' + '[' + syscolumns.name + ']' + ')'
ELSE '[' + syscolumns.name + ']'
END
FROM syscolumns
INNER JOIN systypes on syscolumns.xusertype = systypes.xusertype
WHERE id = object_id(@table1)
ORDER BY colid
END
ELSE
BEGIN
SET @ColumnGrouping = @ColumnList
END
DECLARE @Counter1 varchar(8000)
DECLARE @Counter2 varchar(8000)
-- if we have a unique index then we don't need a row count.
IF @indexlist IS NULL
BEGIN
SET @Counter1 = 'COUNT (*) AS ' + @CounterHeader + ', '
SET @Counter2 = @CounterHeader + ', '
END
ELSE
BEGIN
SET @Counter1 = ''
SET @Counter2 = ''
END
-- here's the wizzy SQL (I will explain it sometime)
SET @SQL1 = 'SELECT ' + @Counter1 + '''' + @table1 + ''' AS ' + @TableNameHeader + ', ' + @ColumnList + ' FROM ' + @Table1 + char(13)
SET @SQL2 = ' GROUP BY ' + @ColumnGrouping + char(13)
SET @SQL3 = ' UNION ALL SELECT ' + @Counter1 + '''' + @table2 + ''' AS ' + @TableNameHeader + ', ' + @ColumnList + ' FROM ' + @Table2 + char(13)
SET @SQL4 = ' GROUP BY ' + @ColumnGrouping + char(13)
SET @SQLTop = 'SELECT ' + @Counter2 + ' Max(' + @TableNameHeader + ') AS ' + @TableNameHeader + ', ' + @ColumnList + ' FROM (' + char(13)
SET @SQLBot = ') A ' + CHAR(13) + ' GROUP BY ' + @Counter2 + @ColumnGrouping + CHAR(13) + ' HAVING COUNT(*) 2'
SET @SQLTop2 = 'SELECT ' + @indexlist +
' , CASE COUNT(*) when 2 ' +
' THEN ''Different'' ' +
' ELSE ''Only in '' + max(' + @TableNameHeader + ') ' +
' END AS [Details] ' +
' FROM ( '
SET @SQLBot2 = ') B GROUP BY ' + @indexlist
-- now output the details
IF @Detail = 1
BEGIN
EXEC (@SQLTop + @SQL1 + @SQL2 + @SQL3 + @SQL4 + @SQLBot)
SET @rc = @@ROWCOUNT
END
IF @Summary = 1
BEGIN
EXEC (@SQLTop2 + @SQLTop + @SQL1 + @SQL2 + @SQL3 + @SQL4 + @SQLBot + @SQLBot2)
SET @rc = @@ROWCOUNT
END
IF @rc = 0
BEGIN
RAISERROR ('Tables ''%s'' and ''%s'' are the same', 10, 1, @table1, @table2) WITH NOWAIT
END
ELSE
BEGIN
RAISERROR ('Tables ''%s'' and ''%s'' contain %d ''different'' rows', 10, 1, @table1, @table2, @rc) WITH NOWAIT
END
SET NOCOUNT OFF
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO -
Compare 2 database tables
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO
IF EXISTS (select * from dbo.sysobjects where id = object_id(N'[dbo].[sp_CompareDBTables]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[sp_CompareDBTables]
GO
CREATE PROC sp_CompareDBTables
@targetDB sysname,
@Summary bit = 0,
@Detail bit = 1
--****************************************
--@targetDB: we'll compare the current database with this one
--@Summary: do we want summary output?
--@Detail: do we want detail output?
--calls sp_CompareTables for each table in the current table
--Note: this SP can "live" in the master DB
--*****************************************/
AS
IF DB_ID(@targetDB) IS NULL
BEGIN
RAISERROR('The target database cannot be found.', 10, 1) WITH NOWAIT
END
DECLARE @SQL varchar(4000)
SET @SQL = 'exec sp_CompareTables ''?'', ''' + @targetDB + '.?'', @Summary = ' + convert(varchar(1) ,@Summary) + ', @Detail = ' + convert(varchar(1) ,@Detail)
EXEC sp_msforeachtable @SQL
GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO -
Finding missing numbers in a sequence
From http://www.sqlservercentral.com/articles/Datetime+Manipulation/61822/
Find Missing Numbers
It is obvious that, to find missing numbers, we need to have a table or resultset which contains all the numbers. Then we can apply a NOT IN clause and identify the missing numbers.
If you are wondering, why I needed missing numbers, here is the reason. In one of my applications, there is an entity that we call coordinator. Each coordinator has a 4 digit numeric code. The code starts at 1000 and ends at 9999. (There is no chance that we will have more than 9000 coordinators at any point of time, so a 4 digit code is sufficient). When the user creates a new coordinator, the system automatically generates a new coordinator number. However, the user can still edit it. The system will allow the user to edit the coordinator number (during a new entry) as long as it does not produce a duplicate code.
With the above functionality, we needed a way to reuse un-used/missing numbers. When we create a new coordinator, we need to find the lowest unused coordinator code. For this purpose we needed a way to identify the missing numbers.
As mentioned earlier in this article, I did not want to keep a temp table. I want to generate the numbers on the fly. There are several ways to do this. I like to use a CTE to do this.
In one of my previous articles, I had presented a way to generate a sequence of numbers by using a CTE. It was fast. But a faster code fragment was posted by Kathi Kellenberger in the discussion forum. It was taken from Itzik Ben-Gan’s book “Inside Microsoft SQL Server 2005 T-SQL Querying”. Here is the code fragment which generates 1 million records in the fraction of a second. [code]
WITH
L0 AS (SELECT 1 AS C UNION ALL SELECT 1), --2 rows
L1 AS (SELECT 1 AS C FROM L0 AS A, L0 AS B),--4 rows
L2 AS (SELECT 1 AS C FROM L1 AS A, L1 AS B),--16 rows
L3 AS (SELECT 1 AS C FROM L2 AS A, L2 AS B),--256 rows
L4 AS (SELECT 1 AS C FROM L3 AS A, L3 AS B),--65536 rows
L5 AS (SELECT 1 AS C FROM L4 AS A, L4 AS B),--4294967296 rows
num AS (SELECT ROW_NUMBER() OVER(ORDER BY C) AS N FROM L5)
SELECT N FROM NUM WHERE N <= 1000000;This code is VERY VERY fast and I find it to be the best candidate for generating a sequence of numbers on the fly. I wanted to re-use this code and hence I created a function that takes a Minimum and Maximum value and returns a resultset containing sequence numbers within the given range. [code]
CREATE FUNCTION dbo.GetNumbers
(
@Start BIGINT,
@End BIGINT
)
RETURNS @ret TABLE(Number BIGINT)
AS
BEGIN
WITH
L0 AS (SELECT 1 AS C UNION ALL SELECT 1), --2 rows
L1 AS (SELECT 1 AS C FROM L0 AS A, L0 AS B),--4 rows
L2 AS (SELECT 1 AS C FROM L1 AS A, L1 AS B),--16 rows
L3 AS (SELECT 1 AS C FROM L2 AS A, L2 AS B),--256 rows
L4 AS (SELECT 1 AS C FROM L3 AS A, L3 AS B),--65536 rows
L5 AS (SELECT 1 AS C FROM L4 AS A, L4 AS B),--4294967296 rows
num AS (SELECT ROW_NUMBER() OVER(ORDER BY C) AS N FROM L5)
INSERT INTO @ret(Number)
SELECT N FROM NUM WHERE N BETWEEN @Start AND @End
RETURN
ENDOnce we have the function to generate the sequence number, we can easily write the code to identify the missing numbers. [code]
1 SELECT MIN(Number)
2 FROM dbo.GetNumbers(1000, 9999)
3 WHERE Number NOT IN (SELECT CoordinatorID FROM Coordinators)
Find Missing Dates
I guess the requirement to find missing dates must be more common than the missing number requirement I mentioned above. To find the missing dates (Dates at which no sales took place, Dates at which a given employ was absent etc) we need to have a table or resultset which contains all the dates within a given range.
The following example shows how to generate a sequence of dates using the same logic we discussed earlier. [code]
--Generate a sequence of all the dates for the month of October, 2007
SELECT
CAST('2007-10-01' AS DATETIME) + Number-1
FROM dbo.GetNumbers(1, 30)--Generate a sequence of all the dates for year 2007
SELECT
CAST('2007-01-01' AS DATETIME) + Number-1
FROM dbo.GetNumbers(1, 365)Once we have a sequence of dates, we can easily apply a NOT IN clause and find the missing dates based on the specific requirement.
-
Are you sitting comfortably?
…Then we shall begin…
Blogs are brilliant. There are a ton of brilliant contributors out there that make learning new technologies so simple. So i guess that it’s high time that I stop leeching so much information and start giving something back too.. I don’t expect too much interaction, this is more about logging my thoughts and findings, and if someone else can benefit from that, then great!
So this blog is going to be about anything I wan (it’s mine after all)… expect information on .NET coding, Silverlight, WCF, ASP.NET (MVC, jQuery, Dynamic Data etc), Photography and so on.
..the name… well it appeared to be one of only a few that remained..damn bloggers…
-
Debugging Replication Errors
use distribution
--------------------------------------------------------------------------------
select top 100 e.*, * from MSrepl_commands c inner join MSrepl_errors e on c.xact_seqno =e.xact_seqno order by e.id desc
--------------------------------------------------------------------------------
exec sp_replmonitorhelpsubscription @publisher=NULL, @publication_type=0
--------------------------------------------------------------------------------
exec sp_helpdistpublisher
--------------------------------------------------------------------------------
exec distribution..sp_replmonitorsubscriptionpendingcmds @publisher=pubservername, @subscriber=subservername, @publisher_db='pubdbname', @subscriber_db='subdbname', @publication='pubname', @subscription_type=0
--------------------------------------------------------------------------------
exec sp_replqueuemonitor @publisher = pubservername, @publisherdb = 'pubdbname', @publication = 'pubname', @tranid = NULL, @queuetype = 0
--------------------------------------------------------------------------------
exec sp_replshowcmds -
Dropping Replication
when struggling to drop a publication or subscription from the API, try one of these:
use [SysTest_1]
exec sp_dropsubscription @ignore_distributor =1,@publication = N'all', @subscriber=N'all',@article = N'all'
exec sp_droppublication @publication = N'all',@ignore_distributor =1
exec sp_removedbreplication 'systest_1' -
Trusted Logins
Trusted logins with IIS and IE6.
Tools | Internet Options | Security Tab | Intranet Zone | Security Levels | Custom Level
Scroll to the bottom of the list and make sure that User Authentication | Logon is set appropriately.
-
Moving VSTS Load Test Results Databases
Email from James … :
Here is the full set of steps that you need to go through to view them. The basic problem is that the connection string to the load test repository is hardcoded into the trx files.
- Restore the results database to your local SQL Server 2005 instance. In theory SQL Server Express (included with the trial DVD that I gave to Andrew) should do the job, but thinking about it you might find a problem restoring due to the file size limitation (depending on what the file sizes were set to on the results server). In this case you will need to get hold of a copy of SQL Server 2005 Standard Edition.
- Create a dummy load test in Visual Studio 2005 (this can just be an empty unit test added to a load test).
- From the ‘Test’ menu, select the ‘Administer Test Controllers’ option. Under the ‘Load Test Results Store’ section, enter the correct connection details for your SQL Server instance.
- Run the dummy load test for a few seconds. Find the resulting trx file and open it with notepad. Search for the tag and copy the value (e.g. LAs4ogoKlDc7tK1quR7UVHrc5d598CfIS8AxIDzjz9YaPiknRPEua16x0ZFwpUM6s5UYUAzzrmtpTf26WVVVFVWiAL1CMaOP).
- In each of the trx files that you took away from the lab, find the and replace the value with that copied in step 4.
- Open the trx file in Visual Studio and right click on the entry in the Test Results window and select ‘View Test Results Database’. The graphs should now be populated from the database.
The extraction of the resultsRepositoryConnectString value must be done when the database being referenced is moved, or the name of the database is changed.
-
Moving VSTS Load Test Results Databases
Email from MS:
Here is the full set of steps that you need to go through to view them. The basic problem is that the connection string to the load test repository is hardcoded into the trx files.
1. Restore the results database to your local SQL Server 2005 instance. In theory SQL Server Express (included with the trial DVD that I gave to Andrew) should do the job, but thinking about it you might find a problem restoring due to the file size limitation (depending on what the file sizes were set to on the results server). In this case you will need to get hold of a copy of SQL Server 2005 Standard Edition.
2. Create a dummy load test in Visual Studio 2005 (this can just be an empty unit test added to a load test).
3. From the ‘Test’ menu, select the ‘Administer Test Controllers’ option. Under the ‘Load Test Results Store’ section, enter the correct connection details for your SQL Server instance.
4. Run the dummy load test for a few seconds. Find the resulting trx file and open it with notepad. Search for the tag and copy the value (e.g. LAs4ogoKasfgad5T354G25Ga7UVHrc5d598CfIS8xxxxFQW4R34xknRPEua16x0ZFwpUM6s5UYUAzzrmtpTf26Wxxx54Q4QRF).
5. In each of the trx files that you took away from the lab, find the and replace the value with that copied in step 4.
6. Open the trx file in Visual Studio and right click on the entry in the Test Results window and select ‘View Test Results Database’. The graphs should now be populated from the database.
The extraction of the resultsRepositoryConnectString value must be done when the database being referenced is moved, or the name of the database is changed.