Login or Sign Up to become a member!
LessThanDot Sit Logo

LessThanDot

Data Management

Less Than Dot is a community of passionate IT professionals and enthusiasts dedicated to sharing technical knowledge, experience, and assistance. Inside you will find reference materials, interesting technical discussions, and expert tips and commentary. Once you register for an account you will have immediate access to the forums and all past articles and commentaries.

LTD Social Sitings

Lessthandot twitter Lessthandot Linkedin Lessthandot friendfeed Lessthandot facebook Lessthandot rss

Note: Watch for social icons on posts by your favorite authors to follow their postings on these and other social sites.

Your profile

    Search

    XML Feeds

    Google Ads

    « Using T-SQL OUTPUT and MERGE To Link Old and New KeysHow much longer will the SQL Server database restore take »
    comments

    Someone asked two interesting questions last night

    1) Is there a way to construct an empty CTE?
    2) Can a CTE be modified? Can I insert rows after it has been constructed?

    Let's look at question number one first. Is there a way to construct an empty CTE? Yes, there is, but why would you want to do this? Anyway, here is one way of having an empty CTE

    1. WITH Hello
    2. AS (
    3. SELECT  name
    4. FROM sysobjects WHERE 1 =0
    5. )
    6. SELECT * FROM Hello

    Question number two (Can a CTE be modified? Can I insert rows after it has been constructed?) is more interesting, first let's look at a simple example and then we will look at something that makes more sense

    We need to create a table first with 1 row

    1. CREATE TABLE testNow(id int)
    2. INSERT testNow VALUES(1)

    Now run the following which will run an update statement against the CTE

    1. ;WITH Hello
    2. AS (
    3. SELECT id FROM testNow
    4.  
    5. )
    6.  
    7. UPDATE Hello SET id = 2

    If we check the table now, we can see that the value changed from 1 to 2

    1. SELECT * FROM testNow

    What about inserts, can we insert into a common table expression? Sure, let's take a look how we can do that.

    1. ;WITH Hello
    2. AS (
    3. SELECT id FROM testNow
    4.  
    5. )
    6.  
    7. INSERT Hello VALUES( 3 )

    If we check the table now, we will see two rows in the table

    1. SELECT * FROM testNow

    So this is all, but what is the point? Really for the examples I gave there is none, you can just update the table directly instead. But, let's look at a more interesting example

    First we need to create the following table

    1. CREATE TABLE SomeTable(id int,SomeVal char(1),SomeDate date)
    2. INSERT SomeTable
    3. SELECT null,'A','20110101'
    4. UNION ALL
    5. SELECT null,'A','20100101'
    6. UNION ALL
    7. SELECT null,'A','20090101'
    8. UNION ALL
    9. SELECT null,'B','20110101'
    10. UNION ALL
    11. SELECT null,'B','20100101'
    12. UNION ALL
    13. SELECT null,'C','20110101'
    14. UNION ALL
    15. SELECT null,'C','20100101'
    16. UNION ALL
    17. SELECT null,'C','20090101'

    Now run a simple select statement

    1. SELECT * FROM SomeTable

    Here is what is in that table

    id	SomeVal	SomeDate
    NULL	A	2011-01-01
    NULL	A	2010-01-01
    NULL	A	2009-01-01
    NULL	B	2011-01-01
    NULL	B	2010-01-01
    NULL	C	2011-01-01
    NULL	C	2010-01-01
    NULL	C	2009-01-01

    Now I would like to set the id to 1 for each distinct SomeVal where the date is the max date and to 0 otherwise
    So my table would look like this

    id	SomeVal	SomeDate
    1	A	2011-01-01
    0	A	2010-01-01
    0	A	2009-01-01
    1	B	2011-01-01
    0	B	2010-01-01
    1	C	2011-01-01
    0	C	2010-01-01
    0	C	2009-01-01

    First let's do the select statement, in order to accomplish what we set out to do we can use the row_number function, partition by SomeVal and order by SomeDate descending

    Run the following statement

    1. ;WITH cte AS(SELECT
    2.         ROW_NUMBER() OVER(PARTITION BY SomeVal
    3.                   ORDER BY SomeDate DESC) AS row,
    4.                 * FROM SomeTable)
    5. SELECT * FROM cte

    Here is our result

    row	id	SomeVal	SomeDate
    1	NULL	A	2011-01-01
    2	NULL	A	2010-01-01
    3	NULL	A	2009-01-01
    1	NULL	B	2011-01-01
    2	NULL	B	2010-01-01
    1	NULL	C	2011-01-01
    2	NULL	C	2010-01-01
    3	NULL	C	2009-01-01

    So where row is 1 we will make SomeVal 1 otherwise we will make it 0, we can do that with a simple case statement

    Here is what the update looks like

    1. ;WITH cte AS(SELECT
    2.         ROW_NUMBER() OVER(PARTITION BY SomeVal
    3.                   ORDER BY SomeDate DESC) AS row,
    4.                 * FROM SomeTable)
    5.  
    6. UPDATE cte
    7. SET id =  CASE WHEN row = 1 THEN 1 ELSE 0 END

    Now run the select against the table again

    1. SELECT * FROM SomeTable


    id	SomeVal	SomeDate
    1	A	2011-01-01
    0	A	2010-01-01
    0	A	2009-01-01
    1	B	2011-01-01
    0	B	2010-01-01
    1	C	2011-01-01
    0	C	2010-01-01
    0	C	2009-01-01

    And as you can see the id column was updated correctly.

    As you can see, using a common table expression can simplify your update statements.

    About the Author

    User bio imageDenis has been working with SQL Server since version 6.5. Although he worked as an ASP/JSP/ColdFusion developer before the dot com bust, he has been working exclusively as a database developer/architect since 2002. In addition to English, Denis is also fluent in Croatian and Dutch, but he can curse in many other languages and dialects (just ask the SQL optimizer) He lives in Princeton, NJ with his wife and three kids.
    Social SitingsTwitterFacebookLinkedInHomePageFlickrLTD RSS Feed
    1390 views
    Instapaper

    No feedback yet

    Leave a comment


    Your email address will not be revealed on this site.

    Your URL will be displayed.
    (Line breaks become <br />)
    (Name, email & website)
    (Allow users to contact you through a message form (your email will not be revealed.)