SQL Data Tools for Business Intelligence released

Last week saw the long awaited release of SQL Server Data Tools for Business Intelligence. This version of SSDT  has the templates for creating Intelligence Services packages, Reporting Services reports, and Analysis Services projects both cubes and tabular, inside of Visual Studio 2012. That’s great news as a lot of developers, myself included, have been waiting for this functionality.

However the bad news is that it doesn’t include the Database Projects templates released last year. You’ll still need to install them separately. But they will work together.

So if you want just the BI templates for Visual Studio 2012 you only have to install the BI version of SSDT. If you also want the database projects you will need to install both the BI templates and the database templates. And if you want to use the test plans for your new database projects and create SSRS reports or SSIS packages you’ll need a full edition of VS 2012, either Premium or Ultimate, plus the database templates plus the BI templates.

You can get the database project templates for either VS2010 or VS2012 from the SSDT Team blog. The BI templates for VS2012 can be found as a Microsoft download. And you can read more about the added functionality on the SSRS Team blog, the SSIS Team blog, or the SSAS Team blog.

How do you perform SQL security audits?

Do you need to track permissions on the SQL server databases that you are responsible for? Are you sure you know everyone who has sysadmin rights, or who can delete records from the production servers? How often should you perform a security audit?

In my case I don’t have to check permissions on a regular basis. But I still want to keep track of who can do what. So I run the following scripts to check both server and database level permissions as well as check the default trace for any new permissions or users I may not be aware of. They also help me easily compare permissions across servers.

I run these scripts on a mix of SQL 2005 and SQL 2008 servers. I haven’t had a chance to run on SQL 2008 R2 or SQL 2012 yet so I’m not guaranteeing that they’ll work there. Also they may return a lot of records, depending on the number of users in your databases. But they can be modified to filter by user. As always, though, please understand the scripts before running them on a production server.

One final note. I used a script I found on the internet a while back as a basis for the ones I’m using now. Unfortunately I don’t remember where I found them so I can’t properly give credit where it’s due.

This script is run against the server and has 2 parts. First it returns a list of server roles and who belongs to which role. The second part returns individual permissions for each account.

-- Server role members
SELECT
sp1.[name]
, sp1.type_desc
, sp2.[name]
, sp2.type_desc
, sp2.create_date
, sp2.modify_date
FROM sys.server_role_members AS srm
INNER JOIN sys.server_principals AS sp1
ON srm.role_principal_id = sp1.principal_id
INNER JOIN sys.server_principals AS sp2
ON srm.member_principal_id = sp2.principal_id
ORDER BY sp1.[name], sp2.[name]

-- Server users
SELECT
sp1.[name]
, sp1.type_desc
, sp1.create_date
, sp1.modify_date
, sp.state_desc AS 'Permission'
, sp.permission_name AS 'Action'
, sp.class_desc
, sc.class_desc
, sp2.[name]
FROM sys.server_permissions AS sp
INNER JOIN sys.server_principals AS sp1
ON sp.grantee_principal_id = sp1.principal_id
INNER JOIN sys.server_principals AS sp2
ON sp.grantor_principal_id = sp2.principal_id
LEFT JOIN sys.securable_classes AS sc
ON sp.major_id = sc.class
ORDER BY sp1.[name], sp1.type_desc

My next script does the same thing on a database level. Just change the name of the database to see who belongs to which database role or other individual rights.

USE <database_name, sysname, master>;

-- Database role members
SELECT
pr1.[name]
, pr1.type_desc
, pr2.[name]
, pr2.type_desc
, pr2.create_date
, pr2.modify_date
FROM sys.database_role_members AS rm
INNER JOIN sys.database_principals AS pr1
ON rm.role_principal_id = pr1.principal_id
INNER JOIN sys.database_principals AS pr2
ON rm.member_principal_id = pr2.principal_id
ORDER BY pr1.[name], pr2.[name]

-- Database users
SELECT
pr.[name]
, pr.type_desc
, pr.create_date
, pr.modify_date
, dp.state_desc AS 'Permission'
, dp.permission_name AS 'Action'
, CASE dp.class
WHEN 0 THEN 'Database::' + DB_NAME()
WHEN 1 THEN ISNULL(s.[name] + '.', '') + OBJECT_NAME(dp.major_id)
WHEN 3 THEN 'Schema::' + SCHEMA_NAME(dp.major_id)
END AS 'Securable'
, pr2.[name]
FROM sys.database_permissions AS dp
INNER JOIN sys.database_principals AS pr
ON dp.grantee_principal_id = pr.principal_id
INNER JOIN sys.database_principals AS pr2
ON dp.grantor_principal_id = pr2.principal_id
LEFT JOIN sys.objects AS o
ON dp.major_id = o.[object_id]
LEFT JOIN sys.schemas AS s
ON o.[schema_id] = s.[schema_id]
WHERE dp.class IN (0, 1, 3, 4)
AND dp.minor_id = 0
ORDER BY pr.[name], pr.type_desc

Finally there’s a way to monitor who is adding users, or changing permissions. Those events are captured by the default trace that runs when the SQL service starts up. My script only checks the latest trace file, but it can be modified to look at older .trc files. If you’ve disabled the default trace then this won’t work.

USE master;

SET NOCOUNT ON;

DECLARE @enable int;
 SELECT TOP 1 @enable = CONVERT(int,value_in_use)
 FROM sys.configurations
 WHERE name = 'default trace enabled';

IF @enable = 1
 BEGIN
 DECLARE @d1 datetime;
 DECLARE @diff int;
 DECLARE @curr_tracefilename varchar(500);
 DECLARE @base_tracefilename varchar(500);
 DECLARE @indx int ;

SELECT @curr_tracefilename = path
 FROM sys.traces
 WHERE is_default = 1;

SET @curr_tracefilename = reverse(@curr_tracefilename);
 SELECT @indx = PATINDEX('%\%', @curr_tracefilename);
 SET @curr_tracefilename = reverse(@curr_tracefilename);
 SET @base_tracefilename = LEFT(@curr_tracefilename,len(@curr_tracefilename) - @indx) + '\log.trc';

SELECT
 t.LoginName
 , t.HostName
 , t.ApplicationName
 , t.StartTime
 , t.DatabaseName
 , t.TargetUserName
 , t.RoleName
 , e.[name] AS EventDesc
 FROM ::fn_trace_gettable(@base_tracefilename, default) AS t
 INNER JOIN sys.trace_events AS e ON t.EventClass = e.trace_event_id
 WHERE t.EventClass BETWEEN 104 AND 114
 END
 

I’d love to hear how you handle security on your servers. I’m always looking for ways to improve my processes.

T-SQL script to add new schedule to existing job

Like a lot of fellow SQL Server DBA’s I have a number of production servers to manage. One of the tasks I have to perform every year is to create a new set of one-time schedules for an existing job. The date of the schedule varies from month to month due to different business logic. This task would be extremely time consuming if I had to use the SSMS interface to add the new schedules to the jobs. So I’ve prepared a T-SQL script I can run on multiple servers at once.

Here’s the script I use. The parameters are all the options you would set in the SSMS GUI. In my case this is a one time schedule not set to run via a recurring pattern (every Monday for example). I set @active_start_date and @active_start_time to the date and time I want the schedule to run on; in this case it’s set to February 4th 2013 at 8:50 PM.

EXEC msdb.dbo.sp_add_jobschedule
@job_name = 'My Job Name',
@name = 'Jan 2013',
@enabled = 1,
@freq_type = 1,
@freq_interval = 1,
@freq_subday_type = 0,
@freq_subday_interval = 0,
@freq_relative_interval = 0,
@freq_recurrence_factor = 1,
@active_start_date = 20130204,
@active_end_date = 99991231,
@active_start_time = 205000,
@active_end_time = 235959

I usually set a year’s worth of schedules at the same time. When I’m done I run the following script to insure that I’ve created the schedules correctly.

DECLARE @job_id UNIQUEIDENTIFIER
SET @job_id = (SELECT job_id FROM msdb.dbo.sysjobs WHERE name = 'My Job Name')

SELECT name, active_start_date, active_start_time
FROM msdb.dbo.sysschedules
WHERE schedule_id IN
(
SELECT schedule_id
FROM msdb.dbo.sysjobschedules
WHERE job_id = @job_id
)
AND enabled = 1

image

And that’s all there is to it. I hope this helps you but please, test the script first. Make sure you understand what it’s doing before running in production. If you screw something up I won’t be around to help you out as I plan on winning the next lottery!

Quickly Build a Database Sandbox Using SSDT Power Tools

I’ve written a few posts on using SQL Server Data Tools (SSDT) to build a database sandbox to play in. There’s a few ways to build out the schema, either by using database object scripts or directly from an existing database. The problem with those solutions is that it doesn’t include the data, though there are ways to populate the sandbox db. But if you use the Power Tools for SSDT you can quickly create and deploy a fully populated version.

First, go to the SSDT blog site to download and install the latest version of SSDT Power Tools. There’s one for Visual Studio 2010 and Visual Studio 2012, be sure to grab the right one. Also grab the latest SSDT update, if you haven’t already. After that it’s a simple 2 step process..

Step One is to right click on the database you’re creating the copy of and choose Extract Data-Tier Application. Enter the name and file path for the extract. Next choose the Extract Settings. You have the option to only extract the schema, or you can include the data. If you choose to include the data you can select one or more tables whose data you want to include. Finally check the options you want, like also scripting logins and permissions. When you’re done your screen will look something like the one below. Go ahead and click OK when you’re done.

image

You can watch the progress while the extract is running. The time required depends on the size of the database. In my little demo I’m using a small database so it only took 30 seconds. The process creates a more detailed log that you can read if you want to see more than just the summary.

image

Step Two is just Step One in reverse. This time right click on the Databases node of the SQL instance where you’re deploying to and choose Publish Data-Tier Application. Select the .dacpac file you created in Step One, enter a database name, edit the Target database connection if you need to, and choose whatever other options you want. Click Publish when you’re ready. In this case I’m publishing back to the same SQL instance where the original lives, but with a different database name. But you will most likely be publishing it on a different instance.

image

Again, you can watch the progress of your deploy, and view the log for more detailed info. My little sample only took 20 seconds.image

After that you can go play in your new database sandbox. Not counting mouse clicks it only took me 50 seconds!

image

Have your own SQL Summit

Last year I attended my first PASS SQL Summit. I wouldn’t have missed it for the world. I’m already looking forward to next year, when I’ll be heading to Charlotte for Summit 2013. Reading the twitter feed for this years event was a bit depressing. It brings back all those great memories from last year.

But even though I can’t be in Seattle next week, there are still plenty of opportunities for me to keep on learning from the best and brightest in the SQL world. And that’s the main reason why I went last year, for the training. That doesn’t have to stop.

So if you’re like me and aren’t heading to Summit 2012 I offer this list of some resources I’ll be using in the next week. Kind of like hosting my own SQL Summit, only without leaving home and a lot cheaper! Most of these are free, except for the final bullet point.  

  • For what I believe is the first time, PASS will be running PASS TV. You’ll be able to watch the keynote sessions, the Women In Technology panel discussion, and some featured spotlight sessions. PASS says they’ll be streaming three 12 hour days with overnight replays so that should make it easy to watch some great content any time during the summit.
  • Watch recordings from a past Summit. Sessions from Summit 2011 are only available to attendees, but you can still find plenty of content from years past. See the list here.
  • Catch up on 24 Hours of PASS recordings. There’s still some I haven’t had a chance to watch yet. What better time than now?
  • Have you checked out the PASS Virtual Chapters yet? If not you’re missing a great resource. log in and find a chapter you’re interested in. They have a regular webcast schedule, and recordings of past sessions are available. Believe me, it’s a huge list!

And there are other websites besides PASS.

  • I’m sure you’ve heard of SQLSkills before. It’s the company run by Paul Randal and Kimberly Tripp and the employer of some very brilliant SQL people. They have a ton of videos online. Some of the best are the ones they recorded for MCM Training. Also check out their Insider Newsletters. The archives usually have links to short 5 -  10 minute videos they discuss in the newsletter.
  • Along the same line is Brent Ozar Unlimited. Brent, Kendra Little, Jeremiah Peschka, and Jes Schultz Borland take turns hosting frequent webcasts on a large variety of topics; SQL, SAN, and virtualization to name a few. They’ve graciously made their past recordings available under First Aid > Video Archive.  
  • Pragmatic Works specializes in Microsoft Business Intelligence. I’ve learned a lot from their in-person classes. They also have weekly webcasts, and there’s a huge list of past webinars.
  • The only pay site on this list is Pluralsight. However it’s such a great resource I wanted to include it. They have a free trial offer to view their entire catalog for 10 days or 200 minutes. And since they have hundreds of courses you’ll probably burn through that in no time. But you can get a monthly subscription for just $29. SQLSkills is recording content for Pluralsight, the same kind of training you’d get in their immersion classes. Stacia Misner has some on SSIS, SSRS, and SSAS. Julie Lerhman has done quite a bit on the Entity Framework. You get a lot for the money.

So don’t just sit around moping because you’re not in Seattle. Keep on learning!

Collecting data with PowerShell

Like most DBAs, I collect different statistics on the SQL servers I’m responsible for. I want to be able to track database growth over time. I also want a way to quickly check how much space is available. At first I wrote a few local scripts and saved to tables in a local database. While learning SSIS for the 70-448 exam I created a package to move the data from the local servers to a central repository and output the last two collections to an Excel spreadsheet. That works pretty well. But I’m always looking for new and possibly better ways to perform my day to day tasks. And I just got a list of new SQL servers to monitor, and I wasn’t able to fully port my SSIS solutions to include some of those servers. So I decided to switch over to PowerShell.

This post isn’t meant to be an overview or PowerShell, or even PowerShell for SQL. Those topics have been covered elsewhere and much more thoroughly than I can here. If you need a starting point for PowerShell I’ll put some links to sites to get you started at the end of this post. Today I’m going to cover two specific tasks; getting total and free space per drive and space used by each database for a list of servers. The results will be stored in a central database that I can query later for trends.

For this post my central repository is called “MONITOR” and the database is called “ServerStats”. Inside ServerStats I’ve created the two tables below;

CREATE TABLE dbo.DBStats(
ServerName sysname NOT NULL,
RunDate datetime NOT NULL,
DBName sysname NOT NULL,
Name sysname NOT NULL,
[FileName] nchar(520) NOT NULL,
TotalSize float NOT NULL,
UsedSpace float NOT NULL,
FreeSpace float NOT NULL,
FileID int NOT NULL
)

CREATE TABLE dbo.DriveSpace(
ServerName sysname NOT NULL,
Drive char(3) NULL,
TotalSpaceInGB numeric(6, 2) NULL,
FreeSpaceInGB numeric(6, 2) NULL,
RunDate datetime NULL
)

To run the PowerShell scripts you’ll need to get a few things. In my profile I load the SQLPSX snap-ins as well as Invoke-SQLCMD2, Write-DataTable, and Out-DataTable. See the Links and Downloads section at the bottom of this post for more information on these modules.

My first script is for getting the drive size. I simply loop through an array of servers, then query WMI for hard drive information with Get-WMIObject. I don’t want info on CD drives so I only filter for where the drive type is 3. I send the result to a data table object ( | Out-DataTable). Finally I write the data table back to my monitoring server with Write-DataTable. Notice I declare the variable $unit as “GB”. PowerShell then gives me my data in gigabytes. I could change this to “MB” if I want to see the size in megabytes instead.

$srvlist = @(get-content ".\ServerList.txt")
$unit = "GB"
$measure = "1$unit"
$wmiQuery = "
SELECT SystemName, Name, DriveType, FileSystem, FreeSpace, Capacity, Label
FROM Win32_Volume WHERE DriveType = 3
"@

foreach ($instance in $srvlist)
{
$dt = Get-WmiObject -ComputerName $instance -Query $wmiQuery |
Select-Object SystemName, Name, @{Label"TotalSpaceIn$unit"; Expression={"{0:n2}" -f ($_.Capacity/$measure)}}, @{Label="FreeSpaceIn$unit"; Expression={"{0:n2}" -f ($_.FreeSpace/$measure)}} |
Where-Object {$_.Name -NotLike '\\?\*'} |
Sort-Object Name | Out-DataTable

Write-DataTable -ServerInstance "MONITOR" –Database ServerStats-TableName DriveSpace -data $dt
}

My second script, for getting database sizes, is similar. I still loop through an array of servers. But this time for each instance I run a dynamic SQL script by calling Invoke-Sqlcmd2. That script creates a temp table, then loops through each database for the sizes. I’m using a trusted connection but if I wasn’t I could supply –UserName and -Password to Invoke-Sqlcmd2.

$srvlist = @(get-content ".\SQLServerList.txt")

foreach ($instance in $srvlist)
{
$dt = Invoke-Sqlcmd2 -ServerInstance $instance -Database master "CREATE TABLE #dbStats(
[Servername] [sysname] NULL,
[RunDate] [datetime] NULL,
[DBName] [sysname] NULL,
[Name] [sysname] NULL,
[FileName] [nchar](520) NULL,
[TotalSize] [float] NULL,
[UsedSpace] [float] NULL,
[FreeSpace] [float] NULL,
[FileID] [int] NULL)

EXEC sp_msforeachdb 'USE [?]

DECLARE @PageSize float
SELECT @PageSize = v.low /1024.0 FROM master.dbo.spt_values v WHERE v.number = 1 AND v.type = ''E''

INSERT INTO #dbStats
SELECT @@servername AS ServerName,
CONVERT(DATETIME, CONVERT(CHAR(12), GETDATE(), 101)) AS RunDate,
''?'' AS DBName,
RTRIM(s.name) AS [Name],
RTRIM(s.filename) AS [FileName],
(s.size * @PageSize) AS [TotalSize],
CAST(FILEPROPERTY(s.name, ''SpaceUsed'') AS float)* CONVERT(float, 8) AS [UsedSpace],
(s.size * @PageSize) - CAST(FILEPROPERTY(s.name, ''SpaceUsed'') AS float) * CONVERT(float, 8) AS [FreeSpace],
CAST(s.fileid AS int) AS [ID]
FROM sysfiles AS s'

SELECT * FROM #dbStats"
Write-DataTable -ServerInstance "MONITOR" –Database ServerStats –TableName DBStats -Data $dt
}

And that’s just about it. I’ve got a SQL Agent job on the monitoring server that runs both PowerShell scripts once a week. The nice thing, at least so far, is that I can run these scripts against different SQL servers and OS versions. My list has servers running Windows 2000 to 2008 R2, and SQL instances in the same time frame. There’s even a few Express editions in there. My next step will be to create an Excel spreadsheet to view the data. I’m thinking PowerView for that.

I’d be interested in hearing how you work with SQL and PowerShell. Just leave a comment. Or let me know how I can improve my scripts.

Links and Downloads

  • Get the SQL Extensions for PowerShell from Codeplex.
  • Invoke-sqlcmd2, Write-DataTable, and Out-DataTable were also written by Chad Miller (Blog | Twitter) and are available on the TechNet site. All these modules are well documented.
  • There are a lot of good sites that can get you started with PowerShell. A simple Google search will turn up plenty. But start with Hey Scripting Guy! Also follow Ed Wilson and his Scripting Wife Teresa on Twitter
  • One of my favorites sites is Allen White’s (Blog | Twitter). Allen also presents at numerous SQLSaturday events. Find one near you where he’s speaking. For that matter, SQLSaturdays usually have at least one session dedicated to PowerShell.
  • Another good PowerShell site is is the Stairway to PowerShell series on SQLServerCentral, written by Ben Miller.

Resending failed emails through sp_send_email

Just a quick little post about an issue that pops up occasionally while I slowly work my way back into SQL after my test.

There are a lot of SQL databases servers that have been configured to use Database Mail to send email alerts for failed jobs, query results, alerts, or other tasks that require some kind of notification. Database Mail, introduced with SQL 2005, is the much improved way to send emails, far better than the older SQL Mail option. If you’re not familiar with Database Mail, go read up on it first on Books Online.

However I’ve found that some DBAs that set up Database Mail aren’t familiar with how to troubleshoot it when users complain they haven’t received expected emails from SQL. Actually it’s pretty simple. Microsoft has included some system views in the msdb database to help you out, and a log to view errors.

If you don’t want to use T-SQL you can view the database mail logs from SSMS. Just right click on Database Mail in the Object Explorer and choose View Database Mail Log (of course this is after you’ve configured Database Mail). Here you can see the status of db mail and any emails that didn’t get sent, including the reason for the failure.

image

You can get the same information by querying msdb.dbo.sysmail_event_log, including filters for event_type and a specific date if you want;

SELECT * FROM msdb.dbo.sysmail_event_log
WHERE event_type = 'error'
AND log_date >= dateadd(d, -1, getdate())

image

You can use other views to see the content of the emails. SYSMAIL_FAILEDITEMS shows emails that didn’t get sent, SYSMAIL_SENTITEMS shows emails that were sent successfully, and SYSMAIL_ALLITEMS  combines the two. You can filter by the email_id you got from the log, or see all by a date.

SELECT *
FROM msdb.dbo.sysmail_faileditems
WHERE mailitem_id = 56299;

OK. So you’ve found out that there was a problem sending emails through SQL. You’ve looked at the log to find the problem and fixed it. Now you want to resend the failed emails. Couldn’t be easier. Just query one of the msdb views for the emails you want to resend, store relevant fields in variables, then call the sp_send_dbmail procedure passing in the variables. You can use it for a single item, or wrap it in a loop to resend multiple emails. I use a script like the one below.

DECLARE @to        varchar(max)
DECLARE @copy    varchar(max)
DECLARE @title    nvarchar(255)
DECLARE @msg    nvarchar(max)
SELECT @to = recipients, @copy = copy_recipients, @title = [subject], @msg = body
FROM msdb.dbo.sysmail_faileditems
WHERE mailitem_id =  56299
EXEC msdb.dbo.sp_send_dbmail
@recipients = @to
@copy_recipients = @copy
@body = @msg,
@subject = @title,
@body_format = 'HTML';

And that’s it. Simple, no?

Using Powershell to be notified when SQL service starts

This is just a quick post while I take a little break from studying for the 70-448 exam next month.

We have 4 SQL 2005 servers and 4 SQL 2008 servers in our environment, 2 of each in 2 data centers. All SQL services on all 8 instances are configured to start manually; we want to look at logs and events before starting up SQL in cases where the servers are restarted with no warning.

However the SQL database services on the 4 instances in one data center are still starting automatically. I’ve double checked all the settings and I’m not aware of any monitoring software that would restart the services. Until I find out what process is starting these services I want a way to be notified of when the services start up.

I’ve already set up a stored procedure to run when the SQL service starts so I thought I’d use that. At first I thought I’d just use SQL mail, but that wouldn’t work if the Agent wasn’t also running. So I decided on using a Powershell script.

It couldn’t be easier. I set few variables with email addresses and the current time, set the SMTP server to our Exchange server, and create a new Net.Mail.SMTPClient. Then it’s just sending the emails with the variables.

$now = Get-Date
$from = "someone@work.com"
$to = "monitoring@work.com"
$subject = "SQL Service on Server 1 Started"
$body = "The SQL service on Server 1 has started at " + $now
$smtpServer = "email.work.com"
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($from, $to, $subject, $body)

I save the above as SendMail.ps1. The only downside is that I call it with xp_cmdshell;

EXEC xp_cmdshell 'powershell -Command "C:\SQL2005\Scripts\SendMail.ps1"'

And that’s it. I’d be interested in hearing any ideas you might have to improve my process, or even better, how to trap the process that’s starting my SQL services.

T-SQL Tuesday #025 – Monitoring table growth

T-SQL Tuesday

This month’s T-SQL Tuesday is being hosted by Allen White (Blog | Twitter). The topic is sharing your T-SQL scripts that you use to make your job easier. Below is one of mine.

One of the things I like to keep track of is table growth; that is, which tables grow the most over a period of time. This helps me to determine if I have enough database space allocated, and gives me a rough estimate of when the servers will need more drive space added.

At first I just monitored the size of the databases files and watched how big they were getting and how much or little free space was still available. But I also wanted a way to see just where that growth was. So I would run sp_spaceused on occasion on our biggest tables and compare each set of results to the previous set.

However this is not very efficient. As our application grew and more and more tables were being added it was harder to know just which tables were being affected the most.

So finally I began to use two un documented procedures to look at all tables in all databases. The first is sp_MSForEachDB, the second is sp_MSForEachTable. Each does just what it sounds like; runs a query against all databases or loops through all tables. I’m not going to go into detail here as neither procedure is new and each has been written about countless times before.

My procedure builds a dynamic SQL statement, getting the schema name and table name using sp_MSForEachTable. Part of that statement is an INSERT statement so I can store the results. Then I run the dynamic statement against all user databases on the server.

USE [PerfDB]
GO

CREATE TABLE [dbo].[TableSpace](
[RunDate] [datetime] NOT NULL,
[DatabaseName] [nvarchar](128) NULL,
[SchemaName] [nvarchar](128) NULL,
[TableName] [nvarchar](128) NOT NULL,
[NumRows] [int] NOT NULL,
[Reserved] [nchar](16) NOT NULL,
[DataUsed] [nchar](16) NOT NULL,
[IndexUsed] [nchar](16) NOT NULL,
[Unused] [nchar](16) NOT NULL
) ON [PRIMARY]
GO

ALTER TABLE [dbo].[TableSpace] ADD  CONSTRAINT [DF_TableSpace_RunDate]  DEFAULT (getdate()) FOR [RunDate]
GO

CREATE PROCEDURE [dbo].[prc_TableSpace]
AS

SET NOCOUNT ON;

DECLARE @SQL varchar(8000)

SELECT @SQL = '
IF ''@'' <> ''master'' AND ''@'' <> ''model'' AND ''@'' <> ''msdb'' AND ''@'' <> ''tempdb''
BEGIN
USE [@] EXECUTE sp_MSForEachTable ''INSERT INTO PerfDB.dbo.TableSpace (TableName, NumRows, Reserved, DataUsed, IndexUsed, Unused) EXEC sp_spaceused ''''?'''';
UPDATE PerfDB.dbo.TableSpace SET SchemaName = LEFT(''''?'''', CHARINDEX(''''.'''', ''''?'''', 1) - 2) WHERE SchemaName IS NULL;
UPDATE PerfDB.dbo.TableSpace SET DatabaseName = ''''@'''' WHERE DatabaseName IS NULL; ''
END
'

EXEC sp_MSforeachdb @SQL, '@'

UPDATE PerfDB.dbo.TableSpace
SET SchemaName = REPLACE(SchemaName, '[', '')

I set up a weekly job that runs during a period of inactivity. I also have a job that runs an SSIS package to collect the table results into a central repository. Then I can run the procedure like the one below to show me how much every table grew in the past week, or a similar one that shows the growth over a period of time.

CREATE PROCEDURE [dbo].[TableSizeCompare]
AS

SET NOCOUNT ON

CREATE TABLE #myTable (
ServerName        NVARCHAR(25),
RunDate            DATETIME,
DatabaseName    NVARCHAR(128),
SchemaName        NVARCHAR(128),
TableName        NVARCHAR(128),
NumRows            INT,
ReservedInKB    INT,
DataUsedInKB    INT,
IndexUsedInKB    INT,
RowNum            TINYINT)

;WITH TableSpaceCTE (ServerName, RunDate, DatabaseName, SchemaName, TableName, NumRows,
ReservedInKB, DataUsedInKB, IndexUsedInKB, RowNum) AS
(
SELECT ServerName, RunDate, DatabaseName, SchemaName, TableName, NumRows,
CONVERT(INT, LEFT(Reserved, LEN(Reserved) - 3)) AS ReservedInKB,
CONVERT(INT, LEFT(DataUsed, LEN(DataUsed) - 3)) AS DataUsedInKB,
CONVERT(INT, LEFT(IndexUsed, LEN(IndexUsed) - 3)) AS IndexUsedInKB,
ROW_NUMBER() OVER (PARTITION BY ServerName, DatabaseName, SchemaName, TableName ORDER BY RunDate DESC) AS RowNum
FROM PerfDB_DW.dbo.TableSpace
)
INSERT INTO #myTable
SELECT ServerName, RunDate, DatabaseName, SchemaName, TableName, NumRows,
ReservedInKB, DataUsedInKB, IndexUsedInKB, RowNum
FROM TableSpaceCTE
WHERE RowNum <= 2

SELECT
MaxRun.ServerName
, MaxRun.DatabaseName
, MaxRun.SchemaName
, MaxRun.TableName
, MaxRun.RunDate AS LastRunDate
, MinRun.RunDate AS PriorRunDate
, MaxRun.NumRows AS CurrentNumRows
, MinRun.NumRows AS PriorNumRows
, MaxRun.NumRows - MinRun.NumRows AS NumRowGrowth
, MaxRun.ReservedInKB AS CurrentReservedInKB
, MinRun.ReservedInKB AS PriorReservedInKB
, MaxRun.ReservedInKB -  MinRun.ReservedInKB AS ReservedGrowthInKB
, MaxRun.DataUsedInKB AS CurrentDataUsedInKB
, MinRun.DataUsedInKB AS PriorDataUsedInKB
, MaxRun.DataUsedInKB -  MinRun.DataUsedInKB AS DataUsedGrowthInKB
, MaxRun.IndexUsedInKB AS CurrentIndexUsedInKB
, MinRun.IndexUsedInKB AS PriorIndexUsedInKB
, MaxRun.IndexUsedInKB -  MinRun.IndexUsedInKB AS IndexUsedInKB
FROM
(SELECT * FROM #myTable WHERE RowNum = 1) AS MaxRun
INNER JOIN
(SELECT * FROM #myTable WHERE RowNum = 2) AS MinRun
ON MaxRun.ServerName = MinRun.ServerName AND MaxRun.DatabaseName = MinRun.DatabaseName
AND MaxRun.SchemaName = MinRun.SchemaName AND MaxRun.TableName = MinRun.TableName
ORDER BY MaxRun.ServerName, MaxRun.DatabaseName, MaxRun.SchemaName, MaxRun.TableName

image

70-448 Study Sheet – Custom logic in a cube with MDX

If you look at the Microsoft Learning page for 70-448 that displays the skills being measured, SSAS is 38 % of the total. Of that, 21% is on Implementing a SSAS Solution. One of those objectives is as follows:

  • Implement custom logic in a cube by using MDX.
    This objective may include but is not limited to: actions; key performance indicators (KPI); calculated members; calculations

Since I’m getting near the time where I’ll schedule my exam, I thought I’d start putting together some study sheets. There’s nothing new here, just some basics that I’ll update as I continue to compile my notes. Feel free to add to the list.

Actions

  • actions are just that: actions that a client can take on a cube. They’re defined in the cube and there’s 3 types:
    • Regular actions– different types
      • Dataset– MDX statements
      • Proprietary– actions that are dependent on the client
      • Rowset– statement to retrieve data on any OLE DB data source
      • Statement– returns success or failure against an OLE DB data source
      • URL – the default action, a string passed to  the web browser.
    • Drillthrough– client gets data from the cube based on the value selected
    • Reporting – gets an SSRS report

Key Performance Indicators (KPI)

  • quantifiable measures that show how a measure performs
  • compared to a set goal over time
  • usually displayed on a dashboard or scorecard.
  • they can be enhanced with actions
  • they can be organized into logical folders with the DISPLAY FOLDER property.
  • the main properties are
    • VALUE– current value of the measure
    • GOAL– what the value should be. It can be a fixed number, a value from a regular member, or a calculated member.
    • STATUS– how the value compares to the goal. Returns 1 for good, 0 for average, and –1 for bad. Can be represented by images defined in STATUS INDICATOR property (stoplight, smiley faces, etc.), but not all clients will show the icon (Excel will, SSRS won’t).
    • TREND– how the value performs over time. Returns same as STATUS. Displays images defined in TREND INDICATOR property.
    • WEIGHT – the importance of the KPI compared to related KPIs.
  • can be accessed by using MDX statements (KPIValue( ), KPIGoal( ), KPIStatus( ), KPITrend( )).

Calculated Members

  • a dimension member that’s calculated dynamically at runtime.
  • created with MDX statement – CREATE MEMBER <MemberName> AS <definition>

Named Sets

  • an alias for a set of dimension members. Created with MDX (CREATE SET <NewSetName> AS <definition>)
  • two types, depends on how the server evaluates them;
    • static named sets are evaluated once
    • dynamic named sets are evaluated during each query. This is the default when creating named sets in BIDS. New in SQL 2008.