Author: way0utwest

  • Aging Software

    I wrote The Age of Software awhile back and noted that supporting previous versions of software isn’t necessarily a good use of resources for development teams. I especially think this is true of SQL Server. But does that mean we should abandon aging software platforms?

    It’s a tough question. I’ve certainly talked about the case for upgrading, and the reasons why you might not. For any particular instance, however, I think that each of you has to make the case about whether the software works, or it doesn’t.

    If it works, then it seems many of us will live with the old software and keep it running. As late as a few years ago I knew a company running SQL 6.5 with a piece of software built in 1996 and last patched in 2001. However this software ran a building key card system, and there wasn’t a good case to be made for upgrading.

    For a software developer, however, when you look at aging pieces of software, even those that customers may pay for support on, is it worth maintaining skills and support? If you don’t have staff turnover, then perhaps. If you don’t, I do think that it might be time to let the product die.

    I’m torn on the way we deal with software in our world. On one hand, I’d like to see customers given source code for end of life platforms in order to support themselves if they wish. On the other, I understand the IP concerns, and business case to let software die.

    Ultimately I’m mostly OK with the current way most vendors support software. If it works for a decade and support ends, I can continue to use it. Until it doesn’t work, and then I am glad that most vendors have an upgrade for me.

    Steve Jones

    The Voice of the DBA Podcast

    Listen to the MP3 Audio ( 2.5MB) podcast or subscribe to the feed at iTunes and LibSyn.

  • Testing Sum By Month

    I’ve been on a testing kick, trying to formalize the ad hoc queries I’ve run into something that’s easier to track. As a result, when I look to solve a problem, I’ve written a test to verify that what I think will happen, actually happens.

    The Problem

    I saw a post recently where someone wasn’t sure how to get the sum of a series of data items by month, so I decided to help them. They asked for a year number, a month number, and a total, so something like this:

    Year   Month   Sales

    2012       1   1500.23

    2012       2   1480.00

    2012       3   1945.00

    2015       7   8933.11

    They mentioned, however, that the had sales data stored as an integer. Not as 201201, but as 1, 2, 3, with a base date being Jan 1, 2012. That’s strange, but it’s a good place to write a test.

    I like to start with the results, since if I don’t know the results, how can I tell if my query works? Let’s get a test going. I’ll start by created my expected results. I’ve come to like using temporary tables, and limited data. I also like to test some boundaries, so Iet’s cross a year.

    CREATE PROCEDURE [tArticles].[test sum of sales by month for multiple months]
    AS
    BEGIN
    -- Assemble
    CREATE TABLE #Expected (
    yearnum INT
    , monthnum TINYINT
    , salestotal NUMERIC(10,2)
    )


    SELECT *
    INTO #actual
    FROM #Expected AS e



     

    INSERT INTO #Expected
    ( yearnum
    , monthnum
    , salestotal
    )
    VALUES
    ( 2012, 11, 2500.23 )
    , ( 2012, 12, 2200.15 )
    , ( 2013, 1, 2656.75 )

    I like to create the actual results table here as well, which allows me to then easily insert into this table from a procedure as well as a query. In this case, I’ll use a query, but I could use insert..exec.

    Once I have results, I need to setup my test data. In this case, I’d probably go grab the rows from a specific period and put them in a temp table and use Data Compare to get them. Or make them up. It doesn’t matter. I just need the data that allows me to test my query.


    EXEC tsqlt.FakeTable @TableName = N'MonthlySales';

    INSERT MothlySales
    VALUES
    ( 11, 1000.00)
    , ( 11, 1500.23)
    , ( 12, 2200.15)
    , ( 13, 1000.00)
    , ( 13, 1656.00)
    , ( 13, 0000.75);

    I don’t try to make this hard. I use easy math, giving myself a few cases. One, two, three rows of data for the months. If I think this isn’t representative, I can add a few more. I don’t try to be difficult, I’m testing a query. If I had rows that might not matter, or I wanted to test if 0 rows are ignored, I could do that.

    Now I need a query. Something simple, a SUM() with a GROUP by is needed. However I need to also change 11 into 2012 11, so that’s an algorithm.

    An easy way to do this is start with a base date. I’d prefer this is in a table, but I can do it inline.

    INSERT #actual

    SELECT
    yearnum = DATEPART( YEAR, DATEADD( MONTH, datenum, '20120101'))
    , MONTHNUM = DATEPART(MONTH, DATEADD( MONTH, DATENUM, '20120101'))
    , SALESTOTAL = SUM(ms.salesamount)
    FROM dbo.MonthlySales AS ms
    GROUP BY
    DATEPART( YEAR, DATEADD( MONTH, datenum, '20120101'))
    , DATEPART(MONTH, DATEADD( MONTH, DATENUM, '20120101'))
    ORDER BY
    DATEPART( YEAR, DATEADD( MONTH, datenum, '20120101'))
    , DATEPART(MONTH, DATEADD( MONTH, DATENUM, '20120101'))
    ;
    GO

    I’ll insert this data into #actual, which tests my query.

    The final step is to assert my tables are equal.

    -- Assert
    EXEC tsqlt.AssertEqualsTable
    @Expected = N'#EXPECTED',
    @Actual = N'#actual',
    @FailMsg = N'The calculations are incorrect';

    The Test

    What happens when I execute this test? I can use tsqlt.run, or my SQL Test plugin.

    2015-09-28 16_07_49-Photos

    In either case, I’ll get a failure.

    2015-09-28 16_08_11-Photos

    When I check the messages, I see the output from tSQLt. In this case, none of my totals seem to match.

    2015-09-28 16_15_23-Photos

    What’s wrong? In my case, I’m adding the integer to the base month, but that means a 1 means 2012 02, not 2012 01. I’m a month off. Let’s adjust the query.

    -- Act
    INSERT #actual
    SELECT

    yearnum = DATEPART( YEAR, DATEADD( MONTH, datenum, '20111201'))
    , MONTHNUM = DATEPART(MONTH, DATEADD( MONTH, DATENUM, '20111201'))
    , SALESTOTAL = SUM(ms.salesamount)
    FROM dbo.MonthlySales AS ms
    GROUP BY
    DATEPART( YEAR, DATEADD( MONTH, datenum, '20111201'))
    , DATEPART(MONTH, DATEADD( MONTH, DATENUM, '20111201'))
    ORDER BY
    DATEPART( YEAR, DATEADD( MONTH, datenum, '20111201'))
    , DATEPART(MONTH, DATEADD( MONTH, DATENUM, '20111201'))

    Now when I run my test, it passes.

    2015-09-28 16_18_09-Photos

    Why Bother?

    This seems trivial, right? What’s the point of this test? After all, I can easily check this with a couple quick queries.

    Well, let’s imagine that we decide to move this base date into a table, or that we alter it. We want our queries to continue to work. I can have this test as part of an automated routine that ensures this test will run each time the CI process runs. Or each time a developer executes a tsqlt.runall in this database (shared or populated from a VCS). I prevent refactoring queries.

    More importantly, I can take results and alter them first, say if someone decides to change this to a windowing query. I could plug a new query in the test (or better yet, use a proc and put that call in the test) , and if I change code, I can verify it still works.

    Write tests. You need them anyway, so why not formalize them? The code around this query, mocking test data, is something I do anyway, so this gets me a few more minutes to verify that the code works. I can tune the query, alter indexes, perf test, and be sure that code is still running cleanly.

    http://www.sqwhere lservercentral.com/Forums/Topic1716471-1292-1.aspx#bm1716535

  • Querying Yesterday

    One of the new features coming in SQL Server 2016 is the temporal table. It’s an interesting concept, one that many businesses have wanted for years. If you’re not sure what this is, we’ve got a collection of resources on Learning about Temporal Tables (and other features). Check them out and we’ll keep adding content as we find it.

    Temporal tables give us some amazing capabilities, but at a cost. As with anything in computers, there is a cost for capability. In this case, we can look back at the view of our data as of a particular point in time. In many ways, this means that we don’t need to bolt on, or query into, auditing data.

    However there are other costs. As with any auditing system, we potentially have substantial data that we need to manage somehow. Certainly we need to choose which tables to track. Even if we don’t have to build a process, we will have to deal with the cost of storage and provisioning, as well as determining the retention periods. We also need to really depend on the system times for our various instances to be in sync.

    I think that this is a needed, and very useful feature. I’m sure there will be bugs to patch, as well as enhancements to be built. We’ll find those over time, but I think that this is one of those features that we’ll come to see as essential in a decade and wonder how we ever built systems without it.

    Steve Jones

    The Voice of the DBA Podcast

    Listen to the MP3 Audio ( 2.1MB) podcast or subscribe to the feed at iTunes and LibSyn.

  • What’s a Code Smell?

    We all have a variety of code patterns and practices that we follow. Most of them were probably picked up along the path of our career. A suggestion from a colleague. A piece of sample code that solved a problem. A performance tuning trick that stopped our phone from ringing. These methods of learning are the way that most of us actually grow our skills over time.

    However just because we learned something, or because a technique solved a problem doesn’t mean it was a good piece of code. In fact, often the code we may think works well might not be the most efficient way to structure the code. Many developers have learned this over the years, as they read about new techniques that are more efficent, elegant, or just simpler.

    Kent Beck and Massimo Arnoldi coined the term code smell years ago, as a way of noting the development patterns and practices that  lead to poorly written, or difficult to maintain code. There have been other attempts to document practices which are not recommended, though the success is probably limited as many developers continue to build on poorly written code rather than refactoring and cleaning their codebase over time.

    Simple Talk  and Phil Factor published a SQL Code Smells ebook awhile back, trying to document the signs of poorly written T-SQL. The book is good, with guidance about particular patterns that can cause you problems over time. The items aren’t meant to be rules, but rather guidelines that you adhere to unless you have a good, specific reason that you can justify to others.

    I ran into a code smell recently where a developer noted that their application depended on a specific database name in order to work.  That’s not in the ebook, but I think it’s easily one I’d avoid. My connection should determine the database, not the application itself. I know there may be exceptions here, but in general, application code shouldn’t be dependent on a particular name.

    I’d urge you to pick up the ebook (it’s free) and keep it handy. See if any of the items listed are habits you might have picked up over time and not realized that they are, in fact, poor practices. I would also recommend you peruse Aaron Bertrand’s Bad Habits to Kick series, as a way of improving your own code.

    Steve Jones

    The Voice of the DBA Podcast

    Listen to the MP3 Audio ( 2.9MB) podcast or subscribe to the feed at iTunes and LibSyn.