Tuesday, October 30, 2012

NULLIF (post, blank)


I revisited a neat little function in SQL Sever called NULLIF, and I thought it might be worth mentioning.  It came in very handy today when I used it to build a query on some summary data, it handled any divide-by-zero errors without much fuss.

NULLIF( param1, param2)

The function will return NULL if the comparsion of the two parameters is equal, otherwise it will just use the first parameter. 

select isnull( revenue / NULLIF(volume,0), 0 ) as [price] from sales_summary


A quick breakdown:

  1. NULLIF(volume, 0)  -- if volume is 0 then this function will return NULL, otherwise it will return the volume
  2. revenue / NULLIF(volume,0)  -- this is just my calculation (revenue/volume = price), if volume is zero the entire thing will return NULL (I like to think of it as exploding out as NULL)
  3. ISNULL( revenue / NULLIF(volume,0), 0 ) -- The entire thing is wrapped by a ISNULL; if NULL is returned by NULLIF, it returns NULL for the entire calculation, which is handled by the ISNULL
An example:

begin tran t1

-- build some test summary data
declare @sales_summary table (
period int,
revenue money,
volume numeric(18,4)
)
;

insert into @sales_summary
select 1, 100.00, 50
union all
select 2, 0, 0
union all
select 3, 5000, 2500
union all
select 4, 30000, 20
;

select * from @sales_summary;

select
period,
revenue,
volume,
--(revenue / volume) as price --This will cause Divide-By-Zero for period 2
( revenue / NULLIF(volume,0) ) as [ex], --example showing the NULLIF returning NULL for period 2
isnull( revenue / NULLIF(volume,0), 0 ) as [price] -- Using the ISNULL to handle NULL and show 0
from @sales_summary
;

rollback tran t1

Tuesday, October 2, 2012

SQL Date table (or Calendar table)

I came across a request to show data out of sql for every day in a month.  I know that I can't assume that my data will have results for every day, so I have to show days where there was no activity.  An easy way to achieve this is in SQL is with a Date table; sometimes they are also called Calendar tables. 

Man am I ever glad I paid attention when someone told me about these things!  It makes building queries against dates much faster. 


Here is an example end result.  (I included the build scripts below)

SELECT
dt.date,
o.order_date,
o.order_number

FROM
Date.DateTable dt
left join dbo.Orders o on dt.date = o.order_date

WHERE
dt.date between '9/1/2012' and '9/5/2012'
;


This will show every day 9/1 - 9/5, and if there is an order that particular day, it will show those as well.  Otherwise the query will show NULL for o.order_date and o.order_number.  I did that just to show what was going on in the left join.






There are a ton of examples out there on how to build Date tables, and this is just my version.  The idea is that you build this table once.  Make sure you cover all the dates and date-parts you expect to be asked for. For example, sometimes have to deal with Julian dates, so I incorporated them into the date table to handle those instances.

This script is a copy of one I used a few years ago, and contains a few user-defined functions, schemas, etc.  So I wouldn't just copy-paste if I were you!

The build script:

--BEGIN TRAN T1;
--drop table Date.DateTable

-- Create the table (I use a "DATE" schema for date-related objects)
-- Some of these fields are just things I know the client asks for a lot.
CREATE TABLE Date.DateTable
(
date_id INT PRIMARY KEY,
[date] datetime,
[month] tinyint,
[day] tinyint,
[year] char(4),
[quarter] tinyint,
[week] tinyint,
[year_day] smallint,
[week_day] tinyint,
[is_first_day_in_month] tinyint,
[is_last_day_in_month] tinyint,
string_date varchar(10),
formatted_date varchar(30),
day_name varchar(10),
month_name varchar(12),
julian_date int,
date_endOfDay datetime,
firstDayOfMonth datetime,
lastDayOfMonth datetime
)
;

-- Setup the date range I want to insert into the table
DECLARE @Date DATETIME;
declare @end_date datetime;

SET @Date = '1/1/1970';
set @end_date = '12/31/2100';

-- Just a count of the number of rows I should expect
select datediff(dd, @Date, @end_date) -- 47846

-- I use a while statement over other options (cursor) because
-- I feel they are fairly easy to read and you only have to run this script once anyway.
WHILE @Date <= @end_date
BEGIN

-- My insert statement uses a few user-defined functions,
-- but you can figure out what they do fairly easily.
INSERT INTO Date.DateTable (
date_id,
[date],
[month],
[day],
[year],
[quarter],
[week],
[year_day],
[week_day],
[is_first_day_in_month],
[is_last_day_in_month],
string_date,
formatted_date,
day_name,
month_name,
julian_date,
date_endOfDay,
firstDayOfMonth,
lastDayOfMonth
)
select
CONVERT(VARCHAR(8), @Date, 112) as [date_id],
@Date as [date],
DATEPART(mm, @Date) as [month],
DATEPART(dd, @Date) as [day],
year(@Date) as [year],
DATEPART(qq, @Date) as [Quarter],
DATEPART(ww, @Date) as [week],
DATEPART(dy, @Date) as [year_day],
DATEPART(dw, @Date) as [week_day],
case when (datediff(dd, @Date, DATE.FirstDayOfMonth(@Date)) = 0) then 1 else 0 end as [is_first_day_in_month],
case when (datediff(dd, @Date, DATE.LastDayOfMonth(@Date)) = 0) then 1 else 0 end as [is_last_day_in_month],
CONVERT(VARCHAR(10), @date, 101) as [string_date],
DATENAME(MM, @date) + RIGHT(CONVERT(VARCHAR(12), @date, 107), 9) as [formatted_date],
DATENAME(dw, @Date) as [day_name],
DATENAME(mm, @Date) as [month_name],
DATE.Convert_to_Julian(@Date) as [julian_date], -- an int representation of a date, used by client
DATE.EndOfDay(@Date) as [date_endOfDay], -- the Last possible (milli) second of the day
DATE.FirstDayOfMonth(@Date) as [firstDayOfMonth],
DATE.EndOfDay(DATE.LastDayOfMonth(@Date)) as [lastDayOfMonth]
;

SET @Date = DATEADD(dd, 1, @Date);
END

-- Hopefully this will equal 47846
select count(*) from Date.DateTable

--ROLLBACK TRAN T1;



Just in case you're interested, here is the build script for the example (i.e. the Orders table):

CREATE TABLE [dbo].[Orders](
[order_id] [int] IDENTITY(1,1) NOT NULL,
[order_date] [datetime] NOT NULL,
[order_number] [nvarchar](50) NOT NULL
) ON [PRIMARY]
;

insert into dbo.Orders (order_date, order_number)
select '9/1/2012', 'Order 1'
union all
select '9/5/2012', 'Order 2'
;

Monday, June 4, 2012

How I got started in Computers (part 1)


I've recently read some blogs from people telling the story of how they got started in computers.  I decided join in and organize my thoughts on the subject.  After I started writing down my thoughts, I decided to break this up a little bit.  My first few computers fulfilled different things in my life that I feel are working mentioning.


Our first computer was a Timex Sinclair 1000.  It was a little computer that only had a keyboard and hooked directly to the TV.  It had no storage outside of the on-board RAM, so you had to save your programs to a cassette tape as audio.

We had a vanilla tape recorder that we connected to the computer, and then we would record the audio of the program to cassette.  I remember a few times playing an unlabeled tape on our stereo and discovering it was a program when the sound was all beeps and fuzz. 

Running an existing program involved reversing the process: hooking up the cassette player and playing the audio while the computer "listened" and converted all that sound back into a program.  Sometimes it worked, sometimes it didn't work.

Years later I learned that some radio stations would broadcast programs over the air.  You would record the audio off the radio and then load the program from the tape you recorded.  I'm not sure if any of the radio stations near me did something like this or if I was too young to know it was occurring.  Either way the thought that this was possible fascinates me.

My brother and I had no games or programs to speak of other than a book that I believe he found in a store.  It was called "51 Game Programs for the Timex Sinclair 1000 and 1500", and it was simply a list of programs and the code you had to type in to get it to work.  Some of them were small, but naturally all the cool looking games were pages upon pages long.  My brother and I would lie on the floor and take turns typing in those programs.  I remember a car racing game that we just couldn't get right – the picture showed the road with twists and turns, but our version only showed the road as straight as an arrow.  We suffered a lot over that program – multiple failures trying to save to tape, retyping to try to get the program right, etc.

Coincidentally I still have that book on my shelf!  My mother tried to sell the computer at a garage sale sometime in the mid-80's and some woman begged my mother to sell her JUST the book (she didn't want the computer).  My mother refused and for some reason my 10-ish old mind decided I would keep the book.  I'm so glad I did, it proudly sits next to all my other computer books on my bookshelf.

A few years ago I thought I had an "Ah-ha!" moment when I remembered the entire audio-saving technique of the Timex.  I thought it might be useful for communicating with a group of others at a ski resort.  This was before smartphones, but I thought you could replace those little walkie-talkies people carried around with one that also had a small keyboard.  Then you could leave a text message using those devices using the same audio bandwidth the radios already used.

Sunday, May 20, 2012

Facebook money

Here's hoping that all these new Facebook millionaires don't just create new wealth elsewhere in the economy but also help extinguish the notion that your kids have to grow up to be doctors and lawyers in order to be successful.

Monday, May 14, 2012

Visual Studio 2010 comment syntax


I just want to air an annoyance with Visual Studio 2010 and how it formats comments.



Code I want to comment:




I select the code and hit the comment button:


(By the way, Ctrl+K, Ctrl+C is the worst key combination I've ever heard of)

The Result:



Why can't I get a result where the comment tags are on their own lines? 
This seems much cleaner to me.  It also is the same syntax the Microsoft doc uses.