A blog about SQL Server, SSIS, C# and whatever else I happen to be dealing with in my professional life.

Find ramblings

Showing posts with label SQL Saturday 53. Show all posts
Showing posts with label SQL Saturday 53. Show all posts

Friday, October 1, 2010

SQL Saturday 53 Riverport directions

The exit for Riverport is an outer road that runs parallel to the Missouri River and winds past a grain silo right after you turn onto it. Do not fear, you are not lost.

Riverport is connected to a covered parking garage by an enclosed walkway. Please park on the 3rd level of the parking garage and take the walkway across to the facility. Go down the stairs to the Main Street Landing.

Directions Riverport Landing 6711 N.E. Birmingham Kansas City, MO 64117
FROM I-70 WEST
I-70 East (to Kansas City) to I-35 North
Take the Armour Road East (HWY 210) Exit
(You will pass Cerner on your left)
Follow Armour Road East to Searcy Creek Parkway.
Exit and continue East on Frontage Road/Birmingham Road.
Continue East through the winding right and left turns, maintaining a parallel orientation to Hwy 210.
After passing under the Riverfront Campus walkway, turn left and enter the garage.

FROM I-70 EAST
I-70 West (to Kansas City) to I-435 North
Get on 210 West (Armour Road)
Follow Armour Road West to Searcy Creek Parkway.
Exit and go East on Frontage Road/Birmingham Road.
Continue East through the winding right and left turns, maintaining a parallel orientation to Hwy 210.
After passing under the Riverfront Campus walkway, turn left and enter the garage.

FROM I-35 NORTH
I-35 South to Armour Road East (HWY 210)
(You will pass Cerner on your left)
Follow Armour Road East to Searcy Creek Parkway.
Exit and continue East on Frontage Road/Birmingham Road.
Continue East through the winding right and left turns, maintaining a parallel orientation to Hwy 210.
After passing under the Riverfront Campus walkway, turn left and enter the garage.

FROM I-35 SOUTH
I-35 North to Armour Road East (HWY 210)
(You will pass Cerner on your left)
Follow Armour Road East to Searcy Creek Parkway.
Exit and continue East on Frontage Road/Birmingham Road.
Continue East through the winding right and left turns, maintaining a parallel orientation to Hwy 210.
After passing under the Riverfront Campus walkway, turn left and enter the garage.

This is just a quick photo display of the area surrounding the event site for SQL Saturday 53 so that you don't think you're lost when trying to find the event. http://picasaweb.google.com/bill.fellows/SQLSaturday53#





Sunday, August 22, 2010

SQL Server 2005/2008 what's new, part 7

SQL Saturday 53, takes place in 41 days. I will presenting 45 new TSQL features in 45 minutes which will be a purely demo driven presentation designed to give the audience a taste of what's out there and serve a springboard for them to explore on their own. Sign up now for this great opportunity for free SQL Server training in Kansas City, MO.

TableSample

Tablesample is a hint to SQL Server to just grab some rows. Which rows? If you're asking "which rows", tablesample isn't for you. No, tablesample is a hint to SQL Server that you're in a lascivious mood and you just want some data now. It's clean data, no dirty reads but you might get some really odd, inconsistent results if you try to do things like apply filtering. Best use I've found so far is quasi-psuedo-random sampling of data. It's not random enough for true random so don't treat it as such but if you just want a drink of the data in the table, it's probably "good enough," assuming you remember the syntax.

The TABLESAMPLE clause is a different sort of animal. Conceptually, one may think it's nothing more than an ugly synonym for TOP or LIMIT, if you're using a different RDBMS vendor. However, from a query processing perspective, TOP can't run until after the query has been run through the wringer which for large tables might negate any performance savings. At least based on my chapter 1 understanding of logical query processing. It's an odd duck because it either includes a page of information or it doesn't. The query plan will always indicate it's a table scan but it will only read from disk if the selected pages, something something. At this point, read the BOL article above because we are unfortunately way beyond my SQL Server knowledge zone.

Calls to tablesample can either be deterministic or non-deterministic based on whether the REPEATABLE option is used. Activity in the database that affects data pages may also affect the deterministic-ness of your REPEATABLE option. These activities include "inserting, updating, deleting, index rebuilding, index defragmenting, restoring a database, and attaching a database" Consult your DBA if you experience eye watering, mouth watering, flower watering or if your queries last for more than 4 hours.

Syntax

TABLESAMPLE is a clause in your FROM statement. From BOL, the syntax is TABLESAMPLE [SYSTEM] (sample_number [ PERCENT | ROWS ])[REPEATABLE (repeat_seed)]. As tablesample requires real tables, none of the jiggered up examples I have used thus far will work. Rather than forcing you to have adventureworks installed, I figured querying against master.dbo.spt_values seemed a safe table to assume will exist on your machine. What I wasn't ready for, was seeing the caveat about pages of data and maybe you won't get anything back. The other surprise was just how variable "10 rows" can be in result sets. In the handful of times I reran, I saw sets ranging from empty, to 47 to 149. Changing it from rows to percent didn't really dial back the variability like I'd have thought based on this statement in BOL "When a number of rows is specified, instead of a percentage based on the total number of rows in the table, that number is converted into a percentage of the rows and, therefore, pages that should be returned. The TABLESAMPLE operation is then performed with that computed percentage." I still saw some pretty big swings from 0 to 61 to 358 rows.
SELECT COUNT(1) rc FROM master.dbo.spt_values T
rc
2508
SELECT * FROM master.dbo.spt_values T tablesample (10 rows)

Invocation 1

namenumbertypelowhighstatus

Invocation 2

namenumbertypelowhighstatus
ROUTE21586EOBNULLNULL0
STATISTICS21587EOBNULLNULL0
ASYMMETRIC KEY USER21825EOBNULLNULL0
CERTIFICATE USER21827EOBNULLNULL0
GROUP USER21831EOBNULLNULL0
SQL USER21843EOBNULLNULL0
WINDOWS USER21847EOBNULLNULL0
SERVICE22099EOBNULLNULL0
INDEX22601EOBNULLNULL0
LOGIN22604EOBNULLNULL0
XML SCHEMA COLLECTION22611EOBNULLNULL0
TYPE22868EOBNULLNULL0
SERVER AUDIT8257EODNULLNULL0
CHECK CONSTRAINT8259EODNULLNULL0
DEFAULT8260EODNULLNULL0
FOREIGN KEY CONSTRAINT8262EODNULLNULL0
STORED PROCEDURE8272EODNULLNULL0
RULE8274EODNULLNULL0
TABLE SYSTEM8275EODNULLNULL0
TRIGGER SERVER8276EODNULLNULL0
TABLE8277EODNULLNULL0
VIEW8278EODNULLNULL0
STORED PROCEDURE EXTENDED8280EODNULLNULL0
DATABASE AUDIT SPECIFICATION16708EODNULLNULL0
SERVER AUDIT SPECIFICATION16723EODNULLNULL0
TRIGGER ASSEMBLY16724EODNULLNULL0
DATABASE16964EODNULLNULL0
OBJECT16975EODNULLNULL0
FULLTEXT CATALOG17222EODNULLNULL0
STORED PROCEDURE ASSEMBLY17232EODNULLNULL0
SCHEMA17235EODNULLNULL0
CREDENTIAL17475EODNULLNULL0
EVENT NOTIFICATION SERVER17491EODNULLNULL0
EVENT SESSION17747EODNULLNULL0
AGGREGATE17985EODNULLNULL0
FUNCTION TABLE-VALUED INLINE SQL17993EODNULLNULL0
PARTITION FUNCTION18000EODNULLNULL0
STORED PROCEDURE REPLICATION FILTER18002EODNULLNULL0
FUNCTION TABLE-VALUED SQL18004EODNULLNULL0
RESOURCE GOVERNOR18258EODNULLNULL0
SERVER ROLE18259EODNULLNULL0
WINDOWS GROUP18263EODNULLNULL0
ASYMMETRIC KEY19265EODNULLNULL0
DATABASE ENCRYPTION KEY19268EODNULLNULL0
MASTER KEY19277EODNULLNULL0
PRIMARY KEY19280EODNULLNULL0
SYMMETRIC KEY19283EODNULLNULL0

What to notice

The rows or percentage supplied to TABLESAMPLE has some fairly wild variability based on table width and how it physically got laid onto disk. If you need to provide an upper bound to that variability, use tablesample with TOP. Don't trust REPEATABLE unless you are working against a read-only database.

Availability

SQL Server 2005+

Saturday, August 21, 2010

SQL Server 2005/2008 what's new, part 6

SQL Saturday 53, takes place in 42 days. I will presenting 45 new TSQL features in 45 minutes which will be a purely demo driven presentation designed to give the audience a taste of what's out there and serve a springboard for them to explore on their own. Sign up now for this great opportunity for free SQL Server training in Kansas City, MO.

NTILE()

The NTILE function is the fourth of four windowing functions introduced in SQL Server 2005. NTILE takes a different approach to paritioning data. ROW_NUMBER, RANK and DENSE_RANK will generate variable sized buckets of data based on the partition key(s). NTILE attempts to split the data into equal, fixed size buckets. BOL has a comprehensive page comparing the ranking functions if you want a quick visual reference on their effects.

Syntax

The syntax for NTILE differs slightly from the other window functions. It's NTILE(@BUCKET_COUNT) OVER ([PARTITION BY _] ORDER BY _) , where @BUCKET_COUNT is a positive integer or bigint value. Using a fast number generator calendar, I added a call to NTILE() based on event date to introduce a rank column.
-- Example of using NTILE to split 30 numbers
-- into 10 and 4 equal sized buckets
SELECT
    GN.number
,   NTILE(10) OVER (ORDER BY GN.number) AS thirds
,   NTILE(4) OVER (ORDER BY GN.number) AS quartile
FROM 
    dbo.GenerateNumbers(30) GN
numberthirdsquartile
111
211
311
421
521
621
731
831
932
1042
1142
1242
1352
1452
1552
1662
1763
1863
1973
2073
2173
2283
2383
2484
2594
2694
2794
28104
29104
30104

What to notice

The, backwardsly named, thirds column was evenly partitioned with the NTILE function. As expected, ten buckets were created and each hold 3 sets of numbers (3 * 10 = 30). Quartile column as expected was 4 buckets of 8, or is it 4 buckets of 7? 4 * 8 = 32; 4 * 7 = 28. Neither is the correct answer. In cases where the rows can't be broken out into even buckets, NTILE will fill from the top down so there is at most a 1 count difference between the first and last bucket.

Availability

SQL Server 2005+

Friday, August 20, 2010

SQL Server 2005/2008 what's new, part 5

SQL Saturday 53, takes place in 43 days. I will presenting 45 new TSQL features in 45 minutes which will be a purely demo driven presentation designed to give the audience a taste of what's out there and serve a springboard for them to explore on their own. Sign up now for this great opportunity for free SQL Server training in Kansas City, MO.

DENSE_RANK()

The DENSE_RANK function is the third of four windowing functions introduced in SQL Server 2005. DENSE_RANK is nearly identical to RANK except that in the case of a tie or duplicate values, DENSE_RANK will leave no gaps in numbering.

Syntax

The syntax for DENSE_RANK is identical to RANK(). It's DENSE_RANK() OVER (ORDER BY _) , parenthesis required. Using the upcoming SQL Saturday calendar, I added a call to DENSE_RANK() based on event date to introduce a rank column.
-- This query demonstrates the use of the DENSE_RANK function
-- Notice the ranks for rows with same key (event_date) are 
-- ranked equally and the subsequent rank number leaves
-- no gaps in the numbers.
;
WITH SQL_SATURDAY (event_date, event_name, event_location) AS
(
SELECT CAST('Aug 21, 2010' as datetime), 'SQLSaturday #51', 'Nashville 2010'
UNION ALL SELECT 'Sep 18, 2010', 'SQLSaturday #46', 'Raleigh 2010'
UNION ALL SELECT 'Sep 18, 2010', 'SQLSaturday #50', 'East Iowa 2010'
UNION ALL SELECT 'Sep 18, 2010', 'SQLSaturday #55', 'San Diego 2010'
UNION ALL SELECT 'Sep 25, 2010', 'SQLSaturday #52', 'Colorado 2010'
UNION ALL SELECT 'Oct 2, 2010', 'SQLSaturday #53', 'Kansas City 2010'
UNION ALL SELECT 'Oct 2, 2010', 'SQLSaturday #48', 'Columbia 2010'
UNION ALL SELECT 'Oct 16, 2010', 'SQLSaturday #49', 'Orlando 2010'
UNION ALL SELECT 'Oct 23, 2010', 'SQLSaturday #54', 'Salt Lake City 2010'
UNION ALL SELECT 'Oct 23, 2010', 'SQLSaturday #56', 'Dallas (BI Edition) 2010'
UNION ALL SELECT 'Oct 29, 2010', 'SQLSaturday #58', 'Minnesota 2010'
UNION ALL SELECT 'Nov 20, 2010', 'SQLSaturday #59', 'New York City 2010'
UNION ALL SELECT 'Jan 22, 2011', 'SQLSaturday #45', 'Louisville 2011'
UNION ALL SELECT 'Jan 29, 2011', 'SQLSaturday #57', 'Houston 2011'
UNION ALL SELECT 'Feb 5, 2011', 'SQLSaturday #47', 'Phoenix 2011'
)
SELECT
    SS.* 
,   DENSE_RANK() OVER (ORDER BY SS.event_date ASC) AS zee_dense_rank
FROM 
    SQL_SATURDAY SS
event_dateevent_nameevent_locationzee_dense_rank
2010-08-21SQLSaturday #51Nashville 20101
2010-09-18SQLSaturday #50East Iowa 20102
2010-09-18SQLSaturday #55San Diego 20102
2010-09-18SQLSaturday #46Raleigh 20102
2010-09-25SQLSaturday #52Colorado 20103
2010-10-02SQLSaturday #48Columbia 20104
2010-10-02SQLSaturday #53Kansas City 20104
2010-10-16SQLSaturday #49Orlando 20105
2010-10-23SQLSaturday #56Dallas (BI Edition) 20106
2010-10-23SQLSaturday #54Salt Lake City 20106
2010-10-29SQLSaturday #58Minnesota 20107
2010-11-20SQLSaturday #59New York City 20108
2011-01-22SQLSaturday #45Louisville 20119
2011-01-29SQLSaturday #57Houston 201110
2011-02-05SQLSaturday #47Phoenix 201111

What to notice

I created a new column called "zee_dense_rank" with the invocation of the DENSE_RANK function. Because I ordered it by dates, and we have 3 events all on 2010-09-18, the rank starts at 1 for Nashville represent! East Iowa, San Diego and Raleigh battled it out for supremacy on 2010-09-18 and it was a three way tie at 2 which resulted in Colorado being ranked 3rd versus 5th if RANK had been used.

Availability

SQL Server 2005+

Thursday, August 19, 2010

SQL Server 2005/2008 what's new, part 4

SQL Saturday 53, takes place in 44 days. I will presenting 45 new TSQL features in 45 minutes which will be a purely demo driven presentation designed to give the audience a taste of what's out there and serve a springboard for them to explore on their own. Sign up now for this great opportunity for free SQL Server training in Kansas City, MO.

RANK()

The RANK function is the second of four windowing functions introduced in SQL Server 2005. RANK is nearly identical to ROW_NUMBER except that in the case of a tie or duplicate values, ROW_NUMBER will select one as coming first. RANK is a New Age, granola loving function that says "you're all winners in my book" and gives all the matches the same value. RANK keeps track of how many ties for Nth place occur and adjusts the next number accordingly.

Syntax

The syntax for RANK is identical to ROW_NUMBER(). It's RANK() OVER (ORDER BY _) , parenthesis required. Using the upcoming SQL Saturday calendar, I added a call to RANK() based on event date to introduce a rank column.
-- This query demonstrates the use of the RANK function
-- Notice the ranks for rows with same key (event_date) are 
-- ranked equally and the subsequent rank number behaves
-- like an identity insert that was rolled back, i.e. gaps
-- in the meaningless identifiers that someone (QA) will question
;
WITH SQL_SATURDAY (event_date, event_name, event_location) AS
(
SELECT CAST('Aug 21, 2010' as datetime), 'SQLSaturday #51', 'Nashville 2010'
UNION ALL SELECT 'Sep 18, 2010', 'SQLSaturday #46', 'Raleigh 2010'
UNION ALL SELECT 'Sep 18, 2010', 'SQLSaturday #50', 'East Iowa 2010'
UNION ALL SELECT 'Sep 18, 2010', 'SQLSaturday #55', 'San Diego 2010'
UNION ALL SELECT 'Sep 25, 2010', 'SQLSaturday #52', 'Colorado 2010'
UNION ALL SELECT 'Oct 2, 2010', 'SQLSaturday #53', 'Kansas City 2010'
UNION ALL SELECT 'Oct 2, 2010', 'SQLSaturday #48', 'Columbia 2010'
UNION ALL SELECT 'Oct 16, 2010', 'SQLSaturday #49', 'Orlando 2010'
UNION ALL SELECT 'Oct 23, 2010', 'SQLSaturday #54', 'Salt Lake City 2010'
UNION ALL SELECT 'Oct 23, 2010', 'SQLSaturday #56', 'Dallas (BI Edition) 2010'
UNION ALL SELECT 'Oct 29, 2010', 'SQLSaturday #58', 'Minnesota 2010'
UNION ALL SELECT 'Nov 20, 2010', 'SQLSaturday #59', 'New York City 2010'
UNION ALL SELECT 'Jan 22, 2011', 'SQLSaturday #45', 'Louisville 2011'
UNION ALL SELECT 'Jan 29, 2011', 'SQLSaturday #57', 'Houston 2011'
UNION ALL SELECT 'Feb 5, 2011', 'SQLSaturday #47', 'Phoenix 2011'
)
SELECT
    SS.* 
,   RANK() OVER (ORDER BY SS.event_date ASC) AS zee_rank
FROM 
    SQL_SATURDAY SS
event_dateevent_nameevent_locationzee_rank
2010-08-21SQLSaturday #51Nashville 20101
2010-09-18SQLSaturday #50East Iowa 20102
2010-09-18SQLSaturday #55San Diego 20102
2010-09-18SQLSaturday #46Raleigh 20102
2010-09-25SQLSaturday #52Colorado 20105
2010-10-02SQLSaturday #48Columbia 20106
2010-10-02SQLSaturday #53Kansas City 20106
2010-10-16SQLSaturday #49Orlando 20108
2010-10-23SQLSaturday #56Dallas (BI Edition) 20109
2010-10-23SQLSaturday #54Salt Lake City 20109
2010-10-29SQLSaturday #58Minnesota 201011
2010-11-20SQLSaturday #59New York City 201012
2011-01-22SQLSaturday #45Louisville 201113
2011-01-29SQLSaturday #57Houston 201114
2011-02-05SQLSaturday #47Phoenix 201115

What to notice

I created a new column called "zee_rank" with the invocation of the RANK function. Because I ordered it by dates, and we have 3 events all on 2010-09-18, the rank starts at 1 for Nashville represent! East Iowa, San Diego and Raleigh battled it out for supremacy on 2010-09-18 and it was a three way tie at 2 which resulted in Colorado being ranked 5th.

Availability

SQL Server 2005+

Wednesday, August 18, 2010

SQL Server 2005/2008 what's new, part 3

SQL Saturday 53, takes place in 45 days. I will presenting 45 new TSQL features in 45 minutes which will be a purely demo driven presentation designed to give the audience a taste of what's out there and serve a springboard for them to explore on their own. Sign up now for this great opportunity for free SQL Server training in Kansas City, MO.

ROW_NUMBER()

The ROW_NUMBER function is one of four windowing functions introduced in SQL Server 2005. ROW_NUMBER() is a monotomically increasing function for each partition within a query. That's a fancy way of saying starting at 1, add 1 for every row you encounter. The partition is simply the signal to start counting over.

Syntax

The syntax simple, it's ROW_NUMBER() OVER (ORDER BY _) , parenthesis required. Using the query from the CTE introduction, I added a call to row_number() to introduce a sequential row number column.
-- This query demonstrates the ROW_NUMBER function 
-- along with partitioning
;
WITH BORDER_STATES (state_name, abbreviation) AS
(
    SELECT 'IOWA', 'IA'
    UNION ALL SELECT 'NEBRASKA', 'NE'
    UNION ALL SELECT 'OKLAHOMA', 'OK'
    UNION ALL SELECT 'KANSAS', 'KS'
    UNION ALL SELECT 'ILLINOIS', 'IL'
    UNION ALL SELECT 'KENTUCKY', 'KY'
    UNION ALL SELECT 'TENNESSEE', 'TN'
)
, BEST_STATE AS
(
    SELECT 'MISSOURI' AS state_name, 'MO' AS state_abbreviation
)
, JOINED AS
(
    SELECT M.*, 1 AS state_rank FROM BEST_STATE M
    UNION
    SELECT BS.*, 2 AS state_rank FROM BORDER_STATES BS
)
SELECT J.*, ROW_NUMBER() OVER (PARTITION BY J.state_rank ORDER BY J.state_rank) AS zee_row_number
FROM JOINED J
state_namestate_abbreviationstate_rankzee_row_number
MISSOURIMO11
IOWAIA21
NEBRASKANE22
OKLAHOMAOK23
KANSASKS24
ILLINOISIL25
KENTUCKYKY26
TENNESSEETN27

What to notice

I created a new column called "zee_row_number" with the invocation of the ROW_NUMBER function. I partitioned my results on the state_rank which lead to my data being segmented into two sets, state_rank 1 and 2. Missouri being the only element in its set is assigned zee_row_number of 1. Within the second set, Iowa was selected as the first element of that set. Your mileage may vary. After that, the other 6 rows had their zee_row_number incremented by 1.

Availability

SQL Server 2005+

Tuesday, August 17, 2010

SQL Server 2005/2008 what's new part 2

SQL Saturday 53, takes place in 46 days. I will presenting 45 new TSQL features in 45 minutes which will be a purely demo driven presentation designed to give the audience a taste of what's out there and serve a springboard for them to explore on their own. Sign up now for this great opportunity for free SQL Server training in Kansas City, MO.

Recursive Common Table Expressions

Recursive CTEs are like a normal Common Table Expressions, just with a little something extra to them. By default, you can have 100 levels of recursion before it chokes. Vast improvement over the 32 levels of default recursion allowed with procedure, function, trigger or nested views.

Syntax

The syntax is quite simple, see books online for the BNF. The following contrived queries count down from 100 and 101 to 0, endpoints inclusive.
-- This query demonstrates recursion by counting down from 100 to 0
;
WITH BASE AS
(
    -- Anchor query
    SELECT 100 AS anchor
    
    -- recursive query
    -- notice that I can reference BASE
    UNION ALL
    SELECT B.anchor -1 FROM BASE B WHERE B.anchor > 0
    
)
SELECT B.* FROM BASE B

-- This query demonstrates recursion by counting down from 101 to 0
-- The MAXRECURSION hint allows us to override the default for good or ill
;
WITH BASE AS
(
    -- Anchor query
    SELECT 101 AS anchor
    
    -- recursive query
    -- notice that I can reference BASE
    UNION ALL
    SELECT B.anchor -1 FROM BASE B WHERE B.anchor > 0
    
)
SELECT B.* 
FROM BASE B 
OPTION (MAXRECURSION 101)

What to notice

Recursive queries can be split into their non-recursive or anchor portion and the recursive portion. The anchor query which can be as complex as need be but it must is evaluated before the recursive portion begins. The recursive will execute until it either overflows the stack or returns an empty set a.k.a. meets the terminal condition. The default maximum recursion level for a CTE is 100 frames. The absolute maximum level of CTE recursion is 32,767 . If you're hitting this limit, congratulations. Now, rethink your query.

Availability

SQL Server 2005+

Monday, August 16, 2010

SQL Server 2005/2008 what's new, part 1

SQL Saturday 53, takes place in 47 days. I will be presenting 45 new TSQL features in 45 minutes which will be a purely demo driven presentation designed to give the audience a taste of what's out there and serve a springboard for them to explore on their own. Sign up now for this great opportunity for free SQL Server training in Kansas City, MO.

Common Table Expressions

I like to come out swinging and Common Table Expressions, CTE, introduced with SQL Server 2005 are worth the price of admission! At their simplest, they're nothing more than syntactic sugar but don't dismiss them as such. There is very little you can do with CTEs that you couldn't do with temporary tables, derived tables, table variables and what have you. The biggest difference developing queries using CTEs versus the Tumbling data anti-pattern is that with a Common Table Expression the compiler can have a chance to optimize your query despite being separated into logic sub tables. Think of them as single use tables with no explicit cleanup required.

Syntax

The syntax is quite simple, see books online for the BNF. The following contrived query builds up data to define the states that border Missouri.
;
WITH BORDER_STATES (state_name, abbreviation) AS
(
    SELECT 'IOWA', 'IA'
    UNION ALL SELECT 'NEBRASKA', 'NE'
    UNION ALL SELECT 'OKLAHOMA', 'OK'
    UNION ALL SELECT 'KANSAS', 'KS'
    UNION ALL SELECT 'ILLINOIS', 'IL'
    UNION ALL SELECT 'KENTUCKY', 'KY'
    UNION ALL SELECT 'TENNESSEE', 'TN'
)
, BEST_STATE AS
(
    SELECT 'MISSOURI' AS state_name, 'MO' AS state_abbreviation
)
, JOINED AS
(
    SELECT M.*, 1 AS state_rank FROM BEST_STATE M
    UNION
    SELECT BS.*, 2 AS state_rank FROM BORDER_STATES BS
)
SELECT * FROM JOINED

What to notice

As a helpful suggestion, preface all your CTEs with a semi-colon on the preceding line. If they are the only statement in a batch, then it doesn't matter. Otherwise, you'll encounter "Incorrect syntax near the keyword 'with'. If this statement is a common table expression, an xmlnamespaces clause or a change tracking context clause, the previous statement must be terminated with a semicolon." BORDER_STATES is an in memory table containing the 8 states that directly border Missouri. Notice that I explicitly specify the column names when I defined the table. BEST_STATE is also an in memory table but here I chose to define the column names within the query. JOINED shows I can reference the tables immediately after defining them. Finally, I reference JOINED from outside the CTE by grabbing all of the data.

What's better, it depends. Besides that hoary chestnut, I find myself defining columns if I am building up some static data values like I do in BORDER_STATES. Otherwise, I typically just alias my columns as I query them like I do with BEST_STATE.

Finally, you should notice that the use of SELECT * leads to puppies being slaughtered. I read it on a blog so it must be true.

Availability

SQL Server 2005+