Breaking my Non-Equi Join cherry
There’s few SQL techniques you seem to keep in the cupboard gathering dust. I don’t think I’ve ever needed to use RIGHT JOIN outside of the classroom. I can recall using FULL OUTER JOIN, just once, to show an employer how not-in-sync their “integrated system” was. Today I broke my professional Non-Equi JOIN cherry!
I basically had one table of appointments and another table providing appointment banding by a date range. The banding wasn’t consistent, spanning weeks and months, so there was no possibility of using an equi-join or doing anything with datetime arithmetic. Perhaps that non-equi join thing I remember reading in the textbook will do?
Here’s a quick run-through of a similar situation. Create some tables and insert some test data. (This first example is in SQL Server 2005)
CREATE TABLE dbo.Appointments
(
AppointId INTEGER IDENTITY (1,1) NOT NULL PRIMARY KEY CLUSTERED,
AppointmentDateTime DATETIME NOT NULL,
FirstName VARCHAR(50) NOT NULL,
LastName VARCHAR(50) NOT NULL,
Reason VARCHAR(50) NULL
);
GO
CREATE TABLE AppointmentBands
(
AppointBandId INTEGER IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
StartDateTime DATETIME NOT NULL,
EndDateTime DATETIME NOT NULL
);
GO
-- Insert Test Data
INSERT INTO dbo.Appointments
(
AppointmentDateTime,
FirstName,
LastName,
Reason
)
SELECT '2010-02-18T17:00:00',
'Rhys',
'Campbell',
'Eye Test'
UNION ALL
SELECT '2010-02-23T12:00:00',
'John',
'Smith',
'Ear Test'
UNION ALL
SELECT '2010-02-28T14:00:00',
'Frank',
'Zappa',
'Eye Test';
GO
INSERT INTO dbo.AppointmentBands
(
StartDateTime,
EndDateTime
)
SELECT '2010-02-18T00:00:00',
'2010-02-22T23:59:59'
UNION ALL
SELECT '2010-02-23T00:00:00',
'2010-02-27T23:59:59'
UNION ALL
SELECT '2010-02-28T00:00:00',
'2010-03-01T23:59:59';
I needed to identify which appointment band each record belonged to. It’s a trivial example here, but the real life situation involved hundreds of thousands of appointments and a few thousand appointment bands. The solution involved using a BETWEEN in the join clause.
SELECT app.*, band.AppointBandId
FROM dbo.Appointments app
INNER JOIN dbo.AppointmentBands band
ON app.AppointmentDateTime BETWEEN band.StartDateTime AND band.EndDateTime;
Such a simple, elegant, single query solution. Perhaps we need to dust these things off from time-to-time?
Here’s the same example, a’la MySQL
CREATE TABLE Appointments
(
AppointId INTEGER AUTO_INCREMENT NOT NULL PRIMARY KEY,
AppointmentDateTime DATETIME NOT NULL,
FirstName VARCHAR(50) NOT NULL,
LastName VARCHAR(50) NOT NULL,
Reason VARCHAR(50) NULL
);
CREATE TABLE AppointmentBands
(
AppointBandId INTEGER AUTO_INCREMENT NOT NULL PRIMARY KEY,
StartDateTime DATETIME NOT NULL,
EndDateTime DATETIME NOT NULL
);
-- Insert Test Data
INSERT INTO Appointments
(
AppointmentDateTime,
FirstName,
LastName,
Reason
)
SELECT '2010-02-18T17:00:00',
'Rhys',
'Campbell',
'Eye Test'
UNION ALL
SELECT '2010-02-23T12:00:00',
'John',
'Smith',
'Ear Test'
UNION ALL
SELECT '2010-02-28T14:00:00',
'Frank',
'Zappa',
'Eye Test';
INSERT INTO AppointmentBands
(
StartDateTime,
EndDateTime
)
SELECT '2010-02-18T00:00:00',
'2010-02-22T23:59:59'
UNION ALL
SELECT '2010-02-23T00:00:00',
'2010-02-27T23:59:59'
UNION ALL
SELECT '2010-02-28T00:00:00',
'2010-03-01T23:59:59';
SELECT app.*, band.AppointBandId
FROM Appointments app
INNER JOIN AppointmentBands band
ON app.AppointmentDateTime BETWEEN band.StartDateTime AND band.EndDateTime;