Tag: SQLNewBlogger

  • Solving Ken’s FizzBuzz 3D–#SQLNewBlogger

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

    I like the FizzBuzz test. It’s cute and fun, and I’ve had my kids solve it, just to think about what it means to structure a simple problem. It’s not a great test of whether you’re a good programmer, but if you can’t solve this, you probably aren’t.

    Ken Fisher set up a challenge to solve FizzBuzz with SQL, but in a 3D manner. He added a few challenge to this, like no modulus operator. I don’t know why you’d deliberately avoid using this, but it’s a programming exercise, so why not. I took the challenges because, well, Ken asked me do.

    SQLNewBlogger

    I’m putting this part first, because this is a good exercise, and a great post for your blog. Solve this yourself first, and write about it. Then you can read my code.

    The coding part of this probably took me 20-25 minutes to do. I worked on it in a few stages, pausing to do other work and let the solution simmer a bit. I realized a few times that I was making it harder, so the breaks were helpful.

    Writing this up was about 30 minutes, but it made me think about what I’d done.

    Building a Solution

    My first thought with this is I need a tally table. Of course, I’m building a set of coordinates from 1 to 100. I started here.

    WITH myTally (n)
    AS
    -- SQL Prompt formatting off
    (SELECT n = ROW_NUMBER() OVER (ORDER BY (SELECT null))
      FROM (VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10)) a(n)
       CROSS JOIN (VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10)) b(n)
    )
    , cteCoordinates (x, y, z)
    -- SQL Prompt formatting on
    AS
    (
    SELECT a.n ,
            b.n ,
            c.n
    FROM myTally AS a
         CROSS JOIN myTally AS b
         CROSS JOIN myTally AS c
    )
    SELECT x, y, z
    FROM cteCoordinates;

    This gave me my list of numbers.

    2018-07-02 14_40_48-SQLQuery4.sql - (local)_SQL2016.sandbox (vstsbuild (53))_ - Microsoft SQL Server

    My next step was to think about the FizzBuzz test. I can’t use Modulo, so how can I determine if I am evenly divisible by 3? I decided to look at the simple division operator. I got these results, but notice anything?

    2018-07-02 14_42_55-SQLQuery4.sql - (local)_SQL2016.sandbox (vstsbuild (53))_ - Microsoft SQL Server

    I get a new number every time the division changes. Immediately I think of window functions here. So I decided to do some checking here. Since I’m looking for a change, I went with a LAG function.

    If the LAG isn’t equal to the current value, I’ve had a change. Therefore, a Fizz.

    2018-07-02 14_45_44-SQLQuery4.sql - (local)_SQL2016.sandbox (vstsbuild (53))_ - Microsoft SQL Server

    Let’s clean this up to show Fizz and add the Buzz with a 5.

    2018-07-02 14_47_27-SQLQuery4.sql - (local)_SQL2016.sandbox (vstsbuild (53))_ - Microsoft SQL Server

    I’m getting there, but what about FizzBuzz? Well, the CASE will execute in order, so let’s add that one at the top.

    2018-07-02 14_49_00-SQLQuery4.sql - (local)_SQL2016.sandbox (vstsbuild (53))_ - Microsoft SQL Server

    I’ve mostly solved this, so let’s put this in my query. I’ll substitute the CASE in for each item of the cross join. I get this:

    WITH myTally (n)
    AS
    -- SQL Prompt formatting off
    (SELECT n = ROW_NUMBER() OVER (ORDER BY (SELECT null))
      FROM (VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10)) a(n)
       CROSS JOIN (VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10)) b(n)
    ),
    cteCoordinates (x, y, z)
    AS
    ( SELECT
    CASE
         WHEN ( a.n/3 != LAG(a.n/3, 1, 0) OVER (ORDER BY a.n)
           AND  a.n/5 != LAG(a.n/5, 1, 0) OVER (ORDER BY a.n)
             )THEN 'FizzBuzz'
         WHEN a.n/3 != LAG(a.n/3, 1, 0) OVER (ORDER BY a.n) THEN 'Fizz'
         WHEN a.n/5 != LAG(a.n/5, 1, 0) OVER (ORDER BY a.n) THEN 'Buzz'
         ELSE CAST(a.n AS VARCHAR(8)) END,
    case WHEN ( b.n/3 != LAG(b.n/3, 1, 0) OVER (ORDER BY b.n)
           AND   b.n/5 != LAG(b.n/5, 1, 0) OVER (ORDER BY b.n)
             )THEN 'FizzBuzz'
         WHEN b.n/3 != LAG(b.n/3, 1, 0) OVER (ORDER BY b.n) THEN 'Fizz'
         WHEN b.n/5 != LAG(b.n/5, 1, 0) OVER (ORDER BY b.n) THEN 'Buzz'
         ELSE CAST(b.n AS VARCHAR(8)) END,
    case WHEN ( c.n/3 != LAG(c.n/3, 1, 0) OVER (ORDER BY c.n)
           AND  c.n/5 != LAG(c.n/5, 1, 0) OVER (ORDER BY c.n)
             )THEN 'FizzBuzz'
         WHEN c.n/3 != LAG(c.n/3, 1, 0) OVER (ORDER BY c.n) THEN 'Fizz'
         WHEN c.n/5 != LAG(c.n/5, 1, 0) OVER (ORDER BY c.n) THEN 'Buzz'
         ELSE CAST(c.n AS VARCHAR(8)) END
        FROM mytally a
        CROSS JOIN mytally b
        CROSS JOIN mytally c
    )
    SELECT *
      FROM cteCoordinates
      ORDER BY cteCoordinates.x, cteCoordinates.y, cteCoordinates.z

    That doesn’t seem to work. Even forgetting the ordering, I have a mess.

    2018-07-02 14_58_53-SQLQuery4.sql - (local)_SQL2016.sandbox (vstsbuild (53))_ - Microsoft SQL Server

    At this point I took a break. Clearly I’m overlooking something in my five minutes of work.

    Debugging

    Let’s move the items around and get some ideas here. I’ll get the actual numbers and then the FizzBuzz results. When I print the coordinate values along with the decodes, I see this.

    2018-07-02 15_04_06-SQLQuery4.sql - (local)_SQL2016.sandbox (vstsbuild (53))_ - Microsoft SQL Server

    What’s my error? I’m not thinking of LAG() correctly. This is by row, and the lagging for the x and y coordinates (the first two columns), aren’t changing at the same rate.

    Aha.

    Let’s change this. I’ll setup a simpler CTE first, one that takes the values from 1-100 and returns the value as well as the FizzBuzz calculation. This gives me this code:

    WITH myTally (n)
    AS
    (
    SELECT n = ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
    FROM
    -- SQL Prompt formatting off
    (    VALUES (1) ,(2) ,(3) ,(4) ,(5) ,(6) ,(7) ,(8) ,(9) ,(10)) AS a (n)
         CROSS JOIN    ( VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10) ) AS b (n)
         CROSS JOIN    ( VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10) ) AS c (n)
    -- SQL Prompt formatting on
    ) ,
          cteFizzBuzz (x, n)
    AS
    (
    SELECT CASE
                WHEN (n / 3 != LAG(n / 3, 1, 0) OVER (ORDER BY n)) AND (n / 5 != LAG(n / 5, 1, 0) OVER (ORDER BY n))
                   THEN 'FizzBuzz'
                WHEN n / 3 != LAG(n / 3, 1, 0) OVER (ORDER BY n) THEN
                    'Fizz'
                WHEN n / 5 != LAG(n / 5, 1, 0) OVER (ORDER BY n) THEN
                    'Buzz'
                ELSE
                    CAST(n AS VARCHAR(8))
            END, n
    FROM myTally
    ) ,    cteCoordinates (x, y, z)

    Now that I have this, let’s build the coordinates now. I’ll use the first set of code above as an example, and cross join the cteFizzBuzz with itself. I’ll make this the third CTE.

    ) ,    cteFinal (x, y, z, a, b, c)
    AS
    (
    SELECT a.n, b.n, c.n, a.x, b.x, c.x
    FROM           cteFizzBuzz AS a
         CROSS JOIN cteFizzBuzz AS b
         CROSS JOIN cteFizzBuzz AS c
    )

    Note that I’m returning the original values (for sorting) and the calculated FizzBuzz values, which are characters. If I didn’t care about this, I could ignore the first few columns.

    In my outer query, I display the words, but order by the numbers.

    SELECT 
        c.a, c.b, c.c
      FROM cteFinal c
      ORDER BY c.x, c.y, c.z

    This gives me the answer (scrolled down to show a few cases).

    2018-07-02 15_12_18-SQLQuery5.sql - (local)_SQL2016.sandbox (vstsbuild (54))_ - Microsoft SQL Server

    On my machine, this takes about 4s to run. Not bad, and probably not optimal. The query plan is a mess, but this is for fun in a quick run, so let’s leave this  for now.

    2018-07-02 15_14_08-SQLQuery5.sql - (local)_SQL2016.sandbox (vstsbuild (54))_ - Microsoft SQL Server

    There are likely better solutions. I’m not an optimization guy out of the box. I get things done first, then I’ll go back and evaluate some time to tune things. In this case, adding in 3 more values (to 300) takes 1:58s. Going to 500 rows caused an SSMS out of memory error.

    If I send the results to a temp table, things are better:

    • 100x100x100 (1,000,000 rows) – 00:01
    • 300x300x300 (27,000,000 rows) – 00:06
    • 500 x 500 x 500 (125,000,000 rows)  – 00:29
    • 1000x1000x1000 (1,000,000,000 rows) – 6:48

    Note, don’t send results to the client if you don’t need to.

  • Finding Inconsistent Key Values–#SQLNewBlogger

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

    I was reading Iris Classon’s blog recently and ran across a post on her day at work. I think it’s a fantastic post that does help younger people understand what a job is like. I’m looking forward to seeing more.

    One quick thing struck me in the post, which was that she has clients that are missing key value settings in a table. I’ve dealt with this, and want to write more, but a quick post on just finding out that there are missing settings.

    Finding the Data Inconsistencies

    Each client should have a set of key value pairs in a settings table. However, because of application problems, not every client does. In the sample data (below), there are 8 clients, each of which should have 5 values in the GlobalSettings table. However, there are only 20 rows in this table when there should be 40.

    This is the type of issue I’ve had occur in an application, especially one that evolves over time. We add a key-value item to the application, which new clients get, but older ones are never populated. This is the same type of issue I saw in a post by Iris Classon.

    How can I find the items that don’t match? One simple way is to count the values, but include a HAVING clause to limit the results. If I want to see who has all the values, I can do this:

    SELECT gs.ClientID, COUNT(*)
    FROM dbo.GlobalSetting AS gs
    GROUP BY gs.ClientID
      HAVING COUNT(*) = 5

    In my sample data, this returns one row, for Client 1. If I change the HAVING clause to < 5, I get the other seven rows.

    2018-06-29 14_46_00-SQLQuery1.sql - (local)_SQL2016.sandbox (vstsbuild (56))_ - Microsoft SQL Server

    There are other considerations here, and this isn’t the best way that you might ensure you have the values. I might have a table, or a derived table that ensures I’m checking the right 5 values.

    I’ve written more about this in an article at SQLServerCentral.

    The Setup

    I built a couple quick tables and added data with this script. Note that there are 8 clients, and that each has a series of settings in a table. There are 5 possible settings (Position, Height, Weight, Number, College)

    CREATE TABLE Client
    (ClientKey INT IDENTITY (1,1) NOT NULL CONSTRAINT ClientPK PRIMARY KEY 
    , ClientName VARCHAR(200)
    , ClientStatus TINYINT)
    go
    CREATE TABLE GlobalSetting
    ( GlobalSettingKey INT IDENTITY (1,1) NOT NULL CONSTRAINT GlobalSettingPK PRIMARY KEY 
    , ClientID INT NOT NULL CONSTRAINT GlobalSettingFK_Client_ClientID FOREIGN KEY REFERENCES Client
    , GlobalSettingName VARCHAR(100)
    , GlobalSettingValue VARCHAR(500)
    )
    GO
    INSERT client VALUES ('Shaquil', 1), ('Von', 1), ('Bradley', 1), ('Shane', 2), ('Todd', 2), ('Jerrol', 3), ('Jeff', 3), ('Josey', 3)
    
     Position, College, Height, weight, number
    INSERT dbo.GlobalSetting
    (
        ClientID ,
        GlobalSettingName ,
        GlobalSettingValue
    )
    VALUES
      (1, 'Position', 'OLB')
    , (1, 'Weight', '250'),
    (1, 'College', 'CSU') , (1, 'Height', '74') , (1, 'Number', '48') , (2, 'Weight', '250') , (2, 'Number', '58') , (2, 'College', 'Texas A&M') , (3, 'Height', '76') , (3, 'Weight', '269') , (4, 'Position', 'OLB') , (4, 'College', 'Missouri') , (4, 'Height', '75') , (5, 'Weight', '230') , (5, 'Number', '51') , (5, 'College', 'Sacramento St') , (6, 'Weight', '235') , (6, 'Position', 'LB') , (7, 'Weight', '249') , (8, 'Number', '47')

    SQLNewBlogger

    This only took about 10 minutes to write, though I had to build the tables and data. Of these, the data took the longest, because I had to look up the values Winking smile.

    This is a basic example of checking data in a business situation. I might write about this in my job, perhaps showing a daily integrity check or a custom metric that I use to ensure the system is working. You can do the same thing and ensure that your data is correct.

  • Getting SQL Agent going for SQL Server on Linux–#SQLNewBlogger

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

    It’s actually a simple procedure, but I thought I’d write a short note to help me remember. The procedure is documented in Books Online.

    When I first started playing with this version, I noticed that SQL Agent was disabled. That’s not great, since SQL Agent is a great tool for various tasks in SQL Server. I can’t start the agent from here, as the underlying implementation is different, and I’m not really a host OS admin when connecting in SSMS.

    2018-05-23 15_19_26-SQLQuery4.sql - ubuntu.master (sa (56))_ - Microsoft SQL Server Management Studi

    After checking which patch level I was at (CU6), I changed to my Linux console, and ran the configuration utility. For Linux, this is mssql-conf.

    2018-05-23 15_20_34-Ubuntu 64-bit SQL Server .210 - VMware Workstation

    The next step is to run the command listed to restart the system. I actually just ran

    sudo sysmctl restart mssql-server

    Once this was done, I refreshed SSMS.

    2018-05-23 15_22_52-SQLQuery4.sql - ubuntu.master (sa (56))_ - Microsoft SQL Server Management Studi

    This appears to work, but let’s test it. I’ll create a new job that does a backup of a database. This should be simple, and I’ll use defaults, just a filename for a full backup. I’ll use this command:

    backup database dbaadmin to disk = ‘dbaadmin.bak’ with init

    I save the job and run it. Sure enough, I have a backup.

    2018-05-23 15_28_37-Ubuntu 64-bit SQL Server .210 - VMware Workstation

    Tada, now I can move forward with work.

    SQLNewBlogger

    This is a fairly simple thing to do, but the writing helps me remember, but more importantly, I can document that I’ve done some learning here and experimenting.

    The next person thinking about interviewing you wants to know that you can learn and solve problems.

  • Finding Tabs in SSMS–#SQLNewBlogger

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

    Someone posted this as a question and I thought it was worth noting. I use SQL Prompt for formatting, and never worry if there are tabs in code, but I know there are people without this amazing tool.

    I added some tabs to a script and want to replace them.

    2018-05-04 09_50_15-SQLQuery1.sql - (local)_SQL2016.sandbox (PLATO_Steve (60))_ - Microsoft SQL Serv

    I hit CTRL+H for the search and replace toolbar. The tab character is represented by a \t in a regular expression. I enter that, and then enter 4 spaces in the replace text box. I do need to click the “Use Regular Expressions” button, which is the one my arrow is on in the image below. It’s a little box with an asterisk in it

    2018-05-04 09_51_39-SQLQuery1.sql - (local)_SQL2016.sandbox (PLATO_Steve (60))_ - Microsoft SQL Serv

    When I do that, tabs are highlighted in SSMS.

    2018-05-04 09_53_32-SQLQuery1.sql - (local)_SQL2016.sandbox (PLATO_Steve (60))_ - Microsoft SQL Serv

    If I click “replace all”, I see 4 replacements, and if I check, the tabs are gone and spaces live.

    2018-05-04 09_51_48-SQLQuery1.sql - (local)_SQL2016.sandbox (PLATO_Steve (60))_ - Microsoft SQL Serv

    SQLNewBlogger

    This is a quick post, an example of what you can do to show you’re building better work habits and learning about your tools.