Thursday, January 27, 2011

An Inside View

I recently had to create a number of views which forced me to brush up on “view” internals, I’m more of a stored proc. kind of guy.  I had forgotten many of the rules and nuisance's involved in creating and maintaining views so I’ve decided to record them in this post for my reference.
I will use the below two tables for demonstration:
USE tempdb; GO CREATE TABLE People( PersonID   INT PRIMARY KEY, FirstName  NVARCHAR(50), MiddleName NVARCHAR(50), LastName   NVARCHAR(50) NOT NULL
);
GO CREATE TABLE Credit( CreditID   INT IDENTITY PRIMARY KEY, PersonID   INT  REFERENCES People(PersonID), Limit      MONEY ); GO INSERT People SELECT DISTINCT BusinessEntityID,
     
FirstName,
     
MiddleName,
     
LastName FROM AdventureWorks2008.Person.Person p JOIN AdventureWorks2008.Sales.SalesOrderHeader s ON p.BusinessEntityID = s.CustomerID; GO INSERT Credit SELECT DISTINCT p.BusinessEntityID,
     
MAX(CEILING(TotalDue)) FROM AdventureWorks2008.Person.Person p JOIN AdventureWorks2008.Sales.SalesOrderHeader s ON p.BusinessEntityID = s.CustomerID GROUP BY p.BusinessEntityID;

First rule to point out is the column limitation involved with views.  A view is limited to ONLY 1,024 columns.  In many cases a collection of 1,024 columns would be considered a database, but for those who love to push the upper limit have at it as I am not going to test this, not that I’ll admit.
Another key point to keep in mind is that when creating a view all derived columns must have column names supplied, if the column name is not specified and the column is not derived the column name is the same as the name in the SELECT statement.  For example, the below error occurs when attempting to create a view that uses a derived column and does not supply a column alias:
image
Column names can be added in the query body, in this case after the derived column concatenating the three name columns, “AS FullName”, or the column names can be specified immediately after the view name, these must be in the same ordinal position as the columns defined in the SELECT statement defined in the view:
image
Some quick and easy rules to consider:
A view CAN NOT:
  • Contain the INTO keyword
  • The OPTION clause
  • COMPUTE or COMPUTE BY clauses, these are deprecated anyway so STAY AWAY.
  • Reference a table variable or temporary table
  • Exceed nesting level of 32
  • The ORDER BY clause can only be used if the TOP clause is included
I can’t help but mention the rule of using ORDER BY in a view.  Prior to SQL 2005 the “work around” to using ORDER BY in a view was to use TOP as this:
CREATE VIEW …
AS
SELECT TOP 100 PERCENT..
ORDER BY
Believe it or not this worked and ordering was honored when the view was SELECTed!  (Don’t hate the player..  Hate the game)  In SQL 2005, and up, this method can still be included within the body of a view, but the ordering is not honored when the view is called from a SELECT statement so the order must be specified from the calling query. 

Transactions Using Views

Views can server as a means to INSERT, UPDATE, OR DELETE to one of the underlying base tables.  There are obviously some limitations with this functionality:
  1. The INSERT, UPDATE, or DELETE statement can only reference columns from one base table. CREATE VIEW Updateable AS
    SELECT
    p.PersonID,
         
    FirstName,
         
    MiddleName,
         
    LastName,
         
    FirstName + ' ' +
         
    ISNULL(MiddleName, '') +
         
    ' ' + LastName AS FullName,
         
    Limit FROM People p JOIN Credit c ON p.PersonID = c.PersonID;
    --This fails as the UPDATE references
    --columns from two tables, People(LastName) and Credit
    (Limit) UPDATE Updateable SET LastName = 'Dye',
       
    Limit = 1000000 WHERE PersonID = 11000

    --This UPDATE statement succeeds since
    --only the People(LastName) is referenced
    UPDATE Updateable SET LastName = 'Dye' WHERE PersonID = 11000
  2. Columns being modified must directly reference the underlying table, the column can not be derived, such as the FullName column in the above view
  3. Columns being modified are not affected by GROUP BY, HAVING, or DISTINCT
  4. The TOP clause is not used in the SELECT statement along with the WITH CHECK OPTION clause
  5. The INSERT, UPDATE, or DELETE can not violate any constraint on the affected base table, NULL, primary key, foreign key, etc.
To handle data modifications of a view INSTEAD OF triggers can be placed on a view to handle INSERT, UPDATE, and/or DELETE. 

Indexed Views

A view that uses WITH SCHEMABINDING can have a clustered unique index,  WITH SCHEMABINDING binds the view to the underlying table(s) which means that the referenced table(s) can not be modified in any way that would affect the view definition.  This can be used to improve the performance of the view, but keep in mind that the index has the same overhead as would any unique clustered index.  The limitations of creating an indexed view:
  1. The column(s) referenced in the index must be unique
  2. A view that contains an OUTER JOIN can not be indexed

    CREATE VIEW IndexedViewOuter WITH SCHEMABINDING AS
    SELECT
    p.PersonID,
         
    FirstName,
         
    MiddleName,
         
    LastName,
         
    FirstName + ' ' +
         
    ISNULL(MiddleName, '') +
         
    ' ' + LastName AS FullName,
         
    Limit FROM dbo.People p RIGHT OUTER JOIN dbo.Credit c ON p.PersonID = c.PersonID --This will fails as the view 
    --uses an outer join, as well as the referenced column are not unique
    CREATE UNIQUE CLUSTERED INDEX ix_indexedview ON IndexedViewOuter(LastName, FirstName, MiddleName)
  3. The query can not use "SELECT *"
A detailed technical article regarding indexed views can be found here.
If the WITH SCHEMABINDING option is used then the ability to create a view using “SELECT * ” is prohibited, which is not necessarily a bad thing.  It is poor practice to use a SELECT all in a view as this can cause issues if the table structure should change:
--Create a table and insert values into it CREATE TABLE tt (
   
test1 INT PRIMARY KEY,
   
test2 INT
   
) GO INSERT INTO tt (test1, test2) VALUES (1,2) GO --Create the view select all columns with * CREATE VIEW vw_tt AS
SELECT
*,
     
GETDATE() AS 'My Date' FROM tt
GO
--Select * from the view SELECT * FROM vw_tt
GO
image
--Alter the table adding another column ALTER TABLE tt ADD test3 INT GO --Select from the view the GETDATE() is NULL now SELECT * FROM vw_tt

image
Since the underlying table has changed and the definition of the view used SELECT * along with a derived column of GETDATE() the result is that the derived column is returning the value of the newly added column.  This can be remedied using the system stored procedure sp_refreshview:
--Refresh the view sp_REFRESHVIEW vw_tt --Select from the view after it has been refreshed
--All columns appear now
SELECT * FROM vw_tt

image --Drop all objects DROP TABLE tt DROP VIEW vw_tt
MSDN’s online documentation for views can be found here and was the primary source for this post.

Tuesday, January 25, 2011

T-SQL Class Examples

I am truly sorry that I am just now getting to posting these examples.

USE AdventureWorks2008R2
--Using variables wiht control flow in t-sql
DECLARE @var INT
SET
@var = (SELECT COUNT (*) FROM Person.Person)
WHILE @var > 0
 
IF (SELECT @var-1) >= 1000
BEGIN
SELECT
@var - 1
SELECT FirstName,
     
MiddleName,
     
LastName
FROM Person.Person
WHERE BusinessEntityID = @var
SET @var = @var - 1
END;
GO
SELECT OrderQty + 10,
     
OrderQty
FROM Sales.SalesOrderDetail;
GO
--Variables have a limimted scope to the batch
--GO ends the scope
DECLARE @var INT = 10
SELECT @var;
SELECT @var;
GO
--@var is out of scope and an error is raiased
SELECT @var;
GO
--Using a common table expression
WITH CTE
AS
(
SELECT *
FROM Person.Person
)
SELECT FirstName
FROM CTE;
GO
--Structured error handling in t-sql
BEGIN TRY
   
-- Generate divide-by-zero error.
   
SELECT 1/0;
END TRY
BEGIN CATCH
  
PRINT 'Cant do that'
  
SELECT ERROR_NUMBER(),
        
ERROR_MESSAGE()
END CATCH;

--Unhandled error that occurs during binding
BEGIN TRY
   
-- Generate divide-by-zero error.
   
SELECT NotHere
  
FROM NoSuchThing
END TRY
BEGIN CATCH
  
PRINT 'Cant do that'
  
SELECT ERROR_NUMBER(),
        
ERROR_MESSAGE()
END CATCH;

--Order by clause ordering by the LastName then FirstName
SELECT FirstName,
     
LastName
FROM Person.Person
ORDER BY LastName,
       
FirstName;
GO

--WHERE using =
SELECT FirstName,
     
MiddleName,
     
LastName
FROM Person.Person
WHERE MiddleName = ''

--LIKE operator last name beginning with Ab and then any other letters
SELECT *
FROM Person.Person
WHERE LastName LIKE 'Ab%';
GO

--LIKE Operator LastName beginning with Ab and the next letter CAN NOT BE e
SELECT FirstName,
     
LastName,
     
MiddleName
FROM Person.Person
WHERE LastName LIKE 'Ab[^e]%';

--Using OR and AND in the WHERE clause
--FirstName Like and MiddleName IS NULL are evaluated together 4 rows
SELECT FirstName,
     
LastName,
     
MiddleName
FROM Person.Person
WHERE LastName LIKE 'Ab%'
AND (FirstName LIKE 'K%'
OR MiddleName IS NULL);

--Using OR and AND in the WHERE clause
--FirstName Like and MiddleName IS NULL are evaluated separately 8,500 rows
SELECT FirstName,
     
LastName,
     
MiddleName
FROM Person.Person
WHERE LastName LIKE 'Ab%'
AND FirstName LIKE 'K%'
OR MiddleName IS NULL

--USING BETWEEN and the >= and <= equivalent
SELECT *
FROM Sales.SalesOrderDetail
WHERE UnitPrice BETWEEN 1 AND 10;

SELECT *
FROM Sales.SalesOrderDetail
WHERE UnitPrice >= 1
AND UnitPrice <= 10;

--Using BETWEEN with character data types
SELECT FirstName,
     
LastName
FROM Person.Person
WHERE LastName BETWEEN 'A' AND 'N';

--Using IN with a sub-query
SELECT *
FROM Person.Address
WHERE City IN (SELECT City FROM Person.Address WHERE City LIKE '%a%');

--NULL values used in expression results in NULL
SELECT 8 + 1 + NULL

--Using ISNULL to replace a NULL value with a specified value
SELECT FirstName,
     
ISNULL(MiddleName, 'N/A'),
     
LastName
FROM Person.Person

--Using ISNULL in an expression
SELECT 8 + 1 + ISNULL(NULL, 2);

--Using COALESCE in an expression to handle NULL values
SELECT 8 + 1 + COALESCE(NULL, NULL, NULL, 4);

--DISTINCT is applied to the ENTIRE row, ot just the column name immediately following DISTINCT
SELECT DISTINCT LastName,
     
FirstName,
     
BusinessEntityID
FROM Person.Person;

--Using String literals
SELECT 'Hello my name is ' + FirstName + ' '+ LastName lName,
     
'This will NEVER CHANGE' AS never
FROM Person.Person;

--Using ISNULL with string literals
SELECT FirstName + ' '+ ISNULL(SUBSTRING(MiddleName, 1,1) + '.'' ') + ' ' + LastName lName,
     
'This will NEVER CHANGE' AS never
FROM Person.Person;

--Using expressions
SELECT OrderQty * UnitPrice - UnitPriceDiscount AS SalesPrice
FROM Sales.SalesOrderDetail
WHERE OrderQty > 10
ORDER BY OrderQty

--COUNT aggregation showing the total count of rows
SELECT COUNT(*)
FROM HumanResourc

SQL Rally Administration Track Voting is Open

Voting has opened for the category of enterprise database administration and deployment for SQL Rally that will be held in Orlando May 11th through the 13th.  The session outlines can be found here and you can vote for the presentations you’d like to attend here.  I have submitted a session for “Monitoring/Auditing” that focuses on SQL Audit

If you are planning on attending then take the time to vote for sessions that interest you for the inaugural PASS SQL Rally!

Friday, January 21, 2011

SQL Saturday #62 Code Samples

I am very sorry that I am just now posting the code samples I used at last weeks SQL Saturday in Tampa.

I am finally getting caught up and will be home for an entire week so there are no excuses for posting!!

Thanks for all who attended and please feel free to contact me with any questions.

--************************************************************
--Create server side trace using t-sql demo
--************************************************************

-- Create a Queue
DECLARE @rc INT
DECLARE @TraceID INT
DECLARE @maxfilesize BIGINT
SET @maxfilesize = 50


EXEC @rc = sp_trace_create @TraceID OUTPUT, 2, N'C:\Audit', @maxfilesize, NULL, 100
IF (@rc != 0) GOTO error

-- Set the events
DECLARE @on BIT
SET @on = 1
EXEC sp_trace_setevent @TraceID, 14, 1, @on
EXEC sp_trace_setevent @TraceID, 14, 9, @on
EXEC sp_trace_setevent @TraceID, 14, 6, @on
EXEC sp_trace_setevent @TraceID, 14, 10, @on
EXEC sp_trace_setevent @TraceID, 14, 14, @on
EXEC sp_trace_setevent @TraceID, 14, 11, @on
EXEC sp_trace_setevent @TraceID, 14, 12, @on
EXEC sp_trace_setevent @TraceID, 15, 15, @on
EXEC sp_trace_setevent @TraceID, 15, 16, @on
EXEC sp_trace_setevent @TraceID, 15, 9, @on
EXEC sp_trace_setevent @TraceID, 15, 13, @on
EXEC sp_trace_setevent @TraceID, 15, 17, @on
EXEC sp_trace_setevent @TraceID, 15, 6, @on
EXEC sp_trace_setevent @TraceID, 15, 10, @on
EXEC sp_trace_setevent @TraceID, 15, 14, @on
EXEC sp_trace_setevent @TraceID, 15, 18, @on
EXEC sp_trace_setevent @TraceID, 15, 11, @on
EXEC sp_trace_setevent @TraceID, 15, 12, @on
EXEC sp_trace_setevent @TraceID, 17, 12, @on
EXEC sp_trace_setevent @TraceID, 17, 1, @on
EXEC sp_trace_setevent @TraceID, 17, 9, @on
EXEC sp_trace_setevent @TraceID, 17, 6, @on
EXEC sp_trace_setevent @TraceID, 17, 10, @on
EXEC sp_trace_setevent @TraceID, 17, 14, @on
EXEC sp_trace_setevent @TraceID, 17, 11, @on
EXEC sp_trace_setevent @TraceID, 10, 15, @on
EXEC sp_trace_setevent @TraceID, 10, 16, @on
EXEC sp_trace_setevent @TraceID, 10, 9, @on
EXEC sp_trace_setevent @TraceID, 10, 17, @on
EXEC sp_trace_setevent @TraceID, 10, 2, @on
EXEC sp_trace_setevent @TraceID, 10, 10, @on
EXEC sp_trace_setevent @TraceID, 10, 18, @on
EXEC sp_trace_setevent @TraceID, 10, 11, @on
EXEC sp_trace_setevent @TraceID, 10, 12, @on
EXEC sp_trace_setevent @TraceID, 10, 13, @on
EXEC sp_trace_setevent @TraceID, 10, 6, @on
EXEC sp_trace_setevent @TraceID, 10, 14, @on
EXEC sp_trace_setevent @TraceID, 12, 15, @on
EXEC sp_trace_setevent @TraceID, 12, 16, @on
EXEC sp_trace_setevent @TraceID, 12, 1, @on
EXEC sp_trace_setevent @TraceID, 12, 9, @on
EXEC sp_trace_setevent @TraceID, 12, 17, @on
EXEC sp_trace_setevent @TraceID, 12, 6, @on
EXEC sp_trace_setevent @TraceID, 12, 10, @on
EXEC sp_trace_setevent @TraceID, 12, 14, @on
EXEC sp_trace_setevent @TraceID, 12, 18, @on
EXEC sp_trace_setevent @TraceID, 12, 11, @on
EXEC sp_trace_setevent @TraceID, 12, 12, @on
EXEC sp_trace_setevent @TraceID, 12, 13, @on
EXEC sp_trace_setevent @TraceID, 13, 12, @on
EXEC sp_trace_setevent @TraceID, 13, 1, @on
EXEC sp_trace_setevent @TraceID, 13, 9, @on
EXEC sp_trace_setevent @TraceID, 13, 6, @on
EXEC sp_trace_setevent @TraceID, 13, 10, @on
EXEC sp_trace_setevent @TraceID, 13, 14, @on
EXEC sp_trace_setevent @TraceID, 13, 11, @on


-- Set the Filters
DECLARE @intfilter INT
DECLARE @bigintfilter BIGINT
DECLARE @databaseid INT

--Set the databaseid to filter only on the fuel database
SELECT @databaseid = DB_ID(N'AdventureWorks2008R2')

EXEC sp_trace_SETfilter @TraceID, 3, 0, 0, @databaseid
-- Set the trace status to start
EXEC sp_trace_SETstatus @TraceID, 1

-- Display trace id for future references
SELECT TraceID=@TraceID
GOTO finish

ERROR:
SELECT ErrorCode=@rc

FINISH:
GO


/*
-- This commented code can be used to stop and delete the trace
-- Stop and remove the trace when done
EXEC sp_trace_setstatus 2, 0
EXEC sp_trace_setstatus @traceid = 2 , @status = 2
*/


--************************************************************
--Create DDL audit
--************************************************************

--Create table to hold DDL events
USE AdventureWorks
GO
CREATE TABLE AuditLog
(ID INT PRIMARY KEY IDENTITY(1,1),
Command NVARCHAR(1000),
PostTime NVARCHAR(24),
HostName NVARCHAR(100),
LoginName NVARCHAR(100)
)
GO

-- create DDL trigger
CREATE TRIGGER Audit
ON DATABASE
FOR DDL_DATABASE_LEVEL_EVENTS
AS
DECLARE @data XML
DECLARE @cmd NVARCHAR(1000)
DECLARE @posttime NVARCHAR(24)
DECLARE @spid NVARCHAR(6)
DECLARE @loginname NVARCHAR(100)
DECLARE @hostname NVARCHAR(100)

SET @data = EVENTDATA()
SET @cmd = @data.value('(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]',
'NVARCHAR(1000)')
SET @cmd = LTRIM(RTRIM(REPLACE(@cmd,
'
'
, '')))
SET @posttime = @data.value('(/EVENT_INSTANCE/PostTime)[1]',
'NVARCHAR(24)')
SET @spid = @data.value('(/EVENT_INSTANCE/SPID)[1]', 'nvarchar(6)')
SET @loginname = @data.value('(/EVENT_INSTANCE/LoginName)[1]',
'NVARCHAR(100)')
SET @hostname = HOST_NAME()

INSERT INTO dbo.AuditLog(Command, PostTime,HostName,LoginName)
VALUES(@cmd, @posttime, @hostname, @loginname)
SELECT @data

GO

-- perform DDL operations
UPDATE STATISTICS Production.Product
GO
CREATE TABLE dbo.Test(col INT)
GO
DROP TABLE dbo.Test
GO

-- View log table
SELECT *
FROM dbo.AuditLog
GO

DELETE [AuditLog]
WHERE ID = 4
-- clean up
DROP TRIGGER Audit
ON DATABASE

DROP TABLE dbo.AuditLog
GO

--************************************************************
--SQL Audit Demo
--************************************************************

USE master;
GO

--Create a server audit for the application. Since the QUEUE_DELAY is not specified
--The default is 1000ms or one minute and if failure occurs the SQL serveice will
--continue to run as the default parameter for ON_FAILURE is continue
CREATE SERVER AUDIT Log_Audit
TO APPLICATION_LOG;
GO

--Create a server audit for a file. QUEUE_DELAY 0 so the audit is synchronous
--ON_FAILURE is set to fail so if a failure occurs the sql service will shutdown
CREATE SERVER AUDIT File_Audit
TO FILE(FILEPATH='C:\AUDIT\')
WITH(ON_FAILURE=SHUTDOWN,
QUEUE_DELAY = 0);
GO

--Create a server audit for the application that will be used to record audit level events.
--Since the QUEUE_DELAY is not specified the default is 1000ms or one minute
--and if failure occurs the SQL service will
--continue to run as the default parameter for ON_FAILURE is continue
--CREATE SERVER AUDIT Audit_Audit
--TO APPLICATION_LOG;
--GO

--The audits are available in the sys.server_audits catalog view
SELECT *
FROM sys.server_audits;
GO

--The aduits are not enabled/on so not visible in the extended events DMV sys.dm_xe_session_targets view
SELECT *
FROM sys.dm_xe_session_targets

SELECT *
FROM sys.dm_xe_packages

--Enable the server audits
ALTER SERVER AUDIT Log_Audit
WITH (STATE=ON)
GO

ALTER SERVER AUDIT File_Audit
WITH (STATE=ON)
GO

--ALTER SERVER AUDIT Audit_Audit
--WITH (STATE=ON)
--GO

--************************************************************
--View the application log and two entries show the audit states
--were enabled since this is intrinsically audited
--************************************************************

--The audits are now visible in the extended events DMV
SELECT *
FROM sys.dm_xe_session_targets dt

--Create a database audit specification for any SELECT or INSERT
--statement on the Person.Person by a member of dbo and any SELECT
--statement on HumanResources.Employee by anyone
USE AdventureWorks2008;
GO

CREATE DATABASE AUDIT SPECIFICATION Person_Employee
FOR SERVER AUDIT File_Audit
ADD(SELECT, INSERT ON Person.Person BY dbo),
ADD(SELECT ON HumanResources.Employee BY public)
WITH (STATE = ON)
GO

--Run SELECT queries against the objects that are being audited
SELECT *
FROM Person.Person;
GO

SELECT *
FROM Person.Person
WHERE LastName = 'Duffy';
GO

SELECT JobTitle,
'David Rocks!!'
FROM HumanResources.Employee;
GO

--Using the sys.fn_get_audit_file function review the audit file
SELECT event_time,
action_id,
session_server_principal_name,
statement
FROM sys.fn_get_audit_file('C:\Audit\*', DEFAULT, DEFAULT);
GO

SELECT *
FROM sys.fn_get_audit_file('C:\Audit\*', DEFAULT, DEFAULT);
GO

--Since the server audit is being used by another audit specification
--this will fail
CREATE DATABASE AUDIT SPECIFICATION Wont_Work_Dude
FOR SERVER AUDIT File_Audit
ADD(SELECT, INSERT ON Person.Person BY dbo),
ADD(SELECT ON HumanResources.Employee BY public)
WITH (STATE = ON)
GO

--******************************************************************************************
--Stop the SQL service and delete the C:\Audit folder and attempt restart. The service
--Will fail to restart since the log can not be written to
--******************************************************************************************

--CREATE SERVER AUDIT SPECIFICATION Audit_Specification
--FOR SERVER AUDIT Audit_Audit
-- ADD (ALTER_AUDIT_GROUP);
--GO

--******************************************************************************************
--
Stop the Database audit specification and then the server audit
--Veiw the c:\Audit folder and note that the audit files still exist
--******************************************************************************************

USE AdventureWorks2008;
GO

ALTER DATABASE AUDIT SPECIFICATION Person_Employee
WITH (STATE=OFF);
GO

DROP DATABASE AUDIT SPECIFICATION Person_Employee;
GO

USE master;
GO

ALTER SERVER AUDIT Log_Audit
WITH (STATE=OFF)
GO

ALTER SERVER AUDIT File_Audit
WITH (STATE=OFF)
GO

DROP SERVER AUDIT Log_Audit;
GO

DROP SERVER AUDIT File_Audit;
GO

--Alter the AuditLogging specification turning the state off and drop it
USE [master]

GO

ALTER SERVER AUDIT SPECIFICATION AuditLogging
WITH (STATE=OFF);
GO

DROP SERVER AUDIT SPECIFICATION AuditLogging;
GO

ALTER SERVER AUDIT Audit_Audit
WITH (STATE=OFF)
GO

DROP SERVER AUDIT Audit_Audit;
GO

Tuesday, January 11, 2011

T-SQL Tuesday 14: Techie Resolutions

T-SQL Tuesday #14 Resolutions

Time for T-SQL Tuesday!

This months event is hosted by Jen McCown who has chosen the topic of “techie resolutions”. 

I hate to make overly general statements, but I can wrap this up in two words….  Catch Up!!!

I have spent much of the past year putting out fires with existing implementations and projects, and as such have not had adequate time to catch up with new technology.  A new project is promising to begin in the near future will utilize SQL 08R2 and will take full advantage of many of its new features and functionality.

SSRS and Mapping

One of the more interesting facets of this project is to utilize SSRS to create reports that will take full advantage of the geo spatial data that we have collected.  I have limited experience working with geo spatial data types in SQL, limited being lab and classroom only, and am excited to begin putting out this data to use in distributed reports. 

PowerPivot

I am very excited about this technology and providing our data in a familiar tool, excel, to the masses.

PowerShell

Working with PowerShell has been on my “Some day” list.  Over the past few years the movement and reliance on this technology is obvious and I can’t afford to procrastinate any longer.

Denali

During the PASS conference I had the time to install and work with SQL 11, Denali.  Unfortunately I was dragged back into the cruel reality of work upon my return home and have had little time to explore since.  I am committed to tinkering with the CTP before it blossoms into a release to market and I am once again struggling to catch up.

I have so many more goals, Ohio State winning a national championship, the TB Bucs making it back to the playoffs, the Magic topping the Southeastern and surpassing the Heat, but I’ll save these for later.

Tuesday, January 4, 2011

SQL Saturday Tampa

The schedule is up here for SQL Saturday #62 in Tampa!  Sessions begin at 8:30 and continue on through 5, and there is still room for the “Day of Data”.