Shrink Transaction Log for none active server - sql-server

Currently I have few database no longer active (No new entry), however my company does not want to drop it as they want keep it for reference purpose.
But due to huge database size I have difficult time to backup and restore. (Company policy, time to time have to backup and restore to test the database is workable or not).
So I'm thinking, I want to shrink the transaction log to reduce the time needed for the process, is that advisable to do that? Emphasize, since no new entry.
FYI, the transaction file (ldf) is much bigger than actual database size (mdf).
No matter is advisable to do or not, or even have better way to do please let me know.

I use this script
DECLARE #DBName varchar(255)
DECLARE #LogName varchar(255)
DECLARE #DATABASES_Fetch int
DECLARE DATABASES_CURSOR CURSOR FOR
select distinct
name, db_name(s_mf.database_id) dbName
from
sys.master_files s_mf
where
s_mf.state = 0 and -- ONLINE
has_dbaccess(db_name(s_mf.database_id)) = 1 -- Only look at databases to which we have access
and db_name(s_mf.database_id) not in ('master','tempdb','model','msdb','distribution')
and db_name(s_mf.database_id) not like 'MSDB%'
and db_name(s_mf.database_id) not like 'Report%'
and type=1
order by
db_name(s_mf.database_id)
OPEN DATABASES_CURSOR
FETCH NEXT FROM DATABASES_CURSOR INTO #LogName, #DBName
WHILE ##FETCH_STATUS = 0
BEGIN
exec ('USE [' + #DBName + '] ; DBCC SHRINKFILE (N''' + #LogName + ''' , 0, TRUNCATEONLY)')
FETCH NEXT FROM DATABASES_CURSOR INTO #LogName, #DBName
END
CLOSE DATABASES_CURSOR
DEALLOCATE DATABASES_CURSOR

Yes, this is a good idea. The easiest way to do this is first change the Recovery Model to Simple like this:
Then you can use Tasks; Shrink; Files to reduce the transaction log space to zero. There are many resources to show you how to shrink a file, but this article explains it nicely:
How to shrink the transaction log
Shrinking a file is not normally recommended, but it advisable if you do not expect more transactions. This will save you time when you backup and it will take up less disk space.

Related

Could this cursor be optimized or rewritten for optimum performance?

There is a need to update all of our databases on our server and perform the same logic on each one. The databases in question all follow a common naming scheme like CorpDB1, CorpDB2, etc. Instead of creating a SQL Agent Job for each of the databases in question (over 50), I have thought about using a cursor to iterate over the list of databases and then perform some dynamic sql on each one. In light of the common notion that cursors should be a last resort; could this be rewritten for better performance or written another way perhaps with the use of the undocumented sp_MSforeachdb stored procedure?
DECLARE #db VARCHAR(100) --current database name
DECLARE #sql VARCHAR(1000) --t-sql used for processing on each database
DECLARE db_cursor CURSOR FAST_FORWARD FOR
SELECT name
FROM MASTER.dbo.sysdatabases
WHERE name LIKE 'CorpDB%'
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #db
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = 'USE ' + #db +
' DELETE FROM db_table --more t-sql processing'
EXEC(#sql)
FETCH NEXT FROM db_cursor INTO #db
END
CLOSE db_cursor
DEALLOCATE db_cursor
Cursors are bad when they are used to tackle a set-based problem with procedural code. I don't think a cursor is necessarily a bad idea in your scenario.
When operations need to be run against multiple databases (backups, integrity checks, index maintenance, etc.), there's no issue with using a cursor. Sure, you could build a temp table that contains database names and loop through that...but it's still a procedural approach.
For your specific case, if you're not deleting rows in these tables based on some WHERE clause criteria, consider using TRUNCATE TABLE instead of DELETE FROM. Differences between the two operations explained here. Note that the user running TRUNCATE TABLE will need ALTER permission on the affected objects.
This will collect the set of delete statements and run them all in a single sequence. This is not necessarily going to be better performance-wise but just another way to skin the cat.
DECLARE #sql NVARCHAR(MAX); -- if SQL Server 2000, use NVARCHAR(4000)
SET #sql = N'';
SELECT #sql = #sql + N';DELETE ' + name + '..db_table -- more t-sql'
FROM master.sys.databases
WHERE name LIKE N'CorpDB%';
SET #sql = STUFF(#sql, 1, 1, '');
EXEC sp_executesql #sql;
You may consider building the string in a similar way inside your cursor instead of running EXEC() inside for each command. If you're going to continue using a cursor, use the following declaration:
DECLARE db_cursor CURSOR
LOCAL STATIC FORWARD_ONLY READ_ONLY
FOR
This will have the least locking and no unnecessary tempdb usage.

How to delete .ldf file from SQL Server 2008?

If I stop SQL-server and then delete the .LDF file (transactionlog file) to the database, what will happen ? Will the database be marked suspect or will SQL-server just create a new automatically ? SQL Server 2008 R2
And My .LDF file Size is Too Big, So how to manage it, whether I can Shrink it or delete
Please Suggest in the Query Form.
You should not delete any of the database files since it can severely damage your database!
If you run out of disk space you might want to split your database in multiple parts. This can be done in the database's properties. So you are able to put each part of the database to a different storage volume.
You also can shrink the transaction log file if you change the recovery mode from full to simple, using following commands:
ALTER DATABASE myDatabase SET RECOVERY SIMPLE
DBCC SHRINKDATABASE (myDatabase , 5)
Switching back to full recovery is possible as well:
ALTER DATABASE myDatabase SET RECOVERY FULL
Update about SHRINKDATABASE - or what I did not know when answering this question:
Although the method above gets rid off some unused space it has some severe disadvantages on database files (MDF) - it will harm your indexes by fragmenting them worsening the performance of your database. So you need to rebuild the indexes afterwards to get rid off the fragmentation the shrink command caused.
If you want to shrink just the log file only might want to use SHRINKFILE instead. I copied this example from MSDN:
USE AdventureWorks2012;
GO
-- Truncate the log by changing the database recovery model to SIMPLE.
ALTER DATABASE AdventureWorks2012
SET RECOVERY SIMPLE;
GO
-- Shrink the truncated log file to 1 MB.
DBCC SHRINKFILE (AdventureWorks2012_Log, 1);
GO
-- Reset the database recovery model.
ALTER DATABASE AdventureWorks2012
SET RECOVERY FULL;
GO
Do not risk deleting your LDF files manually! If you do not need transaction files or wish to reduce them to any size you choose, follow these steps:
(Note this will affect your backups so be sure before doing so)
Right click database
Choose Properties
Click on the 'Options' tab.
Set recovery model to SIMPLE
Next, choose the FILES tab
Now make sure you select the LOG file and scroll right. Under the "Autogrowth" heading click the dots ....
Then disable Autogrowth (This is optional and will limit additional growth)
Then click OK and set the "Initial Size" to the size you wish to have (I set mine to 20MB)
Click OK to save changes
Then right-click the DB again, and choose "Tasks > Shrink > Database", press OK.
Now compare your file sizes!:)
I did it by
Detach the database (include Drop Connections)
Remove the *.ldf file
Attach the database, but remove the expected *.ldf file
Did it for 4 different databases in SQL 2012, i should be the same for SQL 2008
As you can read comments, it is not good solution to remove log. But if you are sure that you do not lose anything, you can just change your DB recovery mode to simple and then use
DBCC shrinkdatabase ('here your database name')
to clear your log.
The worst thing that you can do is to delete log file from disk. If your server had unfinished transactions at moment of server stop, those transactions will not roll back after restart and you will get corrupted data.
You should back up your transaction log, then there will be free space to shrink it. Changing to simple mode then shrinking means you will lose all the transaction data which would be useful in the event of a restore.
The best way to clear ALL ldf files (transaction log files) in all databases in MS SQL server, IF all databases was backed up earlier of course:
USE MASTER
print '*****************************************'
print '************ Czyƛcik LDF ****************'
print '*****************************************'
declare
#isql varchar(2000),
#dbname varchar(64),
#logfile varchar(128),
#recovery_model varchar(64)
declare c1 cursor for
SELECT d.name, mf.name as logfile, d.recovery_model_desc --, physical_name AS current_file_location, size
FROM sys.master_files mf
inner join sys.databases d
on mf.database_id = d.database_id
--where recovery_model_desc <> 'SIMPLE'
and d.name not in ('master','model','msdb','tempdb')
and mf.type_desc = 'LOG'
and d.state_desc = 'online'
open c1
fetch next from c1 into #dbname, #logfile, #recovery_model
While ##fetch_status <> -1
begin
print '----- OPERATIONS FOR: ' + #dbname + ' ------'
print 'CURRENT MODEL IS: ' + #recovery_model
select #isql = 'ALTER DATABASE ' + #dbname + ' SET RECOVERY SIMPLE'
print #isql
exec(#isql)
select #isql='USE ' + #dbname + ' checkpoint'
print #isql
exec(#isql)
select #isql='USE ' + #dbname + ' DBCC SHRINKFILE (' + #logfile + ', 1)'
print #isql
exec(#isql)
select #isql = 'ALTER DATABASE ' + #dbname + ' SET RECOVERY ' + #recovery_model
print #isql
exec(#isql)
fetch next from c1 into #dbname, #logfile, #recovery_model
end
close c1
deallocate c1
This is an improved code, based on: https://www.sqlservercentral.com/Forums/Topic1163961-357-1.aspx
I recommend reading this article: https://learn.microsoft.com/en-us/sql/relational-databases/backup-restore/recovery-models-sql-server
Sometimes it is worthwhile to permanently enable RECOVERY MODEL = SIMPLE on some databases and thus once and for all get rid of log problems. Especially when we backup data (or server) daily and daytime changes are not critical from a security point of view.

Shrinklog all user databases in SQL2008

Hi I am using this script in the process of weekly maintenance, suggest best approach/scripts to do shrinklog. Currently am getting an error with the below script
declare #s nvarchar(4000)
set #s= '
if ''?'' not in (''tempdb'',''master'',''model'',''msdb'')
begin
use [?]
Alter database [?] SET Recovery simple
end '
exec sp_msforeachdb #s
set #s= '
if ''?'' not in (''tempdb'',''master'',''model'',''msdb'')
begin
use [?]
Declare #LogFileLogicalName sysname
select #LogFileLogicalName=Name from sys.database_files where Type=1
DBCC Shrinkfile(#LogFileLogicalName,1)
end'
exec sp_msforeachdb #s
Error Description:
ShrinkLog Execute SQL Task Description: Executing the query "declare #s nvarchar(4000) set #s= ' ..." failed with the following error: "Option 'RECOVERY' cannot be set in database 'tempdb'. Cannot shrink log file 2 (DBServices_Log) because total number of logical log files cannot be fewer than 2. DBCC execution completed. If DBCC printed error messages, contact your system administrator.
note: I am avoiding tempdb(all System db) in my script, but error message shows tempdb?
This is probably the worst maintenance script I've seen in the past year. A maintenance script that every week breaks the log chain and makes the database unrecoverable?? OMG. Not to mention that the simple premise of shrinking the log on a maintenance task is wrong. If the database log has grown to a certain size, than that size is needed. Schedule log backups more frequently to prevent this, but don't schedule log shrink operations.
You can avoid the 5058 error by using an exec statement to alter the database recovery model. The following script will set each user database to a simple recovery model. (It uses Jimmy's answer to detect system databases.)
exec sp_msforeachdb 'if exists (select name from sys.databases d where case when d.name in (''master'',''model'',''msdb'',''tempdb'') then 1 else d.is_distributor end = 0
and name = ''?'' and recovery_model_desc != ''SIMPLE'')
begin
declare #previousRecoveryModel varchar(100) = (select recovery_model_desc from sys.databases where name = ''?'');
print ''Changing recovery model for ? from '' + #previousRecoveryModel + '' to SIMPLE.'';
use master;
-- Using exec avoids a compile-time 5058 error about tempdb, which is a branch that will never be executed in this code.
exec (''alter database [?] set recovery simple'');
end';
Then you won't get the compile-time error about tempdb because the exec statement will compile its statement with the database name variable already substituted.

How to get the logical name of the transaction log in SQL Server 2005

I am trying to write a T-SQL routine that shrink the transaction log file using DBCC SHRINKFILE based on the logical name of the database. The DB_NAME() function gives you the logical name of the database. Is there an equivalent one for the transaction log? If not, is there some other way to get this information? The default name for the transaction logs is <<Database Name>>_log, but I would rather not rely on this.
You can use:
SELECT name
FROM sys.master_files
WHERE database_id = db_id()
AND type = 1
Log files have type = 1 for any database_id and all files for all databases can be found in sys.master_files.
EDIT:
I should point out that you shouldn't be shrinking your log on a routine basis. Your transaction log should be sized appropriately to keep it from ever having to grow, and then left at that size. The transaction log can not be instant file initialized and has to be zero'd out when space is added to it, which is a slow sequential operation that degrades performance.
Assuming a standard database (eg only one log file), the log file is always file_id = 2. This applies even if you have multiple data files (id = 3+ for NDFs).
The DBCC also takes the file id too. So, DBCC SHRINKFILE (2...) will always work. You can't parameterise inside the DBCC so this avoids dynanmic SQL. If you want the name, use FILE_NAME(2).
select Name
from sys.database_files
Generates,
SomeDb_Data
SomeDb_Log
SqlServer 2012
DECLARE #command varchar(1000)
SELECT #command = 'USE [?] DBCC SHRINKFILE (2 , 0, TRUNCATEONLY)'
EXEC sp_MSforeachdb #command
--OR simply
EXEC sp_MSforeachdb 'USE [?] DBCC SHRINKFILE (2 , 0, TRUNCATEONLY)'

Automating DBCC CHECKDB

I am currently trying to script a job on SQL Server 2005 that will automate the DBCC CHECKDB process. Basically, I am using a cursor to run through and run DBCC CHECKDB on every database on an instance. Sometimes it works, running through every database and logging the errors in a table I have designed for that purpose and sometimes it only runs through a few of the databases and stop. Does anyone have any idea what is going on? I have included the code that I use for the cursor.
DECLARE #DbName varchar(100)
DECLARE
GetDbName CURSOR
LOCAL
FORWARD_ONLY
OPTIMISTIC
FOR
SELECT
name
FROM
sys.databases
ORDER BY
name
OPEN GetDbName
FETCH NEXT FROM GetDbName
INTO #DbName
WHILE (##fetch_status = 0)
BEGIN
print #DbName
INSERT INTO
TempLog
EXEC('DBCC CHECKDB ('+ #DbName +') WITH NO_INFOMSGS, TABLERESULTS')
FETCH NEXT FROM GetDbName
INTO #DbName
END
CLOSE GetDbName
DEALLOCATE GetDbName
Errors of a sufficiently high (20+) severity -- in my experience these are most often corruptions such as invalid text pointers -- will stop whatever SQL job is currently running with extreme prejudice, ignoring try/catch constructs and killing the connection. I would suggest moving this task out to an external process with a new connection for each DBCC CHECKDB command so that it can continue in the face of high-severity errors.
use the undocumented sp_MSforeachdb stored proc
exec sp_msforeachdb 'use ?; dbcc checkdb'

Resources