SQL Art: I Made a Christmas Card in tempdb

Most people use SQL Server to count things.
I used it to draw a Christmas tree. In a database. On purpose.

If you’ve ever stared at SSMS and thought “this could use more festive chaos”, this is for you.

What we’re doing

We’re going to:

  • Create a temp table in tempdb

  • Insert a bunch of geometry polygons that form a tree, trunk, and star

  • Stamp “MERRY CHRISTMAS” on top using more polygons

  • Render “AND A HAPPY / NEW YEAR” using a tiny pixel font built from squares

  • Add random baubles because discipline is overrated

  • Union it all so SSMS shows one clean shape

Then SSMS becomes your art gallery.

The only requirement

You need SQL Server with the geometry type available (so basically modern SQL Server). SSMS helps because it can render spatial results visually.

Run this

Paste the whole thing into SSMS and execute it. Then click the Spatial results tab (or the spatial viewer) and enjoy your database becoming a greeting card.

USE tempdb; GO DROP TABLE IF EXISTS #xmasTREE; CREATE TABLE #xmasTREE (shape geometry); -------------------------------------------------------------------------------- -- 1) Xmas tree (insert ONCE) -------------------------------------------------------------------------------- INSERT INTO #xmasTREE(shape) VALUES (geometry::STGeomFromText('POLYGON((4 0, 0 0, 4 2, 1 2, 4 4, 1 4, 4 6, 2 6, 5 10, 8 6, 6 6, 9 4, 6 4, 9 2, 6 2, 10 0, 4 0))', 0)), (geometry::STGeomFromText('POLYGON((3.5 0, 4 -1, 6 -1, 6.5 0, 3.5 0))', 0)), (geometry::STGeomFromText('POLYGON((5 9.5, 4.5 9.25, 4.6 9.9, 4.1 10.2, 4.8 10.2, 5 10.9, 5.2 10.2, 5.9 10.2, 5.4 9.9, 5.5 9.25, 5 9.5))', 0)), (geometry::STGeomFromText('POLYGON((2 5.5, 1.5 5.25, 1.6 5.9, 1.1 6.2, 1.8 6.2, 2 6.9, 2.2 6.2, 2.9 6.2, 2.4 5.9, 2.5 5.25, 2 5.5))', 0)), (geometry::STGeomFromText('POLYGON((8 5.5, 7.5 5.25, 7.6 5.9, 7.1 6.2, 7.8 6.2, 8 6.9, 8.2 6.2, 8.9 6.2, 8.4 5.9, 8.5 5.25, 8 5.5))', 0)), (geometry::STGeomFromText('POLYGON((1 3.5, 0.5 3.25, 0.6 3.9, 0.1 4.2, 0.8 4.2, 1 4.9, 1.2 4.2, 1.9 4.2, 1.4 3.9, 1.5 3.25, 1 3.5))', 0)), (geometry::STGeomFromText('POLYGON((9 3.5, 8.5 3.25, 8.6 3.9, 8.1 4.2, 8.8 4.2, 9 4.9, 9.2 4.2, 9.9 4.2, 9.4 3.9, 9.5 3.25, 9 3.5))', 0)), (geometry::STGeomFromText('POLYGON((1 1.5, 0.5 1.25, 0.6 1.9, 0.1 2.2, 0.8 2.2, 1 2.9, 1.2 2.2, 1.9 2.2, 1.4 1.9, 1.5 1.25, 1 1.5))', 0)), (geometry::STGeomFromText('POLYGON((9 1.5, 8.5 1.25, 8.6 1.9, 8.1 2.2, 8.8 2.2, 9 2.9, 9.2 2.2, 9.9 2.2, 9.4 1.9, 9.5 1.25, 9 1.5))', 0)), (geometry::STGeomFromText('POLYGON((0 -0.5, -0.5 -0.75, -0.4 -0.1, -0.9 0.2, -0.2 0.2, 0 0.9, 0.2 0.2, 0.9 0.2, 0.4 -0.1, 0.5 -0.75, 0 -0.5))', 0)), (geometry::STGeomFromText('POLYGON((10 -0.5, 9.5 -0.75, 9.6 -0.1, 9.1 0.2, 9.8 0.2, 10 0.9, 10.2 0.2, 10.9 0.2, 10.4 -0.1, 10.5 -0.75, 10 -0.5))', 0)), (geometry::STGeomFromText('POLYGON((5 -2, 4.5 -2, 4.5 -1, 5 -1, 5.5 -1, 5.5 -2, 5 -2))', 0)); -------------------------------------------------------------------------------- -- 2) Top text "MERRY CHRISTMAS" (your existing polygons) -------------------------------------------------------------------------------- INSERT INTO #xmasTREE(shape) VALUES (geometry::STGeomFromText('POLYGON((-2 11, -2 12, -1.75 12, -1.5 11.5, -1.25 12, -1 12, -1 11, -1.25 11, -1.25 11.7, -1.5 11.2, -1.75 11.7, -1.75 11, -2 11))', 0)), (geometry::STGeomFromText('POLYGON((-1 11, -1 12, 0 12, 0 11.8, -0.75 11.8, -0.75 11.6, -0.25 11.6, -0.25 11.4, -0.75 11.4, -0.75 11.2, 0 11.2, 0 11, -1 11))', 0)), (geometry::STGeomFromText('POLYGON((0 11, 0 12, 1 12, 1 11.5, 0.4 11.5, 1 11, 0.7 11, 0.2 11.4, 0.2 11, 0 11),(0.2 11.8, 0.8 11.8, 0.8 11.7, 0.2 11.7, 0.2 11.8))', 0)), (geometry::STGeomFromText('POLYGON((1 11, 1 12, 2 12, 2 11.5, 1.4 11.5, 2 11, 1.7 11, 1.2 11.4, 1.2 11, 1 11),(1.2 11.8, 1.8 11.8, 1.8 11.7, 1.2 11.7, 1.2 11.8))', 0)), (geometry::STGeomFromText('POLYGON((2 12, 2.2 12, 2.5 11.6, 2.8 12, 3 12, 2.6 11.5, 2.6 11, 2.4 11, 2.4 11.5, 2 12))', 0)), (geometry::STGeomFromText('POLYGON((4 11, 4 12, 5 12, 5 11.8, 4.25 11.8, 4.25 11.2, 5 11.2, 5 11, 4 11))', 0)), (geometry::STGeomFromText('POLYGON((5 11, 5 12, 5.2 12, 5.2 11.6, 5.8 11.6, 5.8 12, 6 12, 6 11, 5.8 11, 5.8 11.4, 5.2 11.4, 5.2 11, 5 11))', 0)), (geometry::STGeomFromText('POLYGON((6 11, 6 12, 7 12, 7 11.5, 6.4 11.5, 7 11, 6.7 11, 6.2 11.4, 6.2 11, 6 11),(6.2 11.8, 6.8 11.8, 6.8 11.7, 6.2 11.7, 6.2 11.8))', 0)), (geometry::STGeomFromText('POLYGON((7.2 11, 7.2 11.2, 7.4 11.2, 7.4 11.8, 7.2 11.8, 7.2 12, 7.8 12, 7.8 11.8, 7.6 11.8, 7.6 11.2, 7.8 11.2, 7.8 11, 7.2 11))', 0)), (geometry::STGeomFromText('POLYGON((8 11, 8 11.2, 8.8 11.2, 8.8 11.4, 8 11.4, 8 12, 9 12, 9 11.8, 8.2 11.8, 8.2 11.6, 9 11.6, 9 11, 8 11))', 0)), (geometry::STGeomFromText('POLYGON((9 11.8, 9 12, 10 12, 10 11.8, 9.6 11.8, 9.6 11, 9.4 11, 9.4 11.8, 9 11.8))', 0)), (geometry::STGeomFromText('POLYGON((10 11, 10 12, 10.25 12, 10.5 11.5, 10.75 12, 11 12, 11 11, 10.75 11, 10.75 11.7, 10.5 11.2, 10.25 11.7, 10.25 11, 10 11))', 0)), (geometry::STGeomFromText('POLYGON((11 11, 11 12, 12 12, 12 11, 11.75 11, 11.75 11.3, 11.25 11.3, 11.25 11, 11 11),(11.25 11.5, 11.25 11.8, 11.75 11.8, 11.75 11.5, 11.25 11.5))', 0)), (geometry::STGeomFromText('POLYGON((12 11, 12 11.2, 12.8 11.2, 12.8 11.4, 12 11.4, 12 12, 13 12, 13 11.8, 12.2 11.8, 12.2 11.6, 13 11.6, 13 11, 12 11))', 0)); -------------------------------------------------------------------------------- -- 3) Bottom text "AND A HAPPY" / "NEW YEAR" (renderer) -- IMPORTANT: no CREATE TABLE here, no tree insert here -------------------------------------------------------------------------------- DECLARE @Scale2 decimal(10,4) = 0.18; DECLARE @Gap2 decimal(10,4) = @Scale2; DECLARE @Step2 decimal(10,4) = (5 * @Scale2) + @Gap2; DECLARE @LineA nvarchar(50) = N'AND A HAPPY'; DECLARE @LineB nvarchar(50) = N'NEW YEAR'; DECLARE @XA decimal(10,4) = -0.5, @YA decimal(10,4) = -3.4; DECLARE @XB decimal(10,4) = 0.5, @YB decimal(10,4) = -4.6; DECLARE @Font2 TABLE (ch nchar(1), r tinyint, c tinyint); -- Letters needed for these lines: A N D H P Y E W R (already defined here) -- A INSERT @Font2 VALUES (N'A',0,1),(N'A',0,2),(N'A',0,3),(N'A',1,0),(N'A',1,4),(N'A',2,0),(N'A',2,4), (N'A',3,0),(N'A',3,1),(N'A',3,2),(N'A',3,3),(N'A',3,4),(N'A',4,0),(N'A',4,4), (N'A',5,0),(N'A',5,4),(N'A',6,0),(N'A',6,4); -- N INSERT @Font2 VALUES (N'N',0,0),(N'N',0,4),(N'N',1,0),(N'N',1,1),(N'N',1,4),(N'N',2,0),(N'N',2,2),(N'N',2,4), (N'N',3,0),(N'N',3,3),(N'N',3,4),(N'N',4,0),(N'N',4,4),(N'N',5,0),(N'N',5,4),(N'N',6,0),(N'N',6,4); -- D INSERT @Font2 VALUES (N'D',0,0),(N'D',0,1),(N'D',0,2),(N'D',0,3), (N'D',1,0),(N'D',1,4),(N'D',2,0),(N'D',2,4),(N'D',3,0),(N'D',3,4), (N'D',4,0),(N'D',4,4),(N'D',5,0),(N'D',5,4), (N'D',6,0),(N'D',6,1),(N'D',6,2),(N'D',6,3); -- H INSERT @Font2 VALUES (N'H',0,0),(N'H',0,4),(N'H',1,0),(N'H',1,4),(N'H',2,0),(N'H',2,4), (N'H',3,0),(N'H',3,1),(N'H',3,2),(N'H',3,3),(N'H',3,4), (N'H',4,0),(N'H',4,4),(N'H',5,0),(N'H',5,4),(N'H',6,0),(N'H',6,4); -- P INSERT @Font2 VALUES (N'P',0,0),(N'P',0,1),(N'P',0,2),(N'P',0,3), (N'P',1,0),(N'P',1,4),(N'P',2,0),(N'P',2,4), (N'P',3,0),(N'P',3,1),(N'P',3,2),(N'P',3,3), (N'P',4,0),(N'P',5,0),(N'P',6,0); -- Y INSERT @Font2 VALUES (N'Y',0,0),(N'Y',0,4),(N'Y',1,0),(N'Y',1,4),(N'Y',2,1),(N'Y',2,3), (N'Y',3,2),(N'Y',4,2),(N'Y',5,2),(N'Y',6,2); -- E INSERT @Font2 VALUES (N'E',0,0),(N'E',0,1),(N'E',0,2),(N'E',0,3),(N'E',0,4), (N'E',1,0),(N'E',2,0), (N'E',3,0),(N'E',3,1),(N'E',3,2),(N'E',3,3), (N'E',4,0),(N'E',5,0), (N'E',6,0),(N'E',6,1),(N'E',6,2),(N'E',6,3),(N'E',6,4); -- W INSERT @Font2 VALUES (N'W',0,0),(N'W',0,4),(N'W',1,0),(N'W',1,4),(N'W',2,0),(N'W',2,4), (N'W',3,0),(N'W',3,2),(N'W',3,4),(N'W',4,0),(N'W',4,2),(N'W',4,4), (N'W',5,0),(N'W',5,2),(N'W',5,4),(N'W',6,1),(N'W',6,3); -- R INSERT @Font2 VALUES (N'R',0,0),(N'R',0,1),(N'R',0,2),(N'R',0,3), (N'R',1,0),(N'R',1,4),(N'R',2,0),(N'R',2,4), (N'R',3,0),(N'R',3,1),(N'R',3,2),(N'R',3,3), (N'R',4,0),(N'R',4,2),(N'R',5,0),(N'R',5,3),(N'R',6,0),(N'R',6,4); DECLARE @i2 int, @len2 int, @ch2 nchar(1); -- Line A SET @i2 = 1; SET @len2 = LEN(@LineA); WHILE @i2 <= @len2 BEGIN SET @ch2 = SUBSTRING(@LineA, @i2, 1); IF @ch2 <> N' ' BEGIN INSERT INTO #xmasTREE(shape) SELECT geometry::STGeomFromText( CONCAT( 'POLYGON((', CONVERT(varchar(32), CONVERT(decimal(18,6), @XA + ((@i2-1) * @Step2) + (c * @Scale2))), ' ', CONVERT(varchar(32), CONVERT(decimal(18,6), @YA + ((6-r) * @Scale2))), ',', CONVERT(varchar(32), CONVERT(decimal(18,6), @XA + ((@i2-1) * @Step2) + ((c+1) * @Scale2))), ' ', CONVERT(varchar(32), CONVERT(decimal(18,6), @YA + ((6-r) * @Scale2))), ',', CONVERT(varchar(32), CONVERT(decimal(18,6), @XA + ((@i2-1) * @Step2) + ((c+1) * @Scale2))), ' ', CONVERT(varchar(32), CONVERT(decimal(18,6), @YA + ((6-r+1) * @Scale2))), ',', CONVERT(varchar(32), CONVERT(decimal(18,6), @XA + ((@i2-1) * @Step2) + (c * @Scale2))), ' ', CONVERT(varchar(32), CONVERT(decimal(18,6), @YA + ((6-r+1) * @Scale2))), ',', CONVERT(varchar(32), CONVERT(decimal(18,6), @XA + ((@i2-1) * @Step2) + (c * @Scale2))), ' ', CONVERT(varchar(32), CONVERT(decimal(18,6), @YA + ((6-r) * @Scale2))), '))' ), 0) FROM @Font2 WHERE ch = @ch2; END SET @i2 += 1; END -- Line B SET @i2 = 1; SET @len2 = LEN(@LineB); WHILE @i2 <= @len2 BEGIN SET @ch2 = SUBSTRING(@LineB, @i2, 1); IF @ch2 <> N' ' BEGIN INSERT INTO #xmasTREE(shape) SELECT geometry::STGeomFromText( CONCAT( 'POLYGON((', CONVERT(varchar(32), CONVERT(decimal(18,6), @XB + ((@i2-1) * @Step2) + (c * @Scale2))), ' ', CONVERT(varchar(32), CONVERT(decimal(18,6), @YB + ((6-r) * @Scale2))), ',', CONVERT(varchar(32), CONVERT(decimal(18,6), @XB + ((@i2-1) * @Step2) + ((c+1) * @Scale2))), ' ', CONVERT(varchar(32), CONVERT(decimal(18,6), @YB + ((6-r) * @Scale2))), ',', CONVERT(varchar(32), CONVERT(decimal(18,6), @XB + ((@i2-1) * @Step2) + ((c+1) * @Scale2))), ' ', CONVERT(varchar(32), CONVERT(decimal(18,6), @YB + ((6-r+1) * @Scale2))), ',', CONVERT(varchar(32), CONVERT(decimal(18,6), @XB + ((@i2-1) * @Step2) + (c * @Scale2))), ' ', CONVERT(varchar(32), CONVERT(decimal(18,6), @YB + ((6-r+1) * @Scale2))), ',', CONVERT(varchar(32), CONVERT(decimal(18,6), @XB + ((@i2-1) * @Step2) + (c * @Scale2))), ' ', CONVERT(varchar(32), CONVERT(decimal(18,6), @YB + ((6-r) * @Scale2))), '))' ), 0) FROM @Font2 WHERE ch = @ch2; END SET @i2 += 1; END -------------------------------------------------------------------------------- -- 4) Baubles (optional) -------------------------------------------------------------------------------- DECLARE @counter int = 0; WHILE (@counter < 25) BEGIN INSERT INTO #xmasTREE(shape) VALUES (geometry::Point(RAND() * 5 + 2.5, RAND() * 8.5, 0).STBuffer(0.3)); SET @counter += 1; END -- Show everything as one geometry so SSMS displays the full picture SELECT geometry::UnionAggregate(shape) AS shape FROM #xmasTREE WHERE shape IS NOT NULL; DROP TABLE #xmasTREE;

How it works (in plain English)

1) The tree is just polygons

The main outline is one polygon that zigzags upward like layered branches. The trunk is a small rectangle. The star and side ornaments are separate polygons too.

This is the part most people miss: you do not “draw” in SQL, you describe shapes as coordinates.

2) “MERRY CHRISTMAS” is hard-coded geometry

Those letter shapes are prebuilt polygons. Brutal, yes. Reliable, also yes.

3) The bottom text is generated

This is the fun bit.

You define a tiny 5x7-ish pixel font inside a table variable, where each character is a set of (row, col) pixels. Then the script loops through the string and stamps squares into the right places.

That means you can write anything as long as you define the letters.

4) Baubles are circles made from points

geometry::Point(...).STBuffer(0.3) creates a little circle. Random coordinates make them look like decorations.

One warning: RAND() inside loops is not truly random the way people assume. You’ll still get variation, but don’t pretend it’s cryptographically festive.

Three quick upgrades you should do before posting

If you skip these, you’re leaving impact on the table.

  1. Make the baubles deterministic
    Right now, reruns change the look. That’s funny once, annoying after. Use a seeded approach with CHECKSUM(NEWID()) to generate repeatable pseudo randomness per row.

  2. Let readers customise the message easily
    Expose @LineA and @LineB at the top with a comment like “Edit these to change the card.” People love changing the text and reposting.

  3. Add a second colour layer
    SSMS won’t colour it, but you can separate layers (tree, star, baubles, text) into separate result sets so people can screenshot and stack them, or at least understand the composition.

Remix ideas

  • Change the strings to your team name or your product launch

  • Add a snowman using circles and polygons

  • Write a tiny font generator and stop hard-coding letters forever

  • Make it animate by outputting multiple frames (one per SELECT) and flipping through them

What this is really teaching

Spatial types aren’t just for GIS. They’re a geometric playground hiding in plain sight.

And yes, this is absolutely a misuse of tempdb. That’s the point.

Comments

Popular posts from this blog

Free Monthly Budget Spreadsheet (UK-Friendly)

Financial Literacy and ADHD – Money, Mistakes, and Learning the Hard Way