Category: Blog

  • T-SQL Tuesday #37 – A Month of Joins

    tsqltuesdayIt’s time once again for T-SQL Tuesday, and this month is hosted by SQLity.net, Sebastian Meine.

    If you want to know more or participate, read the invitation and write your own blog post.

    The topic this month is joins, in honor of Sebastian’s a-join-a-day series. He’s writing about various aspects of joins, and invites us all to do the same thing on this Tuesday.

    Writing Better Joins

    I’m not a T-SQL expert. I can write code, and understand many type of queries, but I’m not one to dazzle others with their code, like Jeff Moden can. Instead, I want to talk about how I’ve learned to ensure that my code makes sense, is understandable, and most importantly, easy to find the mistakes inside.

    I mainly do this by paying attention to the formatting of the code. I would say that once I started to get away from writing code like this, I found bugs easier, and understood the code better:

    WITH [EMP_cte]([BusinessEntityID], [OrganizationNode], [FirstName], [LastName], [RecursionLevel]) -- CTE name and columns
    AS (
    SELECT e.[BusinessEntityID], e.[OrganizationNode], p.[FirstName], p.[LastName], 0 -- Get the initial list of Employees for Manager n
    FROM [HumanResources].[Employee] e INNER JOIN [Person].[Person] p ON p.[BusinessEntityID] = e.[BusinessEntityID]
    WHERE e.[BusinessEntityID] = @BusinessEntityID
    UNION ALL SELECT e.[BusinessEntityID], e.[OrganizationNode], p.[FirstName], p.[LastName], [RecursionLevel] + 1 -- Join recursive member to anchor
    FROM [HumanResources].[Employee] e INNER JOIN [EMP_cte] ON e.[OrganizationNode].GetAncestor(1) = [EMP_cte].[OrganizationNode]
    INNER JOIN [Person].[Person] p ON p.[BusinessEntityID] = e.[BusinessEntityID]
    )
    SELECT [EMP_cte].[RecursionLevel], [EMP_cte].[OrganizationNode].ToString() as [OrganizationNode], p.[FirstName] AS 'ManagerFirstName', p.[LastName] AS 'ManagerLastName',
    [EMP_cte].[BusinessEntityID], [EMP_cte].[FirstName], [EMP_cte].[LastName] -- Outer select from the CTE
    FROM [EMP_cte] INNER JOIN [HumanResources].[Employee] e ON [EMP_cte].[OrganizationNode].GetAncestor(1) = e.[OrganizationNode]
    INNER JOIN [Person].[Person] p ON p.[BusinessEntityID] = e.[BusinessEntityID]
    ORDER BY [RecursionLevel], [EMP_cte].[OrganizationNode].ToString()
    OPTION (MAXRECURSION 25) 

    I often find code in forums, or sent to me and I need to reformat it so that it looks better. I prefer something like this:

    WITH    [EMP_cte] ( [BusinessEntityID], [OrganizationNode], [FirstName], [LastName], [RecursionLevel] )
              -- CTE name and columns
              AS (
                   SELECT
                    e.[BusinessEntityID]
                   ,e.[OrganizationNode]
                   ,p.[FirstName]
                   ,p.[LastName]
                   ,0 -- Get the initial list of Employees for Manager n
                   FROM
                    [HumanResources].[Employee] e
                    INNER JOIN [Person].[Person] p
                        ON p.[BusinessEntityID] = e.[BusinessEntityID]
                   WHERE
                    e.[BusinessEntityID] = @BusinessEntityID
                   UNION ALL
                   SELECT
                    e.[BusinessEntityID]
                   ,e.[OrganizationNode]
                   ,p.[FirstName]
                   ,p.[LastName]
                   ,[RecursionLevel] + 1 -- Join recursive member to anchor
                   FROM
                    [HumanResources].[Employee] e
                    INNER JOIN [EMP_cte]
                        ON e.[OrganizationNode].GetAncestor(1) = [EMP_cte].[OrganizationNode]
                    INNER JOIN [Person].[Person] p
                        ON p.[BusinessEntityID] = e.[BusinessEntityID]
                 )
        SELECT
            [EMP_cte].[RecursionLevel]
        ,   [EMP_cte].[OrganizationNode].ToString() AS [OrganizationNode]
        ,   p.[FirstName] AS 'ManagerFirstName'
        ,   p.[LastName] AS 'ManagerLastName'
        ,   [EMP_cte].[BusinessEntityID]
        ,   [EMP_cte].[FirstName]
        ,   [EMP_cte].[LastName] -- Outer select from the CTE
        FROM
            [EMP_cte]
            INNER JOIN [HumanResources].[Employee] e
                ON [EMP_cte].[OrganizationNode].GetAncestor(1) = e.[OrganizationNode]
            INNER JOIN [Person].[Person] p
                ON p.[BusinessEntityID] = e.[BusinessEntityID]
        ORDER BY
            [RecursionLevel]
        ,   [EMP_cte].[OrganizationNode].ToString()
    OPTION
            ( MAXRECURSION 25 ) 

    That actually came from reformatting the code using SQL Prompt, a product from my employer, Red Gate Software. I’m lucky in that SQL Prompt formats things as I’d prefer them, indenting and getting the JOIN and ON clauses onto separate lines.

    Having code with structure, where you can clearly see the tables being joined, the clauses in use, and not miss any of the columns being selected at a glance is important. When you’re under stress and trying to debug or develop something, it’s easy to miss something that’s happening in the code if it’s not formatted correctly.

    Whether you like commas before or after columns, or you want things indented so that the names of objects line up doesn’t really matter. What’s important is that you and your team agree on a set of formatting, or have tools that reformat things for each developer in a consistent way. You’ll spend less time trying to understand the code and more time building or fixing it, if it has a consistent layout.

  • Planning

    I made it to the UK on a very smooth, uneventful, on-time trip. That’s just what I like when traveling. After arriving at Heathrow and traveling to Cambridge, I arrived at the Red Gate office just in time for lunch. A fortuitous event as I was hungry after flying all night.

    Most of my afternoon was meeting with various product groups talking about planned enhancements and changes and how these items might benefit DBAs in the field. Grant and I are usually called on for sanity checks on thoughts, ideas, and plans.

    It’s interesting to discuss with product groups how and why they want to make changes. I can’t talk about specifics, but hearing from developers and project managers is always interesting to me. We see the world differently and it’s good to give feedback on products that can make a DBA’s job easier. I haven’t always had the chance to do that, but when I do, it’s a growing experience to hear how a vendor sees the world.

    I wrote about this recently, and a few disagreed, thinking that Microsoft doesn’t listen to them. I disagreed then, and I still do. I get an opinion at Red Gate, and with Microsoft. I don’t get to make a decision, or even much of a vote, but I do get to voice my opinion. Just because it isn’t followed doesn’t mean that someone didn’t listen.

    I don’t know to what extent my thoughts will influence the products, but I did see and discuss things with a variety of people in different departments, and the discussions evolved, so I know my opinion is heard. Whether it changes things remains to be seen.

  • Back Across the Water

    Another trip to the UK this week, with me leaving today for Cambridge and meetings at Red Gate Software. This will be my third trip this year, a record for me, and also the most travel I’ve done in a year.

    We are doing some planning for 2013, for the community and various events that Red Gate is a part of each year. We’ve supported user groups, SQL Saturdays, various conferences and our own SQL in the City events. Our goal is to interact with the community and learn things about how people use our software, as well as promote it, but also to educate data professionals. We want to turn a profit, but we also want to be a part of the community and support it where we can.

    I wish I could say that I’ll travel less next year, but I’m not sure that’s the case. We are looking to more effectively provide support, with more value for the money we spend, but I suspect that will mean more travel for Grant Fritchey and myself.

    We’ll see what happens, and we’ll likely start booking some events in 2013 soon. If you have ideas, suggestions, or requests, please feel free to post something here or send an email to communities@red-gate.com.

  • Speaking at Oracle Training Days

    RMOUGSpeakerA first for me. I’m attending a non-SQL Server event, trying to bring a little knowledge of my platform to those that typically work on the Oracle side of things.

    The Rocky Mountain Oracle Users Group is holding their Training Days 2013 event on Feb 11-13 in Denver and a few of us SQL Server people were invited to speak.

    I’ll be talking about Database Maintenance one of the days, hopefully ensuring that some of the “accidental” SQL Server DBAs that also manage Oracle understand the basics of maintenance in SQL Server.