Tag: T-SQL

  • Getting all Yesterday’s Sales, or Finding Midnight Yesterday

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

    I see questions like this regularly. How do I get all the sales from yesterday? I tried using DATEADD(day, –1, getdate()), but I only get some of the sales.

    Many people working with T-SQL know this is an issue. They know that getdate() returns the date and time of this instant (roughly). At the time of this writing, that’s 3:11 pm.

    2015-11-25 15_11_29-Photos

    However if I want sales from yesterday, I really want all timestamps from midnight on. So I probably want code that looks like this:

    SELECT SUM(ordertotal)
     FROM sales
     WHERE SalesDate > '20151124 00:00:00'
     AND SalesDate < '20151125 00:00:00'
    
    

    How do I get the time to be midnight?

    The easy answer is one I’ve been using quite a bit lately to answer questions, and I’ve refreshed my knowledge of the datetime trick. I use a combination of DATEADD and DATEDIFF to get to a 0 based datetime.

    SELECT DateAdd(Day, Datediff(Day,0, GetDate()), 0)
    
    

    In this case, I’ll get midnight yesterday, or 2015-11-24 00:00:00. This is because I’m using 0 as my base date and looking for the days (in DATEDIFF) since that 0 based date. When I add those days with DATEADD to the same zero based date, I get the correct date, but with a 0 based time.

    This same technique works to find the first of this month.

    SELECT DateAdd(Month, Datediff(Month,0, GetDate()), 0)
    
    

    You can also use other datetime values to normalize those times.

    SQLNewBlogger

    This was a quick post. I had answered the question and spent less than ten minutes putting this together.

  • Puzzled by T-SQL

    Live blogging this a bit as I try things. This will update a bit, so you’ll have to read through.

    Adam Machanic posted this: T-SQL Puzzle-How many rows will this return? SELECT*FROM(VALUES(1),(2))AS x(i)WHERE EXISTS(SELECT MAX(i)FROM(VALUES(1))AS y(i)WHERE y.i=x.i)

    I was in a doctor’s office waiting at the time, but I responded that I didn’t think one row was right. I didn’t have the chance to see what happened, so I couldn’t reason through what was happening. Maybe I should have been able to? Not sure.

    I got home and ran this (thanks, SQL Prompt):

    SELECT
         *
       FROM     ( VALUES ( 1), ( 2) ) AS x ( i )
       WHERE     EXISTS ( SELECT  MAX(y.i)
                        FROM
                        ( VALUES ( 1) ) AS y ( i )
                   WHERE
                     y.i = x.i );
    

    I get two rows back, a 1 and a 2. Very strange.

    I tried experimenting a bit. I created tables and put data in there. Maybe there’s something I don’t get in the VALUES() clause.

    CREATE TABLE mytable99 (id INT);
    CREATE TABLE mytable999 (id INT);
    GO
    INSERT dbo.mytable99
    ( id )
    VALUES
    ( 1 ), (2);
    INSERT dbo.mytable999
    ( id )
    VALUES
    ( 1 )
    ;
    GO
    SELECT
    *
    FROM
    dbo.mytable99 AS x
    WHERE
    EXISTS ( SELECT MAX(y.id)
            FROM dbo.mytable999 AS y
            WHERE y.id = x.id );
    
    

    Same result.

    Hmmm, Adam added a clue. Why does select max(1) work?

    2015-12-03 13_43_15-Photos

    I was guessing that max() operates on the scalar set of [1]. However I’m not sure.

    I then did this:

    UPDATE dbo.mytable999 SET id = 9;
    

    When I re-ran the query, still two rows. Without a match.

    Next I added another row to the first table.

    INSERT dbo.mytable99
    ( id)
    VALUES
    ( 3 );
    

    Now I get three rows.

    What I’m guessing (at this point) is that the correlated subquery returns a 1 for every row of the first table, so this means I get the size and shape of that table. The EXISTS() is always satisfied.

    I’ll be interested to learn what is happening, or if I’m right.

    Update: I’m not.

    Or semi-right.

    The exists is satisfied, but why?

    Adam posted a second hint asking me to remove the max(). I did that and got 1 row. Well that’s interesting. How does the aggregate affect the correlated subquery, and how does this affect the Exists().

    I decided to break down the query inside the EXISTS(). I did this with scalar values. Since I’ve been dealing with two rows, I used those two scalar values.

    	 SELECT
                    y.i --MAX(y.i)
                  FROM
                    ( VALUES ( 1) ) AS y ( i ) 
    			  WHERE
                    y.i = 1 ;
    
    	 SELECT
                    y.i --MAX(y.i)
                  FROM
                    ( VALUES ( 1) ) AS y ( i ) 
    			  WHERE
                    y.i = 2;
    

    With these queries, I get one row and zero rows, just an empty set. That makes sense in terms of why removing the MAX() gives me one row in the whole thing.

    Next I added the MAX back.

    	 SELECT
                    MAX(y.i)
                  FROM
                    ( VALUES ( 1) ) AS y ( i ) 
    			  WHERE
                    y.i = 1 ;
    
    	 SELECT
                    MAX(y.i)
                  FROM
                    ( VALUES ( 1) ) AS y ( i ) 
    			  WHERE
                    y.i = 2;
    

    Now I get one row for the first, and one row for the second? Huh? However the second set is a row with NULL in it. I checked the EXISTS() documentation, and sure enough, if there are rows, this returns true, even if the row has a null value. This isn’t the value of a row, but rather just its presence.

    I then did this to check:

    SELECT 'test' = 1
     WHERE EXISTS( SELECT * FROM mytable WHERE 1 = 0);
    
    SELECT 'test' = 1
     WHERE EXISTS( SELECT null);
    

    Sure enough, the first gives me an empty result set, while the second doesn’t.

    But why does MAX() return a row? I tried this with a simple query:

     SELECT MAX(i)
      FROM ( VALUES (1)) AS x(i)
      WHERE x.i = 2

    Which does return NULL. I did search and saw this explanation on SO, saying that the result of the MAX() for the group (where x.i=2) is undefined, hence the NULL. This is born out as you see here:

    CREATE TABLE mytable88(i INT);
    GO
    SELECT MAX(i) FROM dbo.mytable88 AS m;
    

    Strange. Certainly I wouldn’t have expected that from MAX(). I would have thought it was an empty set, but apparently that’s not the case.

  • Viewing Extended Properties for Information

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

    I’ve been working a little with extended properties, adding and updating them for various objects. However in addition to altering properties, viewing the properties on an object is often necessary. This post will look at how we get the properties in a few different ways.

    The easiest way to see extended properties is to look at the properties of an object in the SSMS Object Explorer. For example, I can right click on a table in OE.

    2015-11-02 20_30_55-

    Once I click Properties, I get a dialog with a lot of items on the left. The bottom one is for Extended Properties, with a simple add/edit/delete grid. Here I can see the property(ies) I’ve added.

    2015-11-02 20_31_07-Table Properties - SalesHeader_Staging

    However this is cumbersome for me. I’d much rather find a way to query the information, which is what I need to do with an application of some sort. I’d think sp_help would work, but it doesn’t. If I run this, I get the following result sets:

    • header with owner, type, and creation date.
    • column list with meta data
    • identity property information.
    • RowGuid column information
    • filegroup storage location.
    • Messages with index, constraint, FK, and schemabinding relations.

    Not very helpful in this case.

    I do know that extended property information is in sys.extended_properties. I can query this view, which gives me some information, but I need to join this with sys.objects for easy to understand information.

    2015-11-02 20_38_42-SQLQuery13.sql - aristotle.RaiseCodeQuality (ARISTOTLE_Steve (69))_ - Microsoft

    This works, and this is one of the ways in which I do query properties in various tSQLt tests.

    There is one other way I’ve seen to query extended properties. When perusing the BOL page for sp_updateextendedproperty, I found sys.fn_listextendedpropery. This is a DMF, a function, that you can use to query for property values. Since it’s a TVF function, I need to use it in a query as a functional object.

    2015-11-02 20_42_27-SQLQuery13.sql - aristotle.RaiseCodeQuality (ARISTOTLE_Steve (69))_ - Microsoft

    There are lots of parameters in this function. However you can guess what they are after working with the other extended property procedures. In fact, the first time I started this post, I was disconnected and had to experiment with the function, adding parameters until it ran without an error.

    The first parameter is the name of the property. This can be NULL, in which case you’ll get all the properties that exist.

    2015-11-02 20_44_48-SQLQuery13.sql - aristotle.RaiseCodeQuality (ARISTOTLE_Steve (69))_ - Microsoft

    The rest of the properties correspond to the level 0, 1, 2 types and names that you are using to filter the results. This is actually a good technique to use with this function, and I’ll be using this more in the future.

    SQLNewBlogger

    This post followed on from the previous ones. In this case, I started this disconnected, using the knowledge I had to write the basics with SSMS and the system table. That took about 20 minutes to document and then I spent 5 minutes experimenting with the function, whose name I had on an open browser tab. Once I worked through that, I spent another 5 minutes writing.

    Thirty minutes to a post. You can do this.

    Reference

    A few items from BOL:

    sp_help – https://msdn.microsoft.com/en-us/library/ms187335.aspx

    sys.extended_properties – https://msdn.microsoft.com/en-us/library/ms177541.aspx

    sys.fn_listextendedproperty – https://msdn.microsoft.com/en-us/library/ms179853.aspx

  • Updating Extended Properties

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

    I wrote recently about adding extended properties. Updating them is very similar. There’s an analogous procedure called sp_updateextendedproperty that changes the value of properties.

    The arguments are again, unintuitive, but the more I work with extended properties, the more comfortable I become. In this case, I have the same name and value, and then the level 0,1, 2 items with both a type and name.

    I highly suggest, however, that you name your parameters, including the names in your calls so programmers running across the T-SQL aren’t depending on position for an understanding of the parameter.

    If I look at the table from the previous post, I can update the value of my property with this code:

    EXEC sys.sp_updateextendedproperty
      @name = 'PKException'
    , @value = 0
    , @level0type = 'schema'
    , @level0name = 'dbo'
    , @level1type = 'table'
    , @level1name = 'SalesHeader_Staging' -- sysname
      ;
    GO
    
    

    However my property needs to exist. If I call this procedure with the wrong property, I get an error.

    2015-11-02 17_25_03-Cortana

    This means that you need to be sure that the property exists before you update it. Good code would have the error handling somewhere.

    SQLNewBlogger

    After writing the previous post, this one took only about ten minutes to do the typing. I’d been working with extended properties, so I had the code and just needed to take the screenshot.

    Reference

    A few items from BOL

    sp_updateextendedproperty – https://msdn.microsoft.com/en-us/library/ms186885.aspx