Click here to monitor SSC

Phil Factor's Phrenetic Phoughts

Simple-Talk columnist
The wilder shores of Transact SQL    Phil on Twitter   Phil on SQL Server Central  Phil on BOS

Drawing Block-graphs from Stored Procedures.

Published Sunday, August 23, 2009 8:21 PM

‘Ridiculous,’ he commented, ‘you’ll be saying it is possible to draw graphs from stored procedures next!’ I’d been talking to the guy about keeping an open mind about what was possible at the database level. He didn’t like my 'can-do' attitude to database development. I didn’t bother to tell him it was a ‘dun-it’ attitude: I’d been producing graphs from stored procedures for years.

I still have a guilty twinge when I confess to doing so, as if it were some unspeakable practice that polite society was not quite ready for: but there are reasons for doing this.

In the dark days of old, when a ‘Development Environment’ meant a dark room full of empty coffee cups and congealing pizza on paper plates, the best way of developing any complex application, was to first create a console on which one could display the state of the machine, along with debug information and diagnostic data. Even now, with sophisticated ready-made tools to help, I still do this when designing commercial database applications, but now I use web technology so I can access the system remotely, and I am not reliant on other people to flesh things out. I don’t do it instead of Reporting Services, of course, it is as well as, at a different time in the development process, and for a different audience: mainly myself.

This sort of monitor is guarded by an interface that allows only standard reports, served from a handful of stored procedures. The ASP.NET scripting is almost nothing but a means to deliver the graph so it can be rendered by the browser.

If I can save a few minutes from the task of polling production systems, then the effort of providing all the status information you need on a few screens is worthwhile. For a rapid assimilation of data, and for spotting potential trouble, nothing beats the graphical representation of data.

One would normally choose to use Reporting Services, of if using PHP, one of the excellent graphing packages such as JPGraph,( http://www.aditus.nu/jpgraph/index.php) but occasionally a bought-in product is either too complex for the job, presents security issues, or doesn’t quite do the task you want.

HTML may seem an unpromising material with which to draw a graph, especially if driven from a stored procedure. This is not entirely the case. It can be used for block graphs, but as I’m generally using block graphs for monitoring purposes, this isn’t really a big issue

Let us imagine we want to draw a block graph of visits to a site over a month. HTML allows you to position entities at absolute positions in the browser window With CSS, the only HTML entities one really needs are DIVs and SPANs. One can assign CSS classes to them so that they will behave like all the other tags (other than table-tags) that you’ve become used to. You can also draw blocks with a wide range of fills and borders. For this exercise we’ll just use DIVs and some old-fashioned HRs so as to keep things simple

The principle is that we have code (normally a stored procedure) that generates the entire script for the HTML page. There are several alternative ways of getting this to the browser. You can save the results to a file via a number of methods and send it to the site via FTP (nice and secure, but no good for ephemeral data), or use a server script such as PHP or ASP.NET.

Here is the graph that we'll produce.

....and here, in its simplest form, is the SQL that produces it. Normally, it would be a stored procedure, and I generally hand it to the calling application as a VARCHAR output variable, but I thought I ought to keep things bare-bones here.


SET NOCOUNT ON
DECLARE
@SampleData TABLE
    
(
      
TheID INT IDENTITY(1, 1),
      
theDate DATETIME,
      
Visits INT
    
)
--we use dummy data of visits per day to a site

INSERT INTO @sampleData SELECT '01 Mar 2009',  15470
INSERT INTO @sampleData SELECT '02 Mar 2009',  15750
INSERT INTO @sampleData SELECT '03 Mar 2009',  14630
INSERT INTO @sampleData SELECT '04 Mar 2009',  8110
INSERT INTO @sampleData SELECT '05 Mar 2009',  9950
INSERT INTO @sampleData SELECT '06 Mar 2009',  13970
INSERT INTO @sampleData SELECT '07 Mar 2009',  14020
INSERT INTO @sampleData SELECT '08 Mar 2009',  20930
INSERT INTO @sampleData SELECT '09 Mar 2009',  14810
INSERT INTO @sampleData SELECT '10 Mar 2009',  13850
INSERT INTO @sampleData SELECT '11 Mar 2009',  8090
INSERT INTO @sampleData SELECT '12 Mar 2009',  7890
INSERT INTO @sampleData SELECT '13 Mar 2009',  10440
INSERT INTO @sampleData SELECT '14 Mar 2009',  11400
INSERT INTO @sampleData SELECT '15 Mar 2009',  12600
INSERT INTO @sampleData SELECT '16 Mar 2009',  12220
INSERT INTO @sampleData SELECT '17 Mar 2009',  15810
INSERT INTO @sampleData SELECT '18 Mar 2009',  9720
INSERT INTO @sampleData SELECT '19 Mar 2009',  8350
INSERT INTO @sampleData SELECT '20 Mar 2009',  11480
INSERT INTO @sampleData SELECT '21 Mar 2009',  11030
INSERT INTO @sampleData SELECT '22 Mar 2009',  13470
INSERT INTO @sampleData SELECT '23 Mar 2009',  19010
INSERT INTO @sampleData SELECT '24 Mar 2009',  17050
INSERT INTO @sampleData SELECT '25 Mar 2009',  8950
INSERT INTO @sampleData SELECT '26 Mar 2009',  9520
INSERT INTO @sampleData SELECT '27 Mar 2009',  12460
INSERT INTO @sampleData SELECT '28 Mar 2009',  14020
INSERT INTO @sampleData SELECT '29 Mar 2009',  12370
INSERT INTO @sampleData SELECT '30 Mar 2009',  13470
INSERT INTO @sampleData SELECT '31 Mar 2009',  16310

--declare all our constants and variables
DECLARE @YHeight INT,
    
@XLength INT,
    
@LeftMargin INT,
    
@BottomMargin INT,
    
@gap INT,
    
@BlockWidth INT,
    
@Scale INT,
    
@MaxScale INT,
    
@ii INT,
    
@iiMax INT,
    
@BlockInterval INT,
    
@verticalLabelOffset INT,
    
@Multiplier INT,
    
@MaxData INT,
    
@Points NUMERIC(9,2)
  
--Firstly we set the basic parameters, which we treat as constants
SELECT  @YHeight = 500,        --the height of the graph in Pixels
        
@XLength = 800,          --the width of the graph in Pixels
        
@verticalLabelOffset = -10,--the offset for the Y labels
        
@LeftMargin = 40,        --the left marging in Pixels
        
@BottomMargin = 30,  --the bottom margin
        
@Gap = 8,                --the gap between blocks
        
@Points = 20         -- the number of label points (roughly!)

--and a little helper table to decide on Y axis labels
DECLARE @ReasonableScale TABLE ( multiplier CHAR )
INSERT  INTO @ReasonableScale ( Multiplier )--the units that we label
        
SELECT  '1'
        
UNION ALL
        
SELECT  '2'
        
UNION ALL
        
SELECT  '5'

--now we calculate the scale from the data
SELECT  @MaxData = MAX(visits)
FROM    @sampleData
SELECT  @multiplier = FLOOR(CEILING(LEFT(@MaxData, 2) / @points) * @MaxData
                            
/ LEFT(@MaxData, 2))
/* this makes me squirm! The next bit makes me want to put my hands over my eyes. Do not do this at home! */                            
SELECT TOP 1
        
@multiplier = ( r.multiplier + REPLICATE('0', LEN(@multiplier) - 1) )
FROM    @ReasonableScale r
ORDER BY ABS(@multiplier - ( r.multiplier + REPLICATE('0',
                              
LEN(@multiplier) - 1) )) ASC
/* somehow, we have worked out the scale and the label points */
--so now we can calculate block widths and the vertical scale for the blocks
SELECT    
  
@BlockWidth=((@XLength-@Leftmargin)/COUNT(*))-@gap,
  
@BlockInterval=@BlockWidth+@Gap,
  
@Scale=CEILING(@Multiplier*@Points/@yHeight*1.00) FROM @sampleData




--and draw the YAxis labels and lines

DECLARE @yaxes TABLE (html VARCHAR(2555))
--for putting in the HTML

SELECT  @ii=0,
        
@iiMax=20*@Multiplier
WHILE @ii<=@iiMax
    
BEGIN
        IF
@ii>@MaxData
            
BREAK ;
        
INSERT  INTO @YAxes (html)
                
SELECT  '<div class="YAxisLabel" style="bottom:'
                        
+CONVERT(VARCHAR(4), (@ii/@scale)+@VerticalLabelOffset
                        
+@bottomMargin)+'px">'+CONVERT(VARCHAR(8), @ii)
                        +
'</div>'
        
IF (@ii%(2*@Multiplier)=0
            
AND @ii>0)
            
INSERT  INTO @YAxes (html)
                    
SELECT  '<hr class="AxisLine" style="bottom:'
                            
+CONVERT(VARCHAR(4), (@ii/@scale)+@bottomMargin)
                            +
'px">'
        
SELECT  @ii=@ii+@Multiplier
    
END
  
--now we can define each CSS class and calculate the positioning and dimensions
SELECT  [html] = '
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<title>Sample Activity chart</title>
<style type="text/css">
<!--
div #graph{
font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
}

#graph .axes{
position: absolute;
bottom: '
+CONVERT(VARCHAR(5), @BottomMargin)+'px;
height: '
+CONVERT(VARCHAR(5), (@ii/@scale)+@bottomMargin)+'px;
width: '
+CONVERT(VARCHAR(5), @XLength-@LeftMargin)+'px;
border-top: 1px solid Silver;
border-right: 1px solid Silver;
border-left: 2px solid;
border-bottom: 2px solid;
left:'
+CONVERT(VARCHAR(5), @LeftMargin)+'px;

}
#graph .title
{
position: absolute;
background-color: White;
bottom: '
+CONVERT(VARCHAR(5), @YHeight-30)+' px;
height: 30px;
width:500px;
left:'
+CONVERT(VARCHAR(5), (@XLength-500)/2)
        +
'px;
text-align: center;
font: italic normal normal 20px Verdana Geneva Arial Helvetica sans-serif;

}
#graph .column{
position: absolute;
background-color: #efefef;
bottom: '
+CONVERT(VARCHAR(5), @BottomMargin+1)+'px;
height: 100px;
width: '
+CONVERT(VARCHAR(15), @BlockWidth)
        +
'px;
border: 1px solid;
font-size: 1px;

}
#graph .XAxisLabel{
position: absolute;
bottom: 10px;
font: italic normal normal 14px Verdana Geneva Arial Helvetica sans-serif;
    }
#graph .YAxisLabel{
position: absolute;
bottom: 0px;
left:0px;
text-align: right;
font: italic normal normal 11px Verdana Geneva Arial Helvetica sans-serif;
width:'
+CONVERT(VARCHAR(5), @LeftMargin-3)+'px;
}
#graph .AxisLine{
position: absolute;
bottom: 100px;
left: '
+CONVERT(VARCHAR(5), @LeftMargin+1)+'px;
width:'
+CONVERT(VARCHAR(5), @XLength-@leftmargin)+'px;
font-size: 15px;
border: none;
color: silver;
height: 1px;
}
-->
</style>
</head>

<body>
<div id="graph">
<!-- here is the basic axes and borders of the graph-->
<div class=axes>&nbsp</div>
'
UNION ALL
SELECT  html
FROM    @YAxes--the vertical axis labels and lines
UNION ALL
  
--do the title
SELECT  [html] = '<div class=title>Activity chart from '
        
+CONVERT(CHAR(11), MIN(theDate), 113)+' to '
        
+CONVERT(CHAR(11), MAX(TheDate), 113)+'</div>'
FROM    @SampleData
UNION ALL
  
--the indvidual columns or blocks
SELECT  [html] = '<div class="column" style="left:'+CONVERT(VARCHAR(8), 1
        
+@LeftMargin+((TheID-1)*@BlockInterval))++'px; height: '
        
+CONVERT(VARCHAR(4), visits/@scale)+'px;'+'">&nbsp;</div>'
FROM    @SampleData
UNION ALL
SELECT  [html] = '<div class="XAxisLabel" style="left:'
        
+CONVERT(VARCHAR(8), @LeftMargin+((TheID-1)*@BlockInterval))+'px;">'
        
+CONVERT(CHAR(6), TheDate, 113)+'</div>'
FROM    @SampleData
WHERE   TheID%4=0
UNION ALL
SELECT  [html] = '
</body>
</HTML>'

This produced a simple little HTML page like this. (remember to execute it with the output set to text, and the number of characters displayed in each column set to something reasonable.) When this is rendered within a browser, it will give you the graph in the illustration above

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">

<html>
<head>
<title>Sample Activity chart</title>
<style type="text/css">
<!--
div #graph{
font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;
}

#graph .axes{
position: absolute;
bottom: 30px;
height: 555px;
width: 760px;
border-top: 1px solid Silver;
border-right: 1px solid Silver;
border-left: 2px solid;
border-bottom: 2px solid;
left:40px;

}
#graph .title
{
position: absolute;
background-color: White;
bottom: 470 px;
height: 30px;
width:500px;
left:150px;
text-align: center;
font: italic normal normal 20px Verdana Geneva Arial Helvetica sans-serif;

}
#graph .column{
position: absolute;
background-color: #efefef;
bottom: 31px;
height: 100px;
width: 16px;
border: 1px solid;
font-size: 1px;

}
#graph .XAxisLabel{
position: absolute;
bottom: 10px;
font: italic normal normal 14px Verdana Geneva Arial Helvetica sans-serif;
}
#graph .YAxisLabel{
position: absolute;
bottom: 0px;
left:0px;
text-align: right;
font: italic normal normal 11px Verdana Geneva Arial Helvetica sans-serif;
width:37px;
}
#graph .AxisLine{
position: absolute;
bottom: 100px;
left: 41px;
width:760px;
font-size: 15px;
border: none;
color: silver;
height: 1px;
}
-->
</style>
</head>

<body>
<div id="graph">
<!-- here is the basic axes and borders of the graph-->
<div class=axes>&nbsp</div>
<div class="YAxisLabel" style="bottom:20px">0</div>
<div class="YAxisLabel" style="bottom:45px">1000</div>
<div class="YAxisLabel" style="bottom:70px">2000</div>
<hr class="AxisLine" style="bottom:80px">
<div class="YAxisLabel" style="bottom:95px">3000</div>
<div class="YAxisLabel" style="bottom:120px">4000</div>
<hr class="AxisLine" style="bottom:130px">
<div class="YAxisLabel" style="bottom:145px">5000</div>
<div class="YAxisLabel" style="bottom:170px">6000</div>
<hr class="AxisLine" style="bottom:180px">
<div class="YAxisLabel" style="bottom:195px">7000</div>
<div class="YAxisLabel" style="bottom:220px">8000</div>
<hr class="AxisLine" style="bottom:230px">
<div class="YAxisLabel" style="bottom:245px">9000</div>
<div class="YAxisLabel" style="bottom:270px">10000</div>
<hr class="AxisLine" style="bottom:280px">
<div class="YAxisLabel" style="bottom:295px">11000</div>
<div class="YAxisLabel" style="bottom:320px">12000</div>
<hr class="AxisLine" style="bottom:330px">
<div class="YAxisLabel" style="bottom:345px">13000</div>
<div class="YAxisLabel" style="bottom:370px">14000</div>
<hr class="AxisLine" style="bottom:380px">
<div class="YAxisLabel" style="bottom:395px">15000</div>
<div class="YAxisLabel" style="bottom:420px">16000</div>
<hr class="AxisLine" style="bottom:430px">
<div class="YAxisLabel" style="bottom:445px">17000</div>
<div class="YAxisLabel" style="bottom:470px">18000</div>
<hr class="AxisLine" style="bottom:480px">
<div class="YAxisLabel" style="bottom:495px">19000</div>
<div class="YAxisLabel" style="bottom:520px">20000</div>
<hr class="AxisLine" style="bottom:530px">
<div class=title>Activity chart from 01 Mar 2009 to 31 Mar 2009</div>
<div class="column" style="left:41px; height: 386px;">&nbsp;</div>
<div class="column" style="left:65px; height: 393px;">&nbsp;</div>
<div class="column" style="left:89px; height: 365px;">&nbsp;</div>
<div class="column" style="left:113px; height: 202px;">&nbsp;</div>
<div class="column" style="left:137px; height: 248px;">&nbsp;</div>
<div class="column" style="left:161px; height: 349px;">&nbsp;</div>
<div class="column" style="left:185px; height: 350px;">&nbsp;</div>
<div class="column" style="left:209px; height: 523px;">&nbsp;</div>
<div class="column" style="left:233px; height: 370px;">&nbsp;</div>
<div class="column" style="left:257px; height: 346px;">&nbsp;</div>
<div class="column" style="left:281px; height: 202px;">&nbsp;</div>
<div class="column" style="left:305px; height: 197px;">&nbsp;</div>
<div class="column" style="left:329px; height: 261px;">&nbsp;</div>
<div class="column" style="left:353px; height: 285px;">&nbsp;</div>
<div class="column" style="left:377px; height: 315px;">&nbsp;</div>
<div class="column" style="left:401px; height: 305px;">&nbsp;</div>
<div class="column" style="left:425px; height: 395px;">&nbsp;</div>
<div class="column" style="left:449px; height: 243px;">&nbsp;</div>
<div class="column" style="left:473px; height: 208px;">&nbsp;</div>
<div class="column" style="left:497px; height: 287px;">&nbsp;</div>
<div class="column" style="left:521px; height: 275px;">&nbsp;</div>
<div class="column" style="left:545px; height: 336px;">&nbsp;</div>
<div class="column" style="left:569px; height: 475px;">&nbsp;</div>
<div class="column" style="left:593px; height: 426px;">&nbsp;</div>
<div class="column" style="left:617px; height: 223px;">&nbsp;</div>
<div class="column" style="left:641px; height: 238px;">&nbsp;</div>
<div class="column" style="left:665px; height: 311px;">&nbsp;</div>
<div class="column" style="left:689px; height: 350px;">&nbsp;</div>
<div class="column" style="left:713px; height: 309px;">&nbsp;</div>
<div class="column" style="left:737px; height: 336px;">&nbsp;</div>
<div class="column" style="left:761px; height: 407px;">&nbsp;</div>
<div class="XAxisLabel" style="left:112px;">04 Mar</div>
<div class="XAxisLabel" style="left:208px;">08 Mar</div>
<div class="XAxisLabel" style="left:304px;">12 Mar</div>
<div class="XAxisLabel" style="left:400px;">16 Mar</div>
<div class="XAxisLabel" style="left:496px;">20 Mar</div>
<div class="XAxisLabel" style="left:592px;">24 Mar</div>
<div class="XAxisLabel" style="left:688px;">28 Mar</div>

</body>
</HTML>

Now, as you can imagine, my code doesn't look like this. I've simplified things down. I'd do the weekends in a different color, I'd probably do a moving average, I'd usually have a stored procedure that rendered several graphs, taking the data as XML. Once you get started, it is amazing what you can do to enhance the graph to make it easier to spot problems, and other things that need investigating. With this sort of reporting system, I've managed to detect attempts at intrusion, and several database issues that I've caught before they became problems. Sure, it has been very useful, but I still feel a twinge of guilt when I explain what I've done. Somehow writing graphs in stored procedures doesn't seem natural.

Comments

 

jonnosan said:

<i>There are several alternative ways of getting this to the browser</i>

you know the only appropriate method for getting this data into the browser is directly from SQL Server - http://blog.jamtronix.com/2006/07/rendering_html_from_sql_server.html
August 24, 2009 5:44 AM
 

Phil Factor said:

Wow Jonno!
I'm in awe of this. I use various ways (normally calling the stored procedure via SQL Native Client from ASP.NET)  or the way I describe here http://www.simple-talk.com/sql/t-sql-programming/the-tsql-of-text-files/ to create the HTML file (and then FTP it to the site).
I can see that your technique could be useful for the debugging phase in development as it is a bit of a pain cutting and pasting into an HTML editor such as Topstyle or Frontpage. Sadly, even my dev database server is different from my  desktop machine, so it wouldn't work for me, but for a laptop.....
August 24, 2009 6:34 AM
 

timothyawiseman@gmail.com said:

Phil and Jonnosan, I am impressed with both of your examples.  I did something vaguely similar outputting excel spreadsheets with somewhat sophisticated formatting by using the OLE interface from a stored procedure once, but that was mostly to show that it could be done.

I think "Somehow writing graphs in stored procedures doesn't seem natural." because it is not.  It is impressive as a display of what SQL can do, but the same thing can be done more easily and with more options with other tools such as MS Access, Python, Report Server, C#.....depending on what you have and exactly what you are trying to do.
September 2, 2009 11:34 AM
 

hemanth.damecharla said:

Phil,
I'm amazed after reading this article. I have been trying to produce reports from sql server and your article has pointed me in the right direction. I have tried some reporting but, not with graphs and all. This was something I made in April, 2009 as a way to quickly produce small html reports from simple queries which the clients wanted to run.

/****** Object:  StoredProcedure [dbo].[User_Sp_Query_GetHTML]    Script Date: 04/27/2009 14:55:14 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[User_Sp_Query_GetHTML] ( @execTSQL varchar(8000))

-- *******************************************************************************
-- FILE: User_Sp_Query_GetHTML.sql
-- DESCRIPTION: Create HTML files for any query passed to the stored procedure.
-- AUTHOR: HEMANTH C DAMECHARLA (HEMANTH.DAMECHARLA@GMAIL.COM)
--
-- @@BOF_REVSION_MARKER
-- REVISION HISTORY:
-- YYYY/MM/DD  BY       DESCRIPTION
-- ==========  =======  ==========================================================
-- 2009/04/22  HEMANTH  V1.0.0.0 CREATED
-- 2009/04/27  HEMANTH  V2.0.0.0 CREATED
-- KNOWN ISSUES:
-- 1. CANNOT GENERATE HTML FOR STORED PROCEDURES
-- 2. THE PAGE MIGHT NOT BE DISPLAYED PROPERLY DEPENDING ON THE BROWSER
-- 3. WORKING ON FINDING OTHER ISSUES
-- @@EOF_REVSION_MARKER
-- *******************************************************************************
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
Declare @serverName as sysname
Declare @dbName as sysname
Declare @dbStatus as sysname
Declare @dbcount as int
Declare @strLEN as int
Declare @columns as varchar(1024)
Declare @HTMLOut as varchar(1024)
Declare @printHTML as varchar(1024)
Declare @sqlSTMT as varchar(8000)
Declare @sqlSTMT1 as varchar(8000)

-- Drop the Temporary Tables used, if they still persist.
IF EXISTS (SELECT * FROM tempdb.dbo.sysobjects WHERE name = '##TempHTML1')
DROP TABLE ##TempHTML1

IF EXISTS (SELECT * FROM tempdb.dbo.sysobjects WHERE name = '##TempHTML2')
DROP TABLE ##TempHTML2

-- Prepare Query
SET @sqlSTMT = 'SELECT * INTO ##TempHTML1 FROM (' + @execTSQL + ') as T1'
EXECUTE ( @sqlSTMT )

Select @serverName = @@servername
PRINT N''+ '<html>'
PRINT N''+ ''
PRINT N''+ ' <head>'
PRINT N''+ ' <meta http-equiv="Content-Type" content="text/html; charSET=windows-1252">'
PRINT N''+ ' <title>'+ @serverName + '</title>'
PRINT N''+ ' </head>'
PRINT N''+ ''
PRINT N''+ ' <body>'
PRINT N''+ ''
PRINT N''+ ' <style type="text/css" media="all">'
PRINT N''+ ' table '
PRINT N''+ ' {'
PRINT N''+ ' margin-bottom: 2em; '
PRINT N''+ ' border-collapse: collapse '
PRINT N''+ ' } '
PRINT N''+ ' td,th '
PRINT N''+ ' {'
PRINT N''+ ' border= 1 solid #999; '
PRINT N''+ ' padding: 0.2em 0.2em; '
PRINT N''+ ' background-color: #FFCCCC'
PRINT N''+ ' text-align: left'
PRINT N''+ ' font-size: 10;'
PRINT N''+ ' } '
PRINT N''+ ' tr'
PRINT N''+ ' {'
PRINT N''+ ' background-color: #EDFEDF'
PRINT N''+ ' }'
PRINT N''+ ' </style> '
PRINT N''+ ''
PRINT N''+ ' <table width="25%">'
SELECT @dbcount = COUNT([name]) FROM master.dbo.sysdatabases WHERE DATABASEPROPERTYEX([name],'Status') = 'ONLINE' AND [name] NOT IN ('master','model','msdb','tempdb')
PRINT N''+ ' <tr align=center>'
SET @printHTML = ' <b><font face="Verdana" size="2"> ServerName: '+@serverName+'</font></b>'
PRINT N''+@printHTML
SET @printHTML = ' </tr>'
PRINT N''+@printHTML
SET @printHTML = ' <tr align=center>'
PRINT N''+@printHTML
SET @printHTML = ' <b><font face="Verdana" size="2"> Number of Online User DBs: '+CAST(@dbcount as varchar)+'</font></b>'
PRINT N''+@printHTML
SET @printHTML = ' </tr>'
PRINT N''+@printHTML
PRINT N''+ ' </table>'
PRINT N''+ ' <table width="100%">'
SET @sqlSTMT = 'SELECT '
--Prepare columns details
SET @printHTML = ' <tr>'
PRINT N''+@printHTML
DECLARE columnCursor CURSOR FOR
SELECT column_name FROM tempdb.information_schema.columns WHERE table_name = '##TempHTML1'
OPEN columnCursor
FETCH columnCursor INTO @columns
WHILE @@FETCH_STATUS = 0
BEGIN
SET @printHTML = ' <td><b><font face="Verdana" size="2">'+@columns+'</font></b></td>'
PRINT N''+@printHTML
SET @sqlSTMT = @sqlSTMT + ''''+'<td> <font face="Verdana" size="2">'+''''+'+ CAST('+ @columns + ' as varchar)+'+''''+'</font></td>'+ ''''+'+'
FETCH columnCursor INTO @columns
END
CLOSE columnCursor
DEALLOCATE columnCursor
SET @printHTML = ' </tr>'
PRINT N''+@printHTML

SET @strLEN = len(@sqlSTMT)-1
--Print N''+CAST(@strLEN as varchar)
SET @sqlSTMT = SUBSTRING( @sqlSTMT, 1, @strLEN)
SET @sqlSTMT = @sqlSTMT + ' AS [HTMLOUT] FROM ##TempHTML1'
--PRINT N''+@sqlSTMT

SET @sqlSTMT1 = 'SELECT * INTO ##TempHTML2 FROM ('+@sqlSTMT+') AS T2'
--PRINT N''+@execTSQL
EXECUTE ( @sqlSTMT1 )

--::BEGIN:: Print HTML output
DECLARE HTMLCursor CURSOR FOR
SELECT * FROM ##TempHTML2
OPEN HTMLCursor
FETCH HTMLCursor INTO @HTMLOut
WHILE @@FETCH_STATUS = 0
BEGIN
SET @printHTML = ' <tr>'
PRINT N''+@printHTML
SET @printHTML = ' ' + @HTMLOut
PRINT N''+@printHTML
SET @printHTML = ' </tr>'
PRINT N''+@printHTML

FETCH HTMLCursor INTO @HTMLOut
END
CLOSE HTMLCursor
DEALLOCATE HTMLCursor
--::END:: Print HTML output
SET @printHTML = ' </table>'
PRINT N''+@printHTML
PRINT N''+ ''
SET @printHTML =' </body>'
PRINT N''+@printHTML
PRINT N''+ ''
SET @printHTML ='</html>'
PRINT N''+@printHTML
PRINT N''+ ''

-- drop temporary tables used.
IF EXISTS (SELECT * FROM tempdb.dbo.sysobjects WHERE name = '##TempHTML1')
DROP TABLE ##TempHTML1

IF EXISTS (SELECT * FROM tempdb.dbo.sysobjects WHERE name = '##TempHTML2')
DROP TABLE ##TempHTML2

END
September 9, 2009 6:32 AM
You need to sign in to comment on this blog
<August 2009>
SuMoTuWeThFrSa
2627282930311
2345678
9101112131415
16171819202122
23242526272829
303112345
Migrating from OCS 2007 R2 to Lync: Part 4
 Having migrated the rest of our users and legacy resources across, and start getting ready to... Read more...

Automated Script-generation with Powershell and SMO
 In the first of a series of articles on automating the process of building, modifying and copying SQL... Read more...

Seth Godin: Big in the IT Business
 Seth Godin has transformed our understanding of marketing in IT. He invented the concept of 'permission... Read more...

Using SQL Test Database Unit Testing with TeamCity Continuous Integration
 With database applications, the process of test and integration can be frustratingly slow because so... Read more...

Converting String Data to XML and XML to String Data
 We all appreciate that, in general, XML documents or fragments are held in strings as text markup. In... Read more...