I'm working to enhance my understanding of some system sprocs and I'm very confused by this script I was working on. To exercise my understanding of sp_MSForEachDB I decided to write a script that would truncate the logs of all databases on a server. As such, I came up with the following script:
sp_MSForEachDb 'IF LOWER(rtrim(''?'')) NOT IN ('''', ''master'', ''tempdb'', ''tempdev'', ''model'', ''msdb'')
BEGIN
declare #LogFile nvarchar(max)
USE [?]
SELECT #LogFile = sys.sysaltfiles.name FROM sys.sysdatabases
INNER JOIN sys.sysaltfiles ON sys.sysdatabases.dbid = sys.sysaltfiles.dbid
WHERE (sys.sysaltfiles.fileid = 1) AND (sys.sysdatabases.name = ''?'')
print ''DB: [?], Log: '' + #LogFile
CHECKPOINT
DBCC SHRINKFILE (#LogFile, 1)
END'
It turns out that only sometimes does this successfully truncate the log of a database. On the databases it fails (no error message, just leaves me with an untruncated log file), it consistently/reproducibly fails.
In the print statement, however, it prints EXACTLY what I would expect it to print. However, if I manually just type out the functional part of this script for each database:
USE [Seed]
CHECKPOINT
DBCC SHRINKFILE('Seedlog', 1)
it works 100% of the time.
Why is my sp_MSForEachDB "loop" not working as expected? What am I missing?
It looks like your query is returning the logical name for the data file and not the log file? (fileID=1)
Related
I would like to know how I can switch from one database to another within the same script. I have a script that reads the header information from a SQL Server .BAK file and loads the information into a test database. Once the information is in the temp table (Test database) I run the following script to get the database name.
This part works fine.
INSERT INTO #HeaderInfo EXEC('RESTORE HEADERONLY
FROM DISK = N''I:\TEST\database.bak''
WITH NOUNLOAD')
DECLARE #databasename varchar(128);
SET #databasename = (SELECT DatabaseName FROM #HeaderInfo);
The problem is when I try to run the following script nothing happens. The new database is never selected and the script is still on the test database.
EXEC ('USE '+ #databasename)
The goal is switch to the new database (USE NewDatabase) so that the other part of my script (DBCC CHECKDB) can run. This script checks the integrity of the database and saves the results to a temp table.
What am I doing wrong?
You can't expect a use statement to work in this fashion using dynamic SQL. Dynamic SQL is run in its own context, so as soon as it has executed, you're back to your original context. This means that you'd have to include your SQL statements in the same dynamic SQL execution, such as:
declare #db sysname = 'tempdb';
exec ('use ' + #db + '; dbcc checkdb;')
You can alternatively use fully qualified names for your DB objects and specify the database name in your dbcc command, even with a variable, as in:
declare #db sysname = 'tempdb';
dbcc checkdb (#db);
You can't do this because Exec scope is limited to dynamic query. When exec ends context is returned to original state. But context changes in Exec itself. So you should do your thing in one big dynamic statement like:
DECLARE #str NVARCHAR(MAX)
SET #str = 'select * from table1
USE DatabaseName
select * from table2'
EXEC (#str)
I've inherited a fairly substantial project, which makes extensive use of SQL Server (2005 and 2008) views.
One step in the build process is the call the sp_refreshviews system stored procedure, to make sure, no changes on any tables have broken our views. This works fine .... except for about three or four (out of 200+) views....
With those, it just bombs out - gives odd error messages like
Msg 15165, Level 16, State 1,
Procedure
sp_refreshsqlmodule_internal, Line 55
Could not find object
'vYourViewNameHere' or you do not
have permission.
which is dead wrong - that view does exist, and I definitely can select from it.
I cannot seem to find any good concise information about why this happens, what triggers it... any ideas? Is there anything I could do to detect such problematic views? Can I change their definitino so that they'd be refreshable again?
Update: I logged a bug report on Microsoft Connect for this - if you agree this seems odd and needs to be fixed, please vote for it!
https://connect.microsoft.com/SQLServer/feedback/details/676728/sp-refreshview-crashes-with-misleading-error-on-views-with-schemabinding
I noticed in the comments you mention it has SCHEMABINDING. I can almost guarantee that is the issue. Books online specifically says this is for use on non-schema bound views.
A scheme-bound view wouldn't allow a breaking change to occur so updating the meta-data is un-necessary. You can safely skip it.
You can identify all the schemabound views like this:
SELECT * FROM sys.views WHERE OBJECTPROPERTY(object_id, 'IsSchemaBound')=1
I ran into the same error when using sp_helptext. In my case the cause was using sp_rename to rename the view. The following code reproduces this error.
create view demo as select dummy = 1
go
exec sp_rename 'demo', 'new_demo'
go
exec sp_refreshview 'new_demo'
go
The only solution is to manually alter the view. Apply this fix to the above solution and you get:
create view demo as select dummy = 1
go
exec sp_rename 'demo', 'new_demo'
go
-- This statement fixes the problem
alter view new_demo as select dummy = 1
go
exec sp_refreshview 'new_demo'
go
My incarnation of this error was:
Msg 8116, Level 16, State 1, Procedure sp_refreshsqlmodule_internal,
Line 75 Argument data type int is invalid for argument 1 of substring
function.
This error message was being reported at various places in the db script. I would say wrong places. If I commented out the SQL this error was reported at, the same error would be reported elsewhere.
I commented out the following call in my script as a workaround, and the script would complete successfully.
-- EXECUTE sp_refreshview #viewName;
Note: My database didn't report having an schemabound views when running the query suggested in RThomas' adjacent answer https://stackoverflow.com/a/6460532/179972
UPDATE - SOLUTION:
After our database script ran successfully with the sp_refreshview command commented out (shown above), we then ran the view refresh code on its own, and it was successful too.
--
This answer doesn't make sense to me as to how it was able to work successfully, however I've documenting it here in case it proves helpful to somebody else.
To find which view is your problem add a print to the normal sppRefreshViews. Nothing earth shattering here, but i thought I would share.
CREATE procedure sppRefreshViews2
as
declare #t varchar (1024)
declare tbl_cur cursor for
select TABLE_NAME from INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'VIEW' and table_name like 'sp%'
OPEN tbl_cur
FETCH NEXT from tbl_cur INTO #t
WHILE ##FETCH_STATUS = 0
BEGIN
print #t
exec ('sp_refreshview ''' + #t + '''')
FETCH NEXT from tbl_cur INTO #t
END
CLOSE tbl_cur
DEALLOCATE tbl_Cur
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.
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)'
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'