Advent of Code 2017 Day 2–#SQLNewBlogger

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

This is day 2 of the Advent of Code 2017. If you want to read about the puzzles, start with Day 1. Here is a look at Day 2, which deals with checksums of rows. However, it’s not just rows, but rows and columns.

Part I

In part 1, we are given a series of numbers in rows and columns, essentially a spreadsheet. We need to look at each row and compare all the values in columns.

This is slightly tricky for T-SQL. The input is a series of text values, so I cheated slightly and inserted those as rows into a table (I like tables). If I were taking this as a programming item, I’d have an import process, so I’ll do that here inline.

I have some idempotent logic here as I was testing and didn’t want to recreate tables and inserts, nor did I want to comment/uncomment things in/out.

IF NOT EXISTS( SELECT name FROM sys.sysobjects AS s WHERE name = ‘Day2’)
CREATE TABLE Day2
( DataRow VARCHAR(1000)
)
GO
IF (SELECT COUNT(*) FROM dbo.Day2 AS d) = 0
INSERT Day2 VALUES
( ‘179    2358    5197    867    163    4418    3135    5049    187    166    4682    5080    5541    172    4294    1397’),
( ‘2637    136    3222    591    2593    1982    4506    195    4396    3741    2373    157    4533    3864    4159    142’),
( ‘1049    1163    1128    193    1008    142    169    168    165    310    1054    104    1100    761    406    173’),
( ‘200    53    222    227    218    51    188    45    98    194    189    42    50    105    46    176’),
( ‘299    2521    216    2080    2068    2681    2376    220    1339    244    605    1598    2161    822    387    268’),
( ‘1043    1409    637    1560    970    69    832    87    78    1391    1558    75    1643    655    1398    1193’),
( ’90    649    858    2496    1555    2618    2302    119    2675    131    1816    2356    2480    603    65    128′),
( ‘2461    5099    168    4468    5371    2076    223    1178    194    5639    890    5575    1258    5591    6125    226’),
( ‘204    205    2797    2452    2568    2777    1542    1586    241    836    3202    2495    197    2960    240    2880’),
( ‘560    96    336    627    546    241    191    94    368    528    298    78    76    123    240    563’),
( ‘818    973    1422    244    1263    200    1220    208    1143    627    609    274    130    961    685    1318’)

The input might be different for you, so you’d have to do your own work.

Splitting a row of data into more rows seems like something easier done with the STRING_SPLIT function, so I’ll use that. This will give me a series of values from the table. I decided to use an inline TVF for this, since it’s a bit easier to read.

The trick here is that the data is tab separated, and \t doesn’t work in SQL Server. However, CHAR(9) works, so let’s use that. Here’s the function:

CREATE FUNCTION AdventChecksum ( @input NVARCHAR(200))
RETURNS TABLE
AS RETURN
SELECT diff = MAX(CAST(myint AS INT)) – MIN(CAST(myint AS INT))
  FROM (SELECT myint = CAST(value AS INT) FROM STRING_SPLIT(@input, CHAR(9))) AS ss
GO

I need to get the difference between the largest and smallest values. STRING_SPLIT will return strings, so I cast this to an INT, then use MAX() and MIN() for the row, subtract one from the other, and return that.

Now I have a way to get a row checksum, so let’s get the entire table. We’ll do that with a CROSS APPLY, and then sum up all the values returned:

SELECT SUM(b.diff)
  FROM day2 a
  CROSS APPLY dbo.AdventChecksum (a.DataRow) b

I guess it works, because I got the right value from a test set, and the puzzle shows as solved. Smile

Part II

Part II adds a twist. Now I don’t need to just compare two values, I actually need to see if any of the values are equally divisible by the other. This means I need to compare all values against each other.

The STRING_SPLIT() function still works well here, however, I need all combinations of the values divided by each other. An evenly divisible set of numbers would have this pseudocode:

INT(a) / INT(b) = FLOAT(a) / FLOAT(b)

Or, there’s another way to look at this. The remainder is 0, so the remainder of the values, using the modulo (%) function, is 0.

One last trick, each number is divisible by itself, so let’s avoid those. The instructions don’t mention the possibility that the only values evenly divisible are matches, so we’ll assume that’s not the case. Checking inputs, this appears to be OK.

I’ll create a new function, where I CROSS JOIN the STRING_SPLIT to get all combinations.

CREATE FUNCTION AdventChecksum2 ( @input NVARCHAR(200))
RETURNS TABLE
AS RETURN
SELECT divmatch = CASE WHEN (CAST(a.value AS INT) % CAST(b.value AS INT)) = 0 AND a.value <> b.value
        THEN (CAST(a.value AS INT) / CAST(b.value AS INT))
      ELSE 0
      end
       FROM STRING_SPLIT(@input, CHAR(9)) a
   CROSS JOIN STRING_SPLIT(@input, CHAR(9)) b
GO

Now I cross apply as in Part I and sum the values, which works. The puzzle is solved.

About way0utwest

Editor, SQLServerCentral
This entry was posted in Blog and tagged , , . Bookmark the permalink.