inactive databases - sql-server

On our company's SQL server, there are a bunch of databases that don't appear to be used. Is there a way to determine the last time someone used a particular db, connected to it, or ran a query against it?

I don't think there's a per-database statistic for last used date.
What you can do is attach SQL Server profiler to the database, with a filter on database name. You can leave this running for a few weeks and see if there's any activity.
Another option is to check Database Properties -> Reports -> Standard Reports -> Index Usage Statistics. If the last use of any index is very old, that's a good indication the database is not being used.
Alternatively, have a look at SQL Server Auditing. I haven't used it myself, but it looks like it might suit your needs.

Here is actually a query I use. It tells you what DB was used last since its last restart. It gives a date it was last used and days since it was last used. This should be what you need. It is dynamic and will work on any server because is uses system views. Run this on the master DB.
DECLARE #RESTART DATETIME
SELECT #RESTART = login_time
FROM sysprocesses
WHERE spid = 1
SELECT ##SERVERNAME AS SERVER_NAME
,DB_NAME(db.database_id) AS 'DATABASE'
, db.state_desc AS 'State'
, #RESTART AS 'SYSTEM_RESTART'
, MAX(coalesce(us.last_user_seek, us.last_user_scan, us.last_user_lookup,#RESTART)) AS 'LAST_USE'
,CASE
WHEN DATEDIFF(DAY,#RESTART, MAX(coalesce(us.last_user_seek, us.last_user_scan, us.last_user_lookup,#RESTART))) = 0
THEN 'Prior to restart ' + CONVERT(VARCHAR(5),DATEDIFF(DAY,MAX(coalesce(us.last_user_seek, us.last_user_scan, us.last_user_lookup,#RESTART)), CURRENT_TIMESTAMP)) +' days ago'
ELSE CONVERT(VARCHAR(5),DATEDIFF(DAY,MAX(coalesce(us.last_user_seek, us.last_user_scan, us.last_user_lookup,#RESTART)), CURRENT_TIMESTAMP))
END AS 'DAYS_SINCE_LAST_USE'
FROM sys.databases db
LEFT OUTER JOIN sys.dm_db_index_usage_stats us ON db.database_id = us.database_id
/* IF you want to exclude offlinbe Dbs in your output
then remove the comment '--AND STATE != 6' below.
*/
WHERE db.database_id > 4 --AND STATE != 6
GROUP BY DB_NAME(db.database_id), db.state_desc
ORDER BY DB_NAME(db.database_id)

Related

Performance problems after updating statistics SQL Server 2014

I've received a database that was previously on SQL Server 2008R2 but was just put on a SQL Server 2014 instance. There were no maintenance tasks run of any kind run on the database since 2014 (e.g. Rebuilding of indexes, updating statistics, etc.).
Once we ran update statistics as part of our regularly scheduled maintenance that we do on a set schedule, the performance of some queries has taken a massive hit to the point where some select statements will seem to never finish.
The queries have some CASE...WHEN statements in them, but I wouldn't expect there to be such a performance hit. Does anybody have any thoughts on what might cause such issues?
I've tried updating the compatibility level to 120 since it was on 100 when the database first came in but, that didn't make any difference on the performance.
If you have only just moved the database, give the system some time to build up its execution plans and cache. Also, do your index maintenance and then something like this for the stats. Dont use sp_updatestats though as it just uses a sample of data not a full scan.
what results do you get for this:
SELECT
[sch].[name] + '.' + [so].[name] AS [TableName] ,
[ss].[name] AS [Statistic],
[sp].[last_updated] AS [StatsLastUpdated] ,
[sp].[rows] AS [RowsInTable] ,
[sp].[rows_sampled] AS [RowsSampled] ,
[sp].[modification_counter] AS [RowModifications],
Convert (decimal(18,2),(convert(numeric,[sp].[modification_counter]) / convert(numeric,[sp].[rows]) * 100)) as [Percent_changed]
FROM [sys].[stats] [ss]
JOIN [sys].[objects] [so] ON [ss].[object_id] = [so].[object_id]
JOIN [sys].[schemas] [sch] ON [so].[schema_id] = [sch].[schema_id]
OUTER APPLY [sys].[dm_db_stats_properties]([so].[object_id],
[ss].[stats_id]) sp
WHERE [so].[type] = 'U'
AND [sp].[modification_counter] > 0
And [sp].[last_updated] < getdate()-1
ORDER BY [Percent_changed] DESC

How to delete a table after a period of inactivity?

For the purpose of my project I cannot use session based temp tables. They need to be persistent but automatically deleted after a certain period of inactivity (no CRUD performed). Is this at all possible?
You can use the SQL Server Agent to Schedule a Job that calls a Stored Procedure that does this work for you. (How to Schedule a Job?)
How do you identify the tables that have not updated since X amount of time ?
Use this Query:
SELECT OBJECT_NAME(OBJECT_ID) AS TableName, last_user_update,
FROM sys.dm_db_index_usage_stats
WHERE database_id = DB_ID('DatabaseName')
AND OBJECT_NAME(OBJECT_ID) LIKE '%%' -- Here is the template name for your tables
AND DATEDIFF(MINUTE, last_user_update, GETDATE()) > 10 -- Last updated more than 10 minutes
Now that you have the tables to be deleted, you can use whatever logic you want to DROP them (Cursor, While, Procedure)
Sure it is. Write it into your program layer.
AUTOMATICALLY - within SQL Server: no. Well, you cold use the agent to start a script regularly.
Tracking what "inactivity" means - your responsibility.
You need save modification date of this table somewhere (for example in the same table or in another special table) and then you can create job, which checks last modification date and then drops the table.

Sql Server - how to get last server restart (DMV reset date/time)

I'm using some modifications to Glenn Berry's excellent DMV queries!
However, I would like to add to the resultset the 'last server restart', or to be more specific, the date/time the statistics for (all, the specific) DMV was reset.
Since it would be quite important to know last reset when looking at the statistics, I want to make absolutely sure the date/time is accurate and shown.
Question: How can you get the most accurate date/time of when a/all DMV statistic was reset?
Thanks!
-D
SELECT sqlserver_start_time FROM sys.dm_os_sys_info
Using a prior question (different key words), I ended up using this approach. As always, up to the individual what would be 'best' for them!
SELECT create_date
FROM sys.databases
WHERE name = 'tempdb'
source: Find out how long the sql server service has been running, from t-sql
This will work but you have to know the service name also its only available with R2 and later
SELECT last_startup_time
FROM sys.dm_server_services
WHERE servicename = "Your Service name"
Although this won't be totally accurate since you can also reset the DB specific views via a DB detach or a DB close.
Also there are two views that can be reset on a live db sys.dm_os_latch_stats and sys.dm_os_wait_stats
There are many ways to check when was SQL server restarted last time.
Below mentioned SQL queries can be used to quickly find out the
server restart date time.
SELECT sqlserver_start_time FROM sys.dm_os_sys_info;
SELECT login_time FROM sys.dm_exec_sessions WHERE session_id = 1;
select start_time from sys.traces where is_default = 1 ;
SELECT crdate FROM sysdatabases WHERE name='tempdb' ;
SELECT create_date FROM sys.databases WHERE name = 'tempdb' ;
There are various way to find out SQL Server reboot time. Following are the codes which return the server re-boot date:
SELECT sqlserver_start_time FROM sys.dm_os_sys_info;
SELECT login_time FROM sys.dm_exec_sessions WHERE session_id = 1;
SELECT start_time from sys.traces where is_default = 1;
SELECT crdate FROM sysdatabases WHERE name='tempdb';
SELECT create_date FROM sys.databases WHERE name = 'tempdb';

Execute query once in SQL Server despite being called mutiple times

I have a situation where a query might be called multiple times from multiple users, but I only want it to run once (per week) against the database. The environment is SQL Server Express so scheduling via SQL Server Agent is not an option. It needs to be 2005 compatible. I'd like to make it as lightweight as possible too, so I'm asking for suggestions. Ideally a database wide declared variable - but I don't think that SQL Server supports such a beast? Thanks
Try something like this:
IF NOT EXISTS ( -- Check if you have the current week content
SELECT *
FROM WeeklyTable
WHERE
DATEPART(YEAR, DateCr) = DATEPART(YEAR, GETDATE())
AND
DATEPART(WEEK, DateCr) = DATEPART(WEEK, GETDATE())
)
BEGIN
-- delete old content
DELETE WeeklyTable
-- insert new content
INSERT INTO WeeklyTable (MyID, MyField1, ... , MyFieldN, DateCr)
SELECT
MyID, MyField1, MyField2, GETDATE()
FROM MainTable
END
You can create indexes you need for the WeeklyTable.
One option would be SQL Scheduler as a add-on to SQL Server Express.
The other option would be to create a small command-line utility that does the querying and schedule that using the Windows Scheduler on the machine where SQL Server Express is installed.
With either of the two setups, you could select the values / numbers you need into a result table once a week, and any requests during the week would be satisfied from that one result table. SQL Server doesn't have "server-wide" variables - but you can always define a table for that purpose...

How can I get a list of all of the user databases via t-sql?

I want to get a list of all of the user databases from an mssql server instance. What's the best way to do this?
I know I can select from sys.databases, but I don't see any way to filter out system databases besides hardcoding a list of names to exclude.
I need the script to work on 2000/2005 and 2008.
If the approach I listed above is the only way to go, what are list of names I should exclude? I don't know if 2005 or 2008 added any new system databases off the top of my head.
Was looking in to this again today and decided to profile what Management Studio was doing to populate the Object Explorer details.
Turns out the solution Microsoft have implemented is pretty simplistic and boils down to the following:
SELECT *
FROM master.sys.databases
WHERE Cast(CASE WHEN name IN ('master', 'model', 'msdb', 'tempdb') THEN 1 ELSE is_distributor END As bit) = 0
Please note that this was performed using SSMS 2008R2 (10.50.4033.0).
The first query will return a table with data regarding all of the databases on the instance:
Select *
From sys.databases
From this table you'll notice you can narrow down the scope of data you're looking for by using the WHERE clause. For example, the following queries will essentially return the same result table (the one you're most likely looking for):
Select *
From sys.databases
Where database_id > 5
Select *
From sys.databases
Where len(owner_sid)>1
These queries will work in SQL Server 2008 and 2012.
On SQL Server 2008 R2 Express, looks like I cannot reliably use any of the above methods. INFORMATION_SCHEMA.SCHEMATA only shows me information in the current database, db_id (database_id) #5 is my first user database, and owner_sid on two of my user databases on one of my mirrored databases (running on SQL Server 2008 R2 Standard) shows owner_sid = 1 for my two most recently created databases. (PablolnNZ's comment above is correct: I did not set an explicit owner for those two databases so it still shows as having an owner of 'sa'.)
The only reliable means I was able to use was the following:
SELECT name FROM sys.databases
WHERE name NOT IN ('master', 'model', 'tempdb', 'msdb', 'Resource')
This works in 2005, not 100% sure about the other versions but I think it will fly. It's a bit of a hack but might get you what you need:
select * from sys.databases where len(owner_sid)>1
As nasty as it sounds to hardcode things. The names and number of system databases has been fairly consistent for several versions of SQL. However, if that is too unpleasant you could semi-hardcode them into a table and then plug that into your query.
Not sure if you can offhand. One note -- on 2k you'll have to use master.dbo.sysdatabases and not master.sys.databases (which doesn't exist in 2k).
Starting with SQL Server 2008 you have access to a view called sys.databases which when joined with sys.server_principals can eliminate the databases owned by sa, which you can (most often) safely discern are the "system databases". Thus, allowing you to filter these items out.
select
d.name
,d.database_id
from
sys.databases d
join
sys.server_principals p
on p.sid = d.owner_sid
where
p.name <> 'sa';

Resources