Main

August 14, 2006

more silly sql

I was amused by Phil Factor's gems of twisted SQL, like his code for compressing ASCII teddy bears, or The Sudoku Puzzle Generator,

So here's my contribution, a function that makes 'banners' suitable for printing on a dot matrix printer. (all code in this post was prettified with Simple-Talk SQL Prettifier )

First, we need a table that contains some font data, e.g.

SELECT FROM font_data WHERE CHAR_number=ASCII('A'ORDER BY row_number 



char
_number,row_number,row_data
65
,0,   ###  
65,1,  ## ## 
65,2##   ##
65,3##   ##
65,4#######
65,5##   ##
65,6##   ##
65,7,        

(
8 row(saffected)  

One way to get this table is to dump the standard 8x25 text mode font from the BIOS ROM, which is stored at memory location FFA6:000E. We can call the MS-DOS debug.com command to get this as some hex digits, and then unpack them into 8 rows of 8 chars each, like this:

------------- SQL STARTS HERE ---------

SET nocount ON

CREATE TABLE 
#bits_for_hex_digit(hex_digit CHAR(1),bits CHAR(4))
INSERT INTO #bits_for_hex_digit(hex_digit,bitsVALUES ('0','    ')
INSERT INTO #bits_for_hex_digit(hex_digit,bitsVALUES ('1','   #')
INSERT INTO #bits_for_hex_digit(hex_digit,bitsVALUES ('2','  # ')
INSERT INTO #bits_for_hex_digit(hex_digit,bitsVALUES ('3','  ##')
INSERT INTO #bits_for_hex_digit(hex_digit,bitsVALUES ('4',' #  ')
INSERT INTO #bits_for_hex_digit(hex_digit,bitsVALUES ('5',' # #')
INSERT INTO #bits_for_hex_digit(hex_digit,bitsVALUES ('6',' ## ')
INSERT INTO #bits_for_hex_digit(hex_digit,bitsVALUES ('7',' ###')
INSERT INTO #bits_for_hex_digit(hex_digit,bitsVALUES ('8','#   ')
INSERT INTO #bits_for_hex_digit(hex_digit,bitsVALUES ('9','#  #')
INSERT INTO #bits_for_hex_digit(hex_digit,bitsVALUES ('A','# # ')
INSERT INTO #bits_for_hex_digit(hex_digit,bitsVALUES ('B','# ##')
INSERT INTO #bits_for_hex_digit(hex_digit,bitsVALUES ('C','##  ')
INSERT INTO #bits_for_hex_digit(hex_digit,bitsVALUES ('D','## #')
INSERT INTO #bits_for_hex_digit(hex_digit,bitsVALUES ('E','### ')
INSERT INTO #bits_for_hex_digit(hex_digit,bitsVALUES ('F','####')

CREATE TABLE #rom_font_dump(data VARCHAR(100))

CREATE TABLE font_data(CHAR_number INTrow_number INT,row_data CHAR(8))

DECLARE @temp_file VARCHAR(100)
DECLARE @cmd VARCHAR(100)
SET @temp_file='%TMP%\debug.txt'

SET @cmd='echo d FFA6:000E l 400 >'+@temp_file
EXEC xp_cmdshell @cmd no_output
SET @cmd='echo q >>'+@temp_file
EXEC xp_cmdshell @cmd no_output
SET @cmd='debug <'+@temp_file
INSERT INTO #rom_font_dump(dataEXEC xp_cmdshell @cmd 

DECLARE @byte_counter INT
SET 
@byte_counter=0

DECLARE row_cursor CURSOR fast_forward FOR
SELECT 
data FROM #rom_font_dump
       
WHERE data LIKE 'ffa6:%'
       
ORDER BY data

DECLARE @data VARCHAR(100)
OPEN row_cursor
FETCH next FROM row_cursor INTO @data
WHILE @@fetch_status >-BEGIN
       DECLARE 
@i INT
       SET 
@i=12
       
WHILE @i<63 BEGIN
               DECLARE 
@row CHAR(8)
               
SET @row=NULL
                
SELECT @row=d1.bits+d2.bits FROM #bits_for_hex_digit d1
                       
INNER JOIN #bits_for_hex_digit d2
                       
ON d1.hex_digit=SUBSTRING(@data,@i,1)
                       AND 
d2.hex_digit=SUBSTRING(@data,@i+1,1)
               
IF @row IS NOT NULL BEGIN            
                       INSERT INTO 
font_data(CHAR_numberrow_number,row_data)
                               
SELECT @byte_counter/8@byte_counter%8@row
                       
SET @byte_counter=@byte_counter+1
               
END
               SET 
@i=@i+3

       
END

       FETCH 
next FROM row_cursor INTO @data
END
CLOSE 
row_cursor
DEALLOCATE row_cursor

DROP TABLE #bits_for_hex_digit
DROP TABLE #rom_font_dump  

------------- SQL ENDS HERE ---------

This is the function that uses that table to turn a string into a banner:

------------- SQL STARTS HERE ---------

CREATE function ufn_Banner(@s VARCHAR(100)) 
returns VARCHAR(8000AS
BEGIN

       DECLARE 
@row_0 VARCHAR(1000)
       
DECLARE @row_1 VARCHAR(1000)
       
DECLARE @row_2 VARCHAR(1000)
       
DECLARE @row_3 VARCHAR(1000)
       
DECLARE @row_4 VARCHAR(1000)
       
DECLARE @row_5 VARCHAR(1000)
       
DECLARE @row_6 VARCHAR(1000)
       
DECLARE @row_7 VARCHAR(1000)

       
SET @row_0 =''
       
SET @row_1 =''
       
SET @row_2 =''
       
SET @row_3 =''
       
SET @row_4 =''
       
SET @row_5 =''
       
SET @row_6 =''
       
SET @row_7 =''

       
DECLARE @i INT
       SET 
@i=0
       
WHILE @i<=LEN(@s
       
BEGIN
               DECLARE 
@c CHAR(1)
               
SET @c=SUBSTRING(@s,@i,1)
                       
SELECT @row_0=@row_0+row_data FROM font_data WHERE CHAR_number=ASCII(@c) AND row_number=0
                       
SELECT @row_1=@row_1+row_data FROM font_data WHERE CHAR_number=ASCII(@c) AND row_number=1
                       
SELECT @row_2=@row_2+row_data FROM font_data WHERE CHAR_number=ASCII(@c) AND row_number=2
                       
SELECT @row_3=@row_3+row_data FROM font_data WHERE CHAR_number=ASCII(@c) AND row_number=3
                       
SELECT @row_4=@row_4+row_data FROM font_data WHERE CHAR_number=ASCII(@c) AND row_number=4
                       
SELECT @row_5=@row_5+row_data FROM font_data WHERE CHAR_number=ASCII(@c) AND row_number=5
                       
SELECT @row_6=@row_6+row_data FROM font_data WHERE CHAR_number=ASCII(@c) AND row_number=6
                       
SELECT @row_7=@row_7+row_data FROM font_data WHERE CHAR_number=ASCII(@c) AND row_number=7
               
SET @i=@i+1
               
       
END
       RETURN
               
@row_0+CHAR(10)
               +
@row_1+CHAR(10)
               +
@row_2+CHAR(10)
               +
@row_3+CHAR(10)
               +
@row_4+CHAR(10)
               +
@row_5+CHAR(10)
               +
@row_6+CHAR(10)
               +
@row_7+CHAR(10)
       
END  

------------- SQL ENDS HERE ---------

And finally, here's some example screenshots.

First, showing the output in SQL Server Management Studio

You could also pop up a banner in internet explorer, using  usp_Show_HTML, like this:

 

August 13, 2006

discovering phil factor

Nick came over on thursday so we could play around with gnokii, which turned into a small debacle (a debaclet?) when we turned out to be one cable short of a comms link, and then the oven died with a half roasted leg of lamb still in it.

On the upside, I did find out about Phil Factor, that plus the wine and the 2 six packs of peroni we got through meant the evening was net positive I think.

Here's some excerpts....

From Attack of the barber-surgeons of IT: A tale of cursed cursors & embedded SQL 

The development foot soldiers in the IT army are required to have two arrows in their quiver: fluency with the procedural language currently in fashion and a measure of skill in database programming.

Few people are proficient in both. It reminds me of the medieval barbers who cheerfully cut off limbs and performed other surgery as a sideline. If they could cut hair, they reasoned, why not cut the body as well?

From The Writing on the wall: lies, damned lies and documented working procedures

The management consultants accepted everything at face value. They did not seem to even consider that old Joe in dispatch, whose job was a sinecure that an energetic man could complete in half an hour, might have lied to them when he described the elaborate and skilled nature of his contribution to the company. It was all noted, analysed, diagrammed and documented. The final report was delivered in forty volumes and placed in a large filing cabinet, along with a final bill to the company of several million pounds.

Also check out The Index: An Elegy for an excerpt from Phil's upcoming book on SQL Server, rendered in Tennyson style epic verse.

July 14, 2006

rendering html from sql server

I can't think of a single legitimate reason for ever doing this, but here's my latest post on rendering html from within a repl, this time from the SQL Server management studio (formerly known as Query Analyzer) . Previous posts show how to do this in irb (ruby) and in PowerShell.

EDIT PROC usp_Show_HTML (@html varchar(8000))
AS
    DECLARE @ie int
    EXEC sp_OACreate 'InternetExplorer.Application',@ie OUT
    EXEC sp_OASetProperty @ie,'menubar',0
    EXEC sp_OASetProperty @ie,'toolbar',0
    EXEC sp_OASetProperty @ie,'statusbar',0
    EXEC sp_OAMethod @ie,'navigate',null,'about:blank'
    DECLARE @doc int
    EXEC sp_OAGetProperty @ie,'document',@doc OUT
    EXEC sp_OAMethod @doc,'write',null,@html
    EXEC sp_OASetProperty @ie, 'Visible', 'true'

This only works if you are connecting to a SQL Server running on localhhost, and if the SQL Server service is set to run under the 'Local System Account' with the 'Allow service to interact with desktop' checkbox ticked.

To use:
SET NOCOUNT ON
CREATE TABLE #t(output varchar(256))
INSERT INTO #t
EXEC xp_cmdshell 'SET'

DECLARE c CURSOR FAST_FORWARD FOR SELECT output FROM #t WHERE output IS NOT NULL
DECLARE @output varchar(256)
DECLARE @html varchar(8000)

SET @html='<table>'
OPEN c
FETCH NEXT FROM c INTO @output

WHILE @@FETCH_STATUS>-1 BEGIN
    SET @html=@html+'<TR><TD>'+REPLACE(@output,'=','</TD><TD>')+'</TD></TR>'
     FETCH NEXT FROM c INTO @output
END
CLOSE c
DEALLOCATE c
SET @html=@html+'</TABLE>'
EXEC usp_Show_HTML @html
DROP TABLE #t