Tag: SQLNewBlogger

  • Getting the Previous Row Value before SQL Server 2012

    Another post for me that is simple and hopefully serves as an example for people trying to get blogging as #SQLNewBloggers.

    I ran across a post where someone that was trying to access the previous value in a table for some criteria. This is a common issue, and  one that’s very easily solved in SQL Server 2012+ with the windowing functions.

    However, what about in SQL Server 2008 R2-?

    NOTE: I’m solving this quickly, the way many people do, but this is an inefficient solution. I’ll show that in another post. However, I’m showing how you can describe and solve a problem here. If you need to solve this, look for a temp table solution (or find a later post from me).

    Setup

    It’s pretty easy. Let’s get some data together. I’ll use a big sample since that’s easier to see the differences.

    CREATE TABLE MyID
    ( myid INT
    , myvalue INT
    );
    GO
    INSERT MyID
    VALUES (1, 10 ),
            (1, 20 ),
            (2, 400),
            (2, 500),
            (2, 600),
            (3, 8000),
            (3, 9000),
            (3, 10000),
            (3, 11000);

    Now, what I want is something that returns the previous row, assuming we’re ordering by the ID and value. If there is no previous value, let’s return a zero. Essentially what we want is something like this:

    select MyID        , MyValue       , MyPrevValue = ISNULL( x, 0)
    from …

    That’s the pseudocode. Obviously I need to fill in blanks. However, let’s build a test. Why? Well, I can then see my result data, and I can re-run the test over and over as I experiment with the query. It’s not hard, I promise.

    EXEC tsqlt.NewTestClass
      @ClassName = N'WindowTests';
      go
    CREATE PROCEDURE [WindowTests].[test check the previous row value for MyID]
    AS
    BEGIN
    -- assemble
    CREATE TABLE #expected (id INT, myvalue INT, PrevValue int) INSERT #expected
    VALUES (1, 10  , 0  ),
            (1, 20  , 10 ),
            (2, 400 , 20 ),
            (2, 500 , 400),
            (2, 600 , 500),
            (3, 8000 , 600),
            (3, 9000 , 8000),
            (3, 10000 , 9000),
            (3, 11000 , 10000) SELECT *
    INTO #actual
      FROM #expected AS e
      WHERE 1 = 0 -- act
    INSERT #actual
    EXEC dummyquery;
    -- assert
    EXEC tsqlt.AssertEqualsTable
      @Expected = N'#expected'
    , @Actual = N'#actual'
    , @FailMsg = N'Incorrect query' END

    If you examine the test, you’ll see that I create a table, insert the results I expect, and then call some procedure. I compare the results of the procedure with the table I built.

    That’s it. A simple test, but I’ll let the computer compare the result sets rather than trusting my eyes.

    Last thing, I’ll build my dummy procedure, which can look like this:

    CREATE PROCEDURE dummyquery
    -- alter procedure dummyquery
    AS
    BEGIN select MyID   , MyValue , PrevValue = MyValue from MyID
      END

    Now I have the outline of what I need. If I run the test now, I’ll get this:

    2016-06-07 10_18_23-Photos

    The test output tells me it has failed, the values in the #expected table (with a <), and the values from my query in the #actual table (with a >).

    Now I can debug and work on this.

    Solving the Problem

    First, I want to order the data and get a number that counts the order. The ROW_NUMBER function does this, which is available in SQL Server 2005+. I won’t go into SQL 2000- solutions because, well they’re more complex and there should be very few SQL 2000 instances left coming up with new problems.

    I can do this with this code:

    2016-06-07 10_21_40-Photos

    Note that I have a sequential counter that lets me order every row with an index. Now, I can access the previous row, since I know the MyKey value will be one less than the current row.

    With this in mind, let’s turn this into a CTE (removing the previous value). Outside of the CTE, I’m going to self-join the CTE to itself. I’ll use a LEFT JOIN since not every row will have a previous row. In fact, the first row won’t.

    The join condition, which you can play with, will be on the outer table’s ID being one less than the first table’s key. You could reverse the math as well, but that’s up to you.

    2016-06-07 10_29_37-Photos

    One last issue. Add an ISNULL to the previous value to return a 0 if there is no match. Now, let’s run the test.

    2016-06-07 10_31_58-Photos

    SQLNewBlogger

    This was a slightly longer post, where I tried to explain how I setup the problem and solved it. I included a test, which didn’t add much coding time. In fact, the writing took far longer than the coding itself.

    This is the type of problem I’d encourage you to solve on your blog. If you want to repeat this, look for a solution with temp tables, as the CTE incurs a lot of reads. This isn’t really what you’d like to do in production code.

  • When were statistics updated?–#SQLNewBlogger

    Another post for me that is simple and hopefully serves as an example for people trying to get blogging as #SQLNewBloggers.

    I ran across the STATS_DATE function recently, and it’s one that I hadn’t used in production code. I’m not sure how this escaped me, as it was added in SQL Server 2008, but I rarely see it written about, so it’s not just me.

    This function takes an object_id and a stats_id, and returns the date the statistics were last updated. The statistics id is the id from sys.stats and doesn’t necessarily correspond to the index ID.

    As a quick example, if you look at the Sales.SalesOrderHeader table in AdventureWorks2012, you can run this:

    SELECT STATS_DATE ( 1266103551 , 2) 

    This should return a simple date. I don’t know if you’ll have the same date in your database, but I assume this is the default date for the sample database.

    2016-06-06 14_07_56-Phone

    Obviously these stats are out of date.

    Or are they? I don’t use this database a lot and haven’t changed the data in this table that I’m aware of. In that case, they may be up to date.

    This can be a handy function, but remember, the age of stats only matters if you’ve had data changes. However with having an understanding of both pieces of information, you might use this to accelerate statistics rebuilds ahead of what AUTO STATISTICS might do.

    SQLNewBlogger

    This was a good chance to dig into and look at how a function works in SQL and how I might use it. You could write this easily.

  • OBJECT_ID()–#SQLNewBlogger

     

    One of the things that is needed in quite a few functions is the object_id of a particular table/view/procedure/function in SQL Server. For example, I was looking at STATS_DATE recently, and it has this definition.

    STATS_DATE (object_id, stats_id)

    In the past, I’d run something like this:

    DECLARE @i INT
    SELECT @i = object_id
     FROM sys.objects 
     WHERE name = 'SalesOrderHeader'
    SELECT STATS_DATE ( @i , 2)

    Actually, I’d really do this as two batches.

    SELECT * FROM sys.objects WHERE name = 'SalesOrderHeader'
    SELECT STATS_DATE ( 1266103551 , 2)  
    

    I’d run the first, get the ID, and paste it into the second. However I’ve learned that isn’t the best way to do this. In fact, when I started doing  a lot of encryption testing and research, I started to take advantage of functions like OBJECT_ID.

    Now, here’s what I’d do:

    SELECT STATS_DATE ( OBJECT_ID(‘Sales.SalesOrderHeader’) , 2) 

    Simple, easy, and I can do this inline. With SQL Prompt, I’m also pretty quick getting this out. Of course, I do need to remember to include the schema, because this won’t work:

    SELECT STATS_DATE ( OBJECT_ID(‘SalesOrderHeader’) , 2) 

    Three warnings. First, qualify your objects. In this case, I should have used Sales.SalesOrderHeader to be sure I get the correct object. There are people that use schemas with the same object in multiple schemas (etl.SalesOrderHeader, audit.SalesOrderHeaders, etc.).

    Second, the object_id() isn’t guaranteed to be unique across databases. I should have pointed that out.

    SQLNewBlogger

    When I find quick tricks or techniques I use often, I try to make a note and then write about them later. It helps me remember, but it also lets me share things with others.

    Perhaps most important, it shows I’m doing and learning things in my career. Winking smile

  • Another Recursive CTE–Doing Math

    Another post for me that is simple and hopefully serves as an example for people trying to get blogging as #SQLNewBloggers.

    I showed how to write a simple recursive query recently that calculated an amount of money paid out each day. Now I want to extend this a bit to include a few math formulas.

    Note: These aren’t terribly useful, but they are good practice for just writing recursive queries.

    Fibonacci Series

    I’m sure many of you have dealt with a Fibonacci series at some point in your life. This is a series where the current value is the sum of the two previous values. In other words,

    term 3 = term 1 + term 2

    term 4 = term 2 + term 3

    etc.

    The series starts with 0, 1 and goes from there. Can we do this in SQL? Sure.

    Let’s start by looking at what we need. We need some counter, a current term, and a previous term. That’s 3 columns in our query. We start by building an anchor, which has the first two terms. The counter is n and the two terms are i and j.

    — Anchor

    select n = 1

    , i = 0

    , j = 1

    Now we add the recursive part. In this case, the counter increases by 1. I only include the counter so I know when to stop. SQL Server has a finite size of various values, and we can exceed that without a way to stop.

    The first value will be calculated from the current value + the next call. The current value will become the second one on the next last call, so we move that over. This gives us.

    select counter + 1

      , first = first + second

    , second = first

    With this, we can then add a WHERE clause to stop. I’ll stop at the first ten terms. Here’s the CTE.

    WITH myFib (n, i, j)
    AS
    (
      — anchor
      SELECT ‘n’ = 1
           , ‘i’ = 0
           , ‘j’ = 1
       UNION ALL
       — recursive section
       SELECT n + 1
            , i + j
            , i
           FROM myFib
    WHERE myFib.n < 10
    )
    SELECT
    ‘Level’ = myFib.n
    , ‘Fibonacci’ = i FROM myFib

    If we run this, we see:

    2016-05-17 19_16_28-Cortana

    We can extend this by altering the WHERE clause.

    A Little Calculus

    What about math functions? Have any of you worked with a series in calculus? If so, you might remember something like this:

    eq0018M

    This is a repetitive calculation, and should either converge or diverge. Can we implement this as a recursive CTE? Sure.

    This one is really simple. I use the POWER() function in my recursive member to raise –1 to whatever counter I’m using for n. I then use a SUM() across all previous values in the outer query to get the sum.

    WITH myPartialSum (n, s)
    AS
    (
    SELECT ‘n’ = 1
         , ‘s’ = POWER( -1, 0)
         UNION ALL
         SELECT n + 1
           , POWER(-1, n)
           FROM myPartialSum
           WHERE n < 100
    )
    SELECT myPartialSum.n
          ,myPartialSum.s
          , ‘partialsum’ = SUM(s) OVER (ORDER BY (SELECT NULL) ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
    FROM myPartialSum

    Note: This series does not converge, as it alternates to infinity.

    Neither of these is terribly useful, but they do allow some practice in writing CTEs that will recurse.

    SQLNewBlogger

    Implement some other series or sequence yourself and explain how it works.