Category Archives: Scripts

This section will store simple scripts

SQL Version

Basic Server Info to obtain SQL Version and OS Version.


/*
Basic Info
*/

SELECT
SERVERPROPERTY('ServerName')	 'SQLServerName',
COALESCE(SERVERPROPERTY('InstanceName'),'DEFAULT') 'InstanceName',
SERVERPROPERTY('ProductVersion') 'ProductVersion',
SERVERPROPERTY('ProductLevel')   'ProductLevel',
SERVERPROPERTY('Edition')		 'Edition',
CASE 
WHEN RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 6.2 then 'Windows 2012'
WHEN RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 6.1 then 'Windows 2008R2'
WHEN RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 6.0 then 'Windows 2008'
WHEN RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 5.2 then 'Windows 2003'
WHEN RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 5.1 then 'Windows XP'
WHEN RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 5.0 then 'Windows 2000'

ELSE RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)
END	'Windows Version'

Example

SQLVersion

Shrink a Data File

Normally, you would never want to shrink data files. Not even in case of emergency when running out of space. If you were to shrink the data file, the database would simply grow again and you end up with the same problem. If the database grew, it is because the database needed the space. If it is partially empty, it is because some data was removed, however, it is very likely that it would be needed again so it is a bad idea to shrink it again. If there was an error that caused the database to grow, or there was a change in process, or something extremely rare happened, you might need to shrink a database file. Since shrinking a database will hit performance and possible introduce some locks on the tables, if you must do it, it is better to do it little by little, using a script similar to this below. Remember, after the shrink is done, your indexes will be fragmented, so it is recommended to rebuild all your indexes (and hence the DB will grow a little)

In order to use this script, you should first find if there is space available on the data files, and find the logical name of the data file to shrink.

To do that, run this script first http://www.cookingsql.com/2016/01/data-file-size-and-free-space-one-database/ and use its output to run the script below.

 

/*
DESCRIPTION   Shrink a single DATA file, in increments.
It will loop to execute the DBCC SHRINKFILE command to shrink the database file by the desired increment
until it reaches the target free space.


CONFIGURATION    TOP.
Run this script in the database with the file to be shrunk.
1.  Make sure to set the USE statement for the correct database
2. Set @DBFileName  Enter the Logical File name (not the physical name)
3. Set @TargetFreeMB to the desired file free space in MB after shrink.
4. Set @ShrinkIncrementMB to the increment to shrink file by in MB

 

Compatibility list:
MSSQL2005
MSSQL2008/SQL2008R2
MSSQL2012
MSSQL2014 
 
*/
SET NOCOUNT ON


declare @DBFileName sysname
declare @TargetFreeMB int
declare @ShrinkIncrementMB int

/*------ CONFIGURATION ---------*/

--USE DATABASE 

-- Set Name of Database file to shrink
set @DBFileName = 'AdventureWorks2012_Data2'

-- Set Desired file free space in MB after shrink
-- Remember, it is not the target file size but how much empty space will be left after the shrink operation
-- @SizeMB = @TargetFreeMB + @usedSpace
set @TargetFreeMB = 1300

-- Set Increment to shrink file by in MB
-- Make sure the @TargetFreeMB + @ShrinkIncrement is bigger than @EmptyDB
set @ShrinkIncrementMB = 300

/*------ END CONFIGURATION ---------*/

-- Declare variables
declare @sql varchar(8000)
declare @SizeMB int
declare @EmptyMB int
declare @UsedMB int

-- Check Input Values

IF NOT EXISTS (select name from sysfiles where name = @DBFileName)
begin
print 'The Data File does not exist on the current database. You either did not set
the USE statement or did not specify the Logical File name correctly
Terminating Batch'
GOTO EOL
end

select
@SizeMB = CAST(size/128.0 AS int)
,@UsedMB = CAST(FILEPROPERTY(name, 'SpaceUsed' ) AS int)/128.0
,@EmptyMB = @SizeMB - @UsedMB
 
FROM sys.database_files
where name = @DBFileName



IF @EmptyMB < @TargetFreeMB
BEGIN
print 'Cannot proceed with operation because the target free space: ' +cast(@TargetFreeMB as nvarchar(10)) +'MB (user entry)
is bigger than the current free space: '+cast(@EmptyMB as nvarchar(10))+'MB
Terminating Batch'
GOTO EOL
END

IF @EmptyMB < @TargetFreeMB+@ShrinkIncrementMB
BEGIN
print 'Cannot proceed with operation because the Shrink Increment: ' +cast(@ShrinkIncrementMB as nvarchar(10)) +'MB (user entry)
Plus the Target Free '+cast(@TargetFreeMB as nvarchar(10))+'MB is bigger than the empty Space '+cast(@EmptyMB as nvarchar(10))+'MB
Terminating Batch'
GOTO EOL
END

 
declare @loopExit int
set @loopExit =  0


-- Loop until file at desired size
while  (@SizeMB > @UsedMB+@TargetFreeMB+@ShrinkIncrementMB) and (@loopExit < 10)
	begin

		set @sql =
		'dbcc shrinkfile ( '+@DBFileName+', '+convert(varchar(20),@SizeMB-@ShrinkIncrementMB)+' ) WITH NO_INFOMSGS'

		print 'Start ' + @sql + ' at '+convert(varchar(30),getdate(),121)
		exec ( @sql )

		print 'Done at '+convert(varchar(30),getdate(),121)

		-- Sets again Original Values
		select
		@SizeMB = CAST(size/128.0 AS int)
		,@UsedMB = CAST(FILEPROPERTY(name, 'SpaceUsed' ) AS int)/128.0
		,@EmptyMB = @SizeMB - @UsedMB
		,@loopExit = @loopExit+1
		FROM sys.database_files
		where name = @DBFileName

	end
------------------------------------

IF (@loopExit = 10)
BEGIN
PRINT 'The SHRINK FILE operation completed 10 loops
This could mean that the file is not shrinking
or you need to increase the "shrink increment value"
Proceed with CAUTION
'
GOTO EOL
END

-- Show Size, Space Used, Unused Space, and Name of all database files


SELECT   
b.name
, b.type_desc
, CAST(b.size/128.0 AS int) 'SizeMB'  
, CAST(b.size/128.0 - CAST(FILEPROPERTY(b.name, 'SpaceUsed' ) AS int)/128.0 AS int) 'SpaceUsedMB'  
, ceiling(b.size/128.0 -CAST(FILEPROPERTY(b.name,'SpaceUsed' ) AS int)/128.0) 'FreeSpaceMB'
, CAST(ceiling(100 * (b.size/128.0 -CAST(FILEPROPERTY(b.name,'SpaceUsed' ) AS int)/128.0)/(b.size/128.0)) AS varchar(8)) + '%'  'FreeSpace'

,  CASE
	WHEN b.is_percent_growth = 0 THEN LTRIM(STR(b.growth * 8.0 / 1024,10,1)) + ' MB, '  
    ELSE 'By ' + CAST(b.growth AS VARCHAR) + ' percent, '
 END
 + CASE  
    WHEN b.max_size = -1 THEN 'unrestricted growth'
	ELSE 'restricted growth to ' + LTRIM(STR(b.max_size * 8.0 / 1024,10,1)) + ' MB'  
    END AS Autogrow
, cast(b.max_size/128.0 as int) 'MaxSize'
, physical_name
 
FROM sys.database_files b



EOL:


 

Data File Size and Free Space – One Database

This script lists file size and how full the data files are for a single Database.

 

/*
DESCRIPTION    List space on a single database and location of its files

CONFIGURATION    Top. Modify the "use" statement
 
Compatibility list:
MSSQL2005
MSSQL2008
 
 
*/


USE Adventure_Works_2012

SELECT  
b.name
, b.type_desc
, CAST(b.size/128.0 AS int) 'SizeMB'  
, CAST(b.size/128.0 - CAST(FILEPROPERTY(b.name, 'SpaceUsed' ) AS int)/128.0 AS int) 'SpaceUsedMB'  
, CAST(ceiling(100 * (b.size/128.0 -CAST(FILEPROPERTY(b.name,'SpaceUsed' ) AS int)/128.0)/(b.size/128.0)) AS varchar(8)) + '%'  'FreeSpace'

,  CASE
	WHEN b.is_percent_growth = 0 THEN LTRIM(STR(b.growth * 8.0 / 1024,10,1)) + ' MB, '  
    ELSE 'By ' + CAST(b.growth AS VARCHAR) + ' percent, '
 END
 + CASE  
    WHEN b.max_size = -1 THEN 'unrestricted growth'
	ELSE 'restricted growth to ' + LTRIM(STR(b.max_size * 8.0 / 1024,10,1)) + ' MB'  
    END AS Autogrow
, cast(b.max_size/128.0 as int) 'MaxSize'
, physical_name
 
FROM sys.database_files b




Database Size Totals

Very simple script that list the total size of a database and its transaction log.

If you need for SQL 2000 click here

 



/*
DESCRIPTION    List space used on databases
 
CONFIGURATION    none

 
Compatibility list:
MSSQL2012
MSSQL2008
MSSQL2005

DOES NOT WORK
MSSQL2000

 
*/


SELECT
    SERVERPROPERTY('SERVERNAME') ServerName,
    SERVERPROPERTY('IsClustered') IsCluster,
    SERVERPROPERTY('ProductVersion') AS ProductVersion,
    case 
when RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 6.3 then 'Windows 2012R2'
when RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 6.2 then 'Windows 2012'
when RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 6.1 then 'Windows 2008R2'
when RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 6.0 then 'Windows 2008'
when RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 5.2 then 'Windows 2003'
when RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 5.1 then 'Windows XP'
when RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 5.0 then 'Windows 2000'

else RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)
end,
    DB_NAME(db.database_id) DatabaseName,
    (CAST(mfrows.RowSize AS FLOAT)*8)/1024 RowSizeMB,
    (CAST(mflog.LogSize AS FLOAT)*8)/1024 LogSizeMB
FROM sys.databases db
    LEFT JOIN (
    SELECT database_id, SUM(size) RowSize 
    FROM sys.master_files 
    WHERE type = 0 
    GROUP BY database_id, type
				) mfrows ON mfrows.database_id = db.database_id
    
    LEFT JOIN (
    SELECT database_id, SUM(size) LogSize 
    FROM sys.master_files 
    WHERE type = 1 
    GROUP BY database_id, type
				) mflog ON mflog.database_id = db.database_id
WHERE
	db.database_id > 4

 

  For SQL 2000


/*
DESCRIPTION    List space used on databases
 
CONFIGURATION    none

 
Compatibility list:
MSSQL2000

 
*/

SELECT
    SERVERPROPERTY('SERVERNAME') ServerName,
    SERVERPROPERTY('IsClustered') IsCluster,
    SERVERPROPERTY('ProductVersion') AS ProductVersion,
    case 
when RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 6.3 then 'Windows 2012R2'
when RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 6.2 then 'Windows 2012'
when RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 6.1 then 'Windows 2008R2'
when RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 6.0 then 'Windows 2008'
when RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 5.2 then 'Windows 2003'
when RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 5.1 then 'Windows XP'
when RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)= 5.0 then 'Windows 2000'

else RIGHT(SUBSTRING(@@VERSION, CHARINDEX('Windows NT', @@VERSION), 14), 3)
end,
    DB_NAME(db.dbid) DatabaseName,
    (CAST(mfrows.RowSize AS FLOAT)*8)/1024 RowSizeMB,
    (CAST(mflog.LogSize AS FLOAT)*8)/1024 LogSizeMB
FROM sysdatabases db
    LEFT JOIN (
    SELECT dbid, SUM(size) RowSize 
    FROM master..sysaltfiles
    WHERE groupID = 1 
    GROUP BY dbid, groupID
				) mfrows ON mfrows.dbid = db.dbid
    
    LEFT JOIN (
    SELECT dbid, SUM(size) LogSize 
    FROM master..sysaltfiles
    WHERE groupID <> 1
    GROUP BY dbid, groupID
				) mflog ON mflog.dbid = db.dbid
WHERE
	db.dbid > 4

Find If Mix Backup Types are Present

There are mainly two types of backups:

  • Native SQL backups (either stored to local disk or a network location)
  • API backups, where software such as TSM/TDP or NetBackup, or CommVault, SQLLite, etc

There could be problems recovering a database if a server has different types of backups configured, unbeknown to the DBA. Per example, if TDP is used, and another DBA takes a local disk backup (out of band backup), it could pose a problem when we are restoring Tlog backups. This script identifies this situation, and lists those databases where different types of backups have been performed. If none are found, the result is blank

 


/*
DESCRIPTION
Queries the location of the last X days of databases backups
and reviews its location: API, DiskBackup or NetworkLocation

If backup location has changed in the last X days,
it returns those databases
If no change in location n the last X days,
it returns empty.

CONFIGURATION Set @days for the number of days to search for backups
Set @dbname for the specific database; leave NULL for all
 
Compatibility list:
MSSQL2012 MSSQL2005 MSSQL2008 MSSQL2000  
*/

 
DECLARE @days INT
DECLARE @dbname sysname
 
SET @days = 7			-- Number of days. Limited by the history stored on MSDB
SET @dbname = NULL		-- Specify database like 'database1' . If Null it will do all
 
 
 
 ---- End of configuration
 
declare @BackupLocation table
(i int identity(1,1) ,db varchar(1000) , bk_type varchar(10), bk_Loc varchar(3))

insert into @BackupLocation 

SELECT
 bs.database_name
, CASE bs.type
WHEN 'D' THEN 'Full'
WHEN 'I' THEN 'Diff'
WHEN 'L' THEN 'TLog'
END AS backup_type
, left(bmf.physical_device_name,3)
 
FROM msdb.dbo.backupmediafamily bmf
INNER JOIN msdb.dbo.backupset bs ON bmf.media_set_id = bs.media_set_id
WHERE 1=1
and bmf.family_sequence_number = '1'
and (CONVERT(datetime, bs.backup_start_date, 102) >= GETDATE() - @days)
--and bs.type = 'D'
and bs.database_name like coalesce(@dbname,'%')


ORDER BY
bs.database_name

--------------------------
declare @summary table

(i int identity(1,1) ,db varchar(1000), db2 varchar(1000) )

insert into @summary

select db, db+'_'+bk_type
 from @BackupLocation 

group by bk_loc, db, bk_type
order by db



 select 
 case
 when count(db2) > 1 then 'Mix Backups Found'
 else 'Only one Type of Backups'
 end as 'backups'
 , db2 from @Summary 
 
 group by db2
 having count(db2)>1 


select db,  bk_type,
case substring(bk_loc,2,1)
WHEN ':' THEN 'LOCAL DISK'
WHEN '\' THEN 'NETWORK LOCATION'  --'
ELSE 'API BACKUPS'
end as 'Type of Backups'

 from @BackupLocation 

where db in
(select db from @Summary 
 group by db2, db
 having count(db2)>1 
 )
 
group by bk_loc, db, bk_type
order by db


Thank you Andrey for the idea to create this script

Shrink All Transaction Logs

The following script loops through all the Tlogs and attempts to shrink them. Remember that a Tlog can be shrunk only if there is empty space at the end. In order to tell where the empty space is, run:


use [database]
DBCC LOGINFO

If the Status column is 0, then it's empty. If you do not have empty space at the end, it will not shrink, so you need to run Tlog backups. Sometimes you need to run Tlog backups several times for the portion that's empty to move to the end of the file. More on that here:

http://www.mssqltips.com/sqlservertip/1225/how-to-determine-sql-server-database-transaction-log-usage/

Now, for the script:

/*
Get a list of Databases and its Tlogs, and shrink them
Do the following.
1- Run Tlog Backups
2- Run this script

Author: Miguel Quintana

Compatibility list:
MSSQL2005
MSSQL2008
MSSQL2012

Does not work:
MSSQL2000

*/




DECLARE @numRec INT
DECLARE @counter INT
DECLARE @DBName SYSNAME
DECLARE @Tlog SYSNAME
DECLARE @CMD NVARCHAR(MAX)

DECLARE @table TABLE
(RowID INT IDENTITY(1, 1), dbname SYSNAME, tlogname SYSNAME)

-- create a table with the DBname and its Tlog file
-- skip offline and system DBs
INSERT INTO @table 
SELECT db_name(database_id), name FROM sys.master_files 
WHERE type_desc = 'LOG' and state_desc ='ONLINE' and database_id > 4

-- Set and initialize counters
SELECT @numRec=count(*), @counter=1 FROM @table

WHILE  @counter <= @NumRec
BEGIN 

SELECT @DBName = dbname, @Tlog = tlogname FROM @table WHERE RowID = @counter

SET @CMD='USE ' + quotename(@DBName, N'[') + 
                        N'
CHECKPOINT
DBCC SHRINKFILE (['+@Tlog+'], 0, TRUNCATEONLY)'

print @CMD
-- EXECUTE (@CMD)

set @counter=@counter+1
END


 

Ok.. well… what happen if you are forced to shrink the Tlog and you don't have space for backups? Now what?

There is a way where you can take a backup "into the ether"… tricking the database into dumping all pages and forcing to mark the Tlog as empty pages.

Doing this will not cause data loss, however it will invalidate the backup chain, and a full backup should be taken as soon as possible.

 

 


BACKUP LOG [AdventureWorks2012] TO  DISK = N'NUL' 

 

Telnet to Send Mail

One of the tools available to test database mail is to take SQL out of the equation and test directly using Telnet.

The first step is to try to Telnet, using the right port.

These commands are run from the CMD prompt on the host where you want to send emails from, and since it's old technology, you cannot make typos. The delete key most of the time does not work so you will have to start over.

Let's say the SMTP server is mail.domain.ext and you are sending emails from a "dummy" account called "admin@domain.com" to your own account "sqldba@email.com"

telnet mail.domain.ext 25

If we are not able to even Telnet to the SMTP server, there might be Network or Firewall issues. Otherwise it should open a new window, and a black prompt

Trying 10.152.152.152...
Connected to mail.domain.ext.
Escape character is '^]'.
220 mail.domain.ext ESMTP Sendmail ?version-number?; ?date+time+gmtoffset?

Then start your commands:

HELO Mail.com
250 mail.domain.ext Hello local.domain.name [loc.al.i.p], pleased to meet you

Don't worry too much about your local domain name although you really should use your exact fully qualified domain name as seen by the outside world the mail server has no choice but to take your word for it as of RFC822-RFC1123. This should give you:

250 mail.domain.ext Hello local.domain.name [loc.al.i.p], pleased to meet you

Enter your email info:

MAIL FROM: admin@domain.ext
250 2.1.0 mail@domain.ext... Sender ok
RCPT TO: sqldba@email.com
250 2.1.0 mail@otherdomain.ext... Recipient ok

DATA
Type in something here
.

To end the message enter a single "." on a line on it's own.

The mail server should reply with: 250 2.0.0 ???????? Message accepted for delivery.

If we are able to Telnet, but we are not allowed to send emails ("unable to relay message"), it means that we are talking to the SMTP server, but we are not part of the Account Control List (ACL) on the SMTP server. Ask the Admin to add you to the ACL so we can relay emails to your own network (provide the part after the @ sign of your email).

You can close the connection by issuing the QUIT command. The mailserver should reply with something like:

221 2.0.0 mail.domain.ext closing connection Connection closed by foreign host.

 

Database Mail Troubleshooting

Database Mail Troubleshooting can be tricky.

The first step is to Telnet to the SMTP server and see if you can send emails, and receive them.

 

After verifying that Telnet works, we usually start by getting the name of the profile and account details to test sending Email:


declare @subj nvarchar(100)
set @subj = 'Email from' + @@servername

EXEC sp_send_dbmail
  @profile_name='DBA_PROFILE'
, @recipients='sqldba@email.com'
, @subject=@subj
, @body='Please review this email Test 1'
--,  @file_attachments='E:\MSSQLDBA\Scripts\backup_report\log\bk_report.csv'
go

Then we would look at the actual queue:


-- Mail Queue: sent ITEMS
select * from  msdb.dbo.sysmail_mailitems
order by send_request_date desc

-- Display Error Queue
SELECT * FROM msdb.dbo.sysmail_event_log;

 

This query tracks specific emails and their error, if any

use MSDB
select a.mailitem_id, a.subject, a.body, a.sent_status, b.description
from sysmail_allitems a
left join sysmail_log b on a.mailitem_id = b.mailitem_id
order by a.mailitem_id desc

order by a.mailitem_id desc

Database Mail rarely breaks, but sometimes there are delays on the queue mails and you might need to clear the queue. In one occasion, I received the error:

The DB Mail gave the following error: Object Instance Not Set to an Instance of an Object.

The Maintenance Plans said:

Failed to acquire connection "Local Server Connection". Connection may not be configured correctly or you may not have the right permissions on this connection.

This happened right after a Windows Update that modified .Net Framework. Reverting to the old version or even repairing it fixed DB Mail

As I learn more about it, I'll be adding more

Database Mail Info

Database Mail profile information, along with account names and SMTP server


/*
Compatibility list:
MSSQL2005 - MSSQL2008 - MSSQL2008R2 - MSSQL2012
 
Does not work
MSSQL2000
 
Update Log:
*/

DECLARE       @DBEngineLogin       VARCHAR(100)
EXECUTE       master.dbo.xp_instance_regread
              @rootkey      = N'HKEY_LOCAL_MACHINE',
             @key          = N'SYSTEM\CurrentControlSet\Services\MSSQLServer',
              @value_name   = N'ObjectName',
              @value        = @DBEngineLogin OUTPUT

select 
p.name
, p.description
,a.name
, a.description
, a.email_address
, a.display_name
, a.replyto_address
, s.servertype
, s.servername
, s.port
, coalesce(s.username,@DBEngineLogin) -- If the username is empty, it is authenticating using Windows Credentials
, s.use_default_credentials
, s.enable_ssl
from msdb.dbo.sysmail_profile p
inner join msdb.dbo.sysmail_profileaccount pa on p.profile_id = pa.profile_id
inner join msdb.dbo.sysmail_account a on a.account_id = pa.account_id
inner join msdb.dbo.sysmail_Server s on a.account_id = s.account_id
GO

Generate a Random Password

Useful to generate random passwords


    /*
    DESCRIPTION: 
    Generates a (pseudo)random string of chars to be used in a password
     
    Configuration: on top. Length and desired chars
     
    Compatibility list:
    MSSQL2005
    MSSQL2008
    MSSQL2008R2
    MSSQL2012
     
    Not tested:
    MSSQL2000
     
    Update Log:
    */
     
    set nocount on
     
    declare
    	@plength int,
    	@password varchar(50),
    	@string varchar(72), 
    --52 possible letters + 10 possible numbers + up to 20 possible extras
    	@numbers varchar(10),
    	@extra varchar(20),
    	@index tinyint
     
     /* configuration  */
     
    set @plength = 10
     
     
    -- eliminate 0, 1, i, l, o, o to make the password more readable
    set @string = 'abcdefghjklmnpqrstuvwxyzabcdefghijkmnpqrstuvwxyz' -- option @charset = 0
    set @numbers = '23456789'
    set @extra = '>_!@#$%&=?<>' -- special characters
     
     
    /* end configuration  */
     
     
    set @string = @string + @numbers + @extra
     
    set @password = ''
    	
    	while (@plength > 0)
    	begin
    		set @index = (abs(checksum(newid())) % len(@string)) + 1 
    		
    		set @password = @password + substring(@string, @index, 1)
     
    		set @plength -= 1 --set @plength = @plength - 1
    	end
     
     
    /*
    -- This section actually changes the password of the SA account
    declare @cmd nvarchar(100)
    set @cmd = 'alter login [sa] with password=n'''+@password+''' unlock'
     
    execute (@cmd)
    -- */