<?xml version="1.0" ?> 
<?xml-stylesheet type='text/xsl' href='rss.xslt' version='1.0'?>
<!--  RSS generation by 'Tom Muck's Blog' on Sun, 15 Mar 2026 06:10:16 GMT   --> 
<rss version="0.92">
	<channel>
		<title>Tom Muck's Blog: SQL</title> 
		<link>http://www.tom-muck.com/blog/?cat=11</link> 
		<description>Tom Muck's Blog</description> 
		<webMaster>tom.muck@gmail.com</webMaster> 
		<language>en-us</language> 
		<item>
			<title>Title Case Function for SQL</title>
			<description><![CDATA[<p>I saw a few &quot;title case&quot; functions online for SQL Server, but none of them seem to take into account the fact that minor words like &quot;the&quot;, &quot;a&quot;, &quot;and&quot; are not capitalized in a title. Most of them also use slow-running loops. This function is fast, using a built in string_split function for splitting the title into a table of words, then using COALESCE to put it back together, using some string functions to capitalize major words:</p><p>&nbsp;</p><div class="code"><p>CREATE FUNCTION fn_TitleCase (@InputString VARCHAR(4000) )</p><p>RETURNS VARCHAR(4000)</p><p>AS</p><p>BEGIN</p><p><br /></p><p>DECLARE @val VARCHAR(4000)</p><p><br /></p><p>Select @val = COALESCE(@val + ' ' +&nbsp;</p><p><span style=&quot;white-space: pre;&quot;>	</span>CASE WHEN value IN ('and','as', 'but', 'for', 'if', 'nor', 'or', 'so', 'yet','a',&nbsp;</p><p>&nbsp;'an', 'the', 'as', 'at', 'by', 'for', 'in', 'of',&nbsp;</p><p>&nbsp;'off', 'on', 'per', 'to', 'up', 'via') THEN LOWER(value)&nbsp;</p><p><span style=&quot;white-space: pre;&quot;>		</span>ELSE UPPER(LEFT(value,1))&nbsp; &nbsp;+ LOWER(RIGHT(value,len(value)-1) )&nbsp;</p><p><span style=&quot;white-space: pre;&quot;>	</span>END,&nbsp;</p><p><span style=&quot;white-space: pre;&quot;>	</span>UPPER(LEFT(value,1)) + LOWER(RIGHT(value,len(value)-1)) )</p><p>&nbsp; &nbsp; &nbsp; &nbsp; From STRING_SPLIT(@inputString, ' ');</p><p><br /></p><p>RETURN ISNULL(@val,'')</p><p>END&nbsp;</p></div><p>&nbsp;</p>]]></description> 
			<link>http://www.tom-muck.com/blog/index.cfm?newsid=217</link>
			<guid isPermaLink="true">http://www.tom-muck.com/blog/index.cfm?newsid=217</guid>
			<pubDate>Sun, 08 May 2022 05:25:53 GMT</pubDate>
		</item>
		<item>
			<title>Upgrades and Backups Pt. 2: Shrinking SQL Server Express Log Files</title>
			<description><![CDATA[<p><a title="SQL Server Express automated backup for ColdFusion" href="../index.cfm?newsid=193" target="_self">Part 1</a> described how to create a bare bones SQL Server automated backup plan for a SQL Express database using a ColdFusion scheduled task. I also wanted to truncate and shrink old log files on a schedule, so came up with a stored procedure to do it, cobbled together from various snippets. The new stored procedure takes in a database name as parameter, then looks up the log file name. If it's found (it should be if the db name is correct) it truncates and shrinks the file. Here's the script:</p><p class="code">USE [master]<br />GO<br /><br />SET ANSI_NULLS ON<br />GO<br />SET QUOTED_IDENTIFIER ON<br />GO<br /><br />CREATE PROCEDURE [dbo].[sp_TruncateLogs]&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @databaseName nvarchar(1024)<br />AS <br />BEGIN <br />&nbsp;SET NOCOUNT ON; <br /><br />&nbsp;DECLARE @sql NVARCHAR(1000) <br />&nbsp;DECLARE @logfilename NVARCHAR(1000)<br /><br />&nbsp;SELECT @logfilename = b.name <br />&nbsp;FROM sys.sysdatabases a&nbsp; <br />&nbsp;&nbsp;INNER JOIN sys.sysaltfiles b <br />&nbsp;&nbsp;&nbsp;ON a.dbid = b.dbid <br />&nbsp;WHERE fileid = 2 <br />&nbsp;&nbsp;AND a.name = @databaseName<br /><br />&nbsp;IF @@rowcount = 1 BEGIN<br />&nbsp;<br />&nbsp;&nbsp;SET @sql = '<br />&nbsp;&nbsp;USE ' + @databaseName + '<br />&nbsp;&nbsp;BACKUP LOG ' + @databaseName + '<br />&nbsp;&nbsp;with truncate_only<br />&nbsp;&nbsp;dbcc shrinkfile (' + @logfilename + ', 1)'<br /><br />&nbsp;&nbsp;EXECUTE sp_executesql @sql <br />&nbsp;END<br /><br />END </p><p>Next, just add it to the loop in the original ColdFusion scheduled task script:</p><p class="code">&lt;cfstoredproc datasource=&quot;master&quot; procedure=&quot;dbo.sp_TruncateLogs&quot;&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;cfprocparam cfsqltype=&quot;cf_sql_varchar&quot; value=&quot;#ListGetAt(variables.databases, variables.i)#&quot; /&gt;<br />&lt;/cfstoredproc&gt;<br /></p>]]></description> 
			<link>http://www.tom-muck.com/blog/index.cfm?newsid=195</link>
			<guid isPermaLink="true">http://www.tom-muck.com/blog/index.cfm?newsid=195</guid>
			<pubDate>Sat, 26 Feb 2011 13:21:55 GMT</pubDate>
		</item>
		<item>
			<title>Upgrades and Backups: Automated Backup for SQL Express</title>
			<description><![CDATA[<p>I've been lax in my blogging and my ColdFusion duties lately. I just today installed CF 9 for the first time, and I'm usually way ahead of the curve on that. Work has been super busy, and we are stuck on CF 8 at the office for the foreseeable future. So far, the upgrade has been quick and painless. Now I need to start diving into the new features. I think the main feature for me is the general performance enhancements, but I'm sure I'll get into the Office integration as well. One nice thing about upgrading is being able to try out the features as Enterpise version for a month before putting in the license key for Pro version and losing the functionality. 
</p>
<p>I was also thinking about a SQL Server upgrade. I'm running the Express version, which is perfect for most small-medium web sites. One thing it doesn't have is a way to do maintenance. I've been doing manual backups. Well, after looking at the price of a SQL Server license, I decided to look for a good automated backup. I found a backup script online at <a href="http://www.mssqltips.com/tip.asp?tip=1174" target="_blank">http://www.mssqltips.com/tip.asp?tip=1174</a> and modified it to meet my needs (made directory name a parameter)</p>
<p class="code"> CREATE PROCEDURE [dbo].[sp_BackupDatabase] <br />
	@databaseName nvarchar(1024), <br />
	@type CHAR(1) = 'F', <br />
	@directory nvarchar(1024) = 'C:\SQLBackup'<br />
	AS <br />
	BEGIN <br />
	SET NOCOUNT ON; <br />
	<br />
DECLARE @sql NVARCHAR(1000) <br />
DECLARE @currentDate NVARCHAR(20) <br />
<br />
SELECT @currentDate = REPLACE(CONVERT(VARCHAR, GETDATE(),111),'/',') + <br />
REPLACE(CONVERT(VARCHAR, GETDATE(),108),':',') <br />
<br />
IF @type = 'F' <br />
	SET @sql = 'BACKUP DATABASE ' + <br />
	@databaseName + <br />
	' TO DISK = ' + <br />
	@directory + '\' + <br />
	@databaseName + '\' + <br />
	@databaseName + '_Full_' + <br />
@currentDate + '.BAK' <br />
<br />
IF @type = 'D' <br />
	SET @sql = 'BACKUP DATABASE ' + <br />
	@databaseName + <br />
	' TO DISK = ' + <br />
	@directory + '\' +<br />
	@databaseName + '\' + <br />
	@databaseName + '_Diff_' + <br />
@currentDate + '.BAK' WITH DIFFERENTIAL' <br />
<br />
IF @type = 'L' <br />
	SET @sql = 'BACKUP LOG ' + <br />
	@databaseName + <br />
	' TO DISK = ' + <br />
	@directory + '\' +<br />
	@databaseName + '\' + <br />
	@databaseName + '_Log_' + <br />
@currentDate + '.TRN' <br />
<br />
EXECUTE sp_executesql @sql <br />
END </p>
<p>Next, I created my own stored procedure for deleting old database backups. Basically, in a "real" SQL Server, the backup task will delete backups back to a date you specify. We'll pass in the database name, type of backup, timeframe (in weeks), and the directory name. </p>
<p class="code">CREATE PROCEDURE [dbo].[sp_RemoveOldBackups] <br />
@databaseName sysname, <br />
@backupType CHAR(1) = 'F', <br />
@howlong integer = 4, <br />
@directory nvarchar(1024) = 'C:\SQLBackup'<br />
AS <br />
BEGIN <br />
SET NOCOUNT ON; <br />
DECLARE @type nchar(3)<br />
SET @type = 'BAK'<br />
if @backupType = 'L' BEGIN SET @type = 'TRN' END<br />
DECLARE @backupPath nvarchar(2048)<br />
SET @backupPath = @directory + '\' + @databaseName<br />
declare @DeleteDate nvarchar(50)<br />
declare @DeleteDateTime datetime<br />
set @DeleteDateTime = DateAdd(ww, -1 * @howlong, GetDate())<br />
set @DeleteDate = (Select Replace(Convert(nvarchar, @DeleteDateTime, 111), '/', '-') + 'T' + Convert(nvarchar, @DeleteDateTime, 108))<br />
<br />
EXECUTE master.dbo.xp_delete_file 0, @backupPath, @type, @DeleteDate,1<br />
<br />
END </p>
<p>Now, rather than use Windows scheduled tasks for this, I wanted a simple ColdFusion scheduled task. The stored procedures should be stored in the master database, so you'll need a datasource that will allow execute permissions on these two stored procedures. Finally, save the following into a ColdFusion file and store it in one of your web sites. Set the scheduled task to execute once a week.</p>
<p class="code">&lt;cftry&gt;<br />
	&lt;!--- List of databases to backup ---&gt;<br />	
	&lt;cfset variables.databases = &quot;mydatabase,Northwind,myOtherDatabase&quot; /&gt;<br />
	&lt;!--- Path to the backup folder ---&gt;
	<br />
	&lt;cfset variables.backupPath = &quot;Z:\SQLBackup&quot; /&gt;<br />
	&lt;!--- How many weeks to keep backups ---&gt;
	<br />
&lt;cfset variables.howLong = &quot;4&quot; /&gt;<br />
<br />
&lt;!--- Loop through the databases ---&gt;<br />&lt;cfloop from=&quot;1&quot; to=&quot;#ListLen(variables.databases)#&quot; index=&quot;variables.i&quot;&gt;<br />
	&nbsp;&nbsp;&lt;cftry&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;!---	First, backup the database ---&gt;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&lt;cfstoredproc datasource=&quot;master&quot; procedure=&quot;sp_BackupDatabase&quot;&gt;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;cfprocparam cfsqltype=&quot;cf_sql_varchar&quot; value=&quot;#ListGetAt(variables.databases, variables.i)#&quot; /&gt;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;cfprocparam cfsqltype=&quot;cf_sql_char&quot; value=&quot;F&quot; /&gt;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;cfprocparam cfsqltype=&quot;cf_sql_varchar&quot; value=&quot;#variables.backupPath#&quot; /&gt;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&lt;/cfstoredproc&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--- Next,	delete the old databases ---&gt;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&lt;cfstoredproc datasource=&quot;master&quot; procedure=&quot;sp_RemoveOldBackups&quot;&gt;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;cfprocparam cfsqltype=&quot;cf_sql_varchar&quot; value=&quot;#ListGetAt(variables.databases, variables.i)#&quot; /&gt;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;cfprocparam cfsqltype=&quot;cf_sql_char&quot; value=&quot;F&quot; /&gt;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;cfprocparam cfsqltype=&quot;cf_sql_integer&quot; value=&quot;4&quot; /&gt;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;cfprocparam cfsqltype=&quot;cf_sql_varchar&quot; value=&quot;#variables.backupPath#&quot; /&gt;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&lt;/cfstoredproc&gt;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&lt;cfcatch&gt;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;cflog application=&quot;yes&quot; text=&quot;Database backup failed #now()# for #ListGetAt(variables.databases, i)#&quot; /&gt;<br />
	&nbsp;&nbsp;&nbsp;&nbsp;&lt;/cfcatch&gt;<br />
	&nbsp;&nbsp;&lt;/cftry&gt; <br />
&lt;/cfloop&gt;<br />&lt;cfcatch&gt;<br />
&nbsp;&nbsp;&nbsp;&lt;cflog application=&quot;yes&quot; text=&quot;Database backup failed #now()# outside loop&quot; /&gt;<br />&lt;/cfcatch&gt;<br />
&lt;/cftry&gt;
</p>
<p>Obviously, this is the bare bones of a backup system. You can modify the scripts to perform differential backups to meet your needs, log file backups, and add some error handling to the stored procedures, but this is a good start.</p>
]]></description> 
			<link>http://www.tom-muck.com/blog/index.cfm?newsid=193</link>
			<guid isPermaLink="true">http://www.tom-muck.com/blog/index.cfm?newsid=193</guid>
			<pubDate>Thu, 10 Feb 2011 00:16:41 GMT</pubDate>
		</item>
		<item>
			<title>SQL hacking on the web</title>
			<description><![CDATA[<p>There has been a new rash of SQL injection attacks originating from the far east and other places using the following types of attacks:</p>
<blockquote>
  <p>somevariable=1%20and%201=convert(int,(select%20top%201%20username%20from%20adminusers))</p>
</blockquote>
<p> or</p>
<blockquote>
  <p> somevariable=1%20and%201=convert(int,(select%20top%201%20char(97)%2bpassword%20from%20adminusers))</p>
</blockquote>
<p>or an attack specific to SQL Server:</p>
<blockquote>
  <p> somevariable=convert(int,(select top 1 table_name from information_schema.tables))--sp_password<br />
  </p>
  <p>somevariable=convert(int,(select top 1 table_name from information_schema.tables where table_name not in (dtproperties)))--sp_password<br />
  </p>
  <p>somevariable=convert(int,(select top 1 table_name from information_schema.tables where table_name not in (dtproperties,sysconstraints)))--sp_password<br />
  </p>
  <p>somevariable=convert(int,(select top 1 table_name from information_schema.tables where table_name not in (dtproperties,sysconstraints,syssegments)))--sp_password<br />
  </p>
  <p>somevariable=convert(int,(select top 1 table_name from information_schema.tables where table_name not in (dtproperties,sysconstraints,syssegments)))--sp_password</p>
</blockquote>
<p>The first problem was an exploit of the user's default error handling page -- if no error handling is in place, the error message might contain the username, password, or other information:</p>
<blockquote>
  <p>Error Executing Database Query. [Macromedia][SQLServer JDBC   Driver][SQLServer]Syntax error converting the varchar value 'yourpassword' to a   column of data type int. &lt;br&gt;The error occurred on line 102. </p>
</blockquote>
<p>In the real attack, the user password was shown on the page. The password was prefaced with the letter &quot;A&quot; -- the char(97) in the attack. This is in case the password started with a number. This can be prevented by using &lt;cfqueryparam&gt; or other device specific to your programming language to make sure integer values are passed as integers. </p>
<p>The second problem is that the default web database user has access to tables that should never be accessible to the web. The  malicious user was able to obtain table information from<strong> information_schema.tables</strong>, and work from there, systematically building each time on information that was previously obtained. </p>
<p>The best possible scenario is to turn off all table access to the web and only access data through stored procedures. That is not always possible. At the very minimum, only expose the data necessary for the site, and only allow access to statements that are required for operation of the site. For example, if you have a table called &quot;Payments&quot;, and this is only available to admins, create two SQL username/password logins and use one for the publicly accessed site and one for the admin section. Turn off all permissions to the &quot;Payments&quot; table for the web user. Create &quot;SELECT&quot; permissions only on tables that only need to have data displayed.</p>
<p>As a DBA (which you are if you have a web site with a database and you are the person responsible for the database), you need to know how to secure your data. That involves setting up specific database users for specific access. If a web host gives you a dbo user for a specific database, <strong>do not under any circumstance use this username for your web site</strong>. This user can be used to create web user logins with specific access. MySQL has similar security features. Access users are out of luck.</p>
<p>The other key is never displaying error messages to users. Make sure your error handling page only shows a pretty message to the user with no information in it, like &quot;You've created an error. Go back and try again.&quot; Or prettier than that.</p>
<p>And don't use words or letters for username/password combinations. Passwords should be 10 characters or more, and contain letters, numbers, and special characters. Brute force password guessing programs can figure out a password quickly if you use English language words or just letters.<br />
</p>
<p> I'm getting these attacks on my site too. It's scary sometimes having a web site, but hopefully there are safety measures in place to keep these parasites out.</p>
]]></description> 
			<link>http://www.tom-muck.com/blog/index.cfm?newsid=167</link>
			<guid isPermaLink="true">http://www.tom-muck.com/blog/index.cfm?newsid=167</guid>
			<pubDate>Wed, 08 Aug 2007 00:32:00 GMT</pubDate>
		</item>
		<item>
			<title>Track browser resizing in your database using AJAX -- part 2</title>
			<description><![CDATA[<p>Part 1 of this post showed the server-side code for a browser resize tracker. This part will show the client-side script. This can go on any type of page -- php, coldfusion, html, etc. The scripts consist of several functions:</p>
<ul>
<li>getBrowserSize() -- called in the onload and onresize event to capture the browser size and pass to the server-side page </li>
<li>getSize() -- gets the size of the browser window</li>
<li>passFields() -- takes an array of fields (fieldname, value, fieldname, value, etc) and a URL and passes the fields to the URL as querystring variables</li>
<li>resetSizeTimer() -- creates a timer so that when the browser is resized, only one event is recorded (browser resizing typically fires the onResize event numerous times in succession.)</li>
</ul>
<p>In addition, we set a global variable to act as a flag for the resize timer. The code is pretty straightforward, and can be placed in the head of any file:</p>
<p class="code">&lt;script&gt;<br>
var size_timer = false;<br>
<br>
// Subroutine to get the size of the window <br>
function getSize() {<br>
&nbsp;var myWidth = 0, myHeight = 0;<br>
&nbsp;if(typeof(window.innerWidth) == 'number') {<br>
&nbsp;&nbsp;//Non-IE<br>
&nbsp;&nbsp;myWidth = window.innerWidth;<br>
&nbsp;&nbsp;myHeight = window.innerHeight;<br>
&nbsp;}else if(document.documentElement &amp;&amp;<br>
&nbsp;&nbsp;(document.documentElement.clientWidth || document.documentElement.clientHeight)) {<br>
&nbsp;&nbsp;//IE 6+ in 'standards compliant mode'<br>
&nbsp;&nbsp;myWidth = document.documentElement.clientWidth;<br>
&nbsp;&nbsp;myHeight = document.documentElement.clientHeight;<br>
&nbsp;} else if(document.body &amp;&amp; (document.body.clientWidth || document.body.clientHeight)) {<br>
&nbsp;&nbsp;//IE 4 compatible<br>
&nbsp;&nbsp;myWidth = document.body.clientWidth;<br>
&nbsp;&nbsp;myHeight = document.body.clientHeight;<br>
&nbsp;}<br>
&nbsp;return [myWidth, myHeight];<br>
}<br>
<br>
// Pass fields to server given a URL and fields in name/value pairs <br>
function passFields(url,args) {<br>
&nbsp;url += &quot;?&quot;;<br>
&nbsp;for(var i=0; i&lt;args.length; i=i+2) {<br>
&nbsp;&nbsp;url += args[i] + &quot;=&quot; + args[i+1] + &quot;&amp;&quot;;<br>
&nbsp;}<br>
&nbsp;//Set up the AJAX communication <br>
&nbsp;if (window.XMLHttpRequest) {<br>
&nbsp;&nbsp;req = new XMLHttpRequest();<br>
&nbsp;} else if (window.ActiveXObject) {<br>
&nbsp;&nbsp;req = new ActiveXObject(&quot;Microsoft.XMLHTTP&quot;);<br>
&nbsp;}<br>
&nbsp;try { <br>
&nbsp;&nbsp;// Pass the URL to the server <br>
&nbsp;&nbsp;req.open(&quot;GET&quot;, url, true); <br>
&nbsp;&nbsp;req.send(null);<br>
&nbsp;}catch(e) {<br>
&nbsp;//nothing;<br>
&nbsp;}<br>
}<br>
function resetSizeTimer() {<br>
&nbsp;size_timer = false;<br>
}<br>
<br>
// Get the size and pass to the server <br>
function getBrowserSize() {<br>
&nbsp;if(size_timer)return;<br>
&nbsp;size_timer = true;<br>
&nbsp;self.setTimeout('resetSizeTimer()',1000);<br>
&nbsp;var theArray = getSize();<br>
&nbsp;var url = &quot;getBrowserSize.php&quot;;<br>
&nbsp;var args = new Array();<br>
&nbsp;args.push(&quot;width&quot;);<br>
&nbsp;args.push(theArray[0]);<br>
&nbsp;args.push(&quot;height&quot;);<br>
&nbsp;args.push(theArray[1]);<br>
&nbsp;args.push(&quot;screenwidth&quot;);<br>
&nbsp;args.push(screen.width);<br>
&nbsp;args.push(&quot;screenheight&quot;);<br>
&nbsp;args.push(screen.height);<br>
&nbsp;args.push(&quot;pagename&quot;);<br>
&nbsp;args.push(window.location);<br>
&nbsp;passFields(url, args);<br>
}<br>
<br>
  &lt;/script&gt;</p>
<p>All you need to do is to add the   getBrowserSize() function to the onload and onresize events of the &lt;body&gt; tag:</p>
<p class="code">&lt;body onload=&quot;getBrowserSize();&quot; onresize=&quot;getBrowserSize();&quot;&gt;</p>
<p>Now, when you browse the page, the server records the browser size upon load and upon resize. Typical information would look like this:</p>
<table style="border:1px solid #ccc;">
<tr><th>Width</th>
<th>Height</th>
<th>Screen <br>
width</th>
<th>Screen <br>
height</th>
<th>IP</th>
<th>Page name </th></tr>
<tr><td>856</td><td>788</td><td>1280</td><td>1024</td><td>192.168.1.2</td><td>http://mysite.com/index.cfm</td>
</tr>
<tr><td>766</td><td>625</td><td>1280</td><td>1024</td>
<td>192.168.1.2</td><td>http://mysite.com/index.cfm</td></tr>
<tr><td>948</td><td>751</td><td>1280</td><td>1024</td>
<td>192.168.1.2</td><td>http://mysite.com/index.cfm</td></tr>
<tr>
<td>1025</td><td>757</td><td>1280</td><td>1024</td><td>192.168.1.2</td><td>http://mysite.com/index.cfm</td></tr>  
</table>
<p>The technique is handy and can be used for any other situation where you need to pass client-side information to the server. </p>
<p>Cross-posted at <a href="http://www.communitymx.com/blog">CMXTraneous</a></p>
]]></description> 
			<link>http://www.tom-muck.com/blog/index.cfm?newsid=100</link>
			<guid isPermaLink="true">http://www.tom-muck.com/blog/index.cfm?newsid=100</guid>
			<pubDate>Fri, 18 Nov 2005 12:19:23 GMT</pubDate>
		</item>
		<item>
			<title>Track browser resizing in your database using AJAX -- part 1</title>
			<description><![CDATA[<p>It's always interesting to find out about viewing habits of web visitors. One of the things that is hard to determine when building a web page is how big to make your pages. Do you assume the user has 1280x1024? Do you assume 800x600? Do you assume that the user will have a fully maximized browser? One way to find out this information is to read the properties via JavaScript and store them. AJAX gives a web developer a valuble tool that allows the server  to communicate with the browser in real time based on client-side events (such as resizing). I wrote a little script that I can insert on a page to track the resizing made by a user in relation to his screen resolution. After getting this information from a variety of users, I can run queries on the data and get some insight into browsing habits and adjust my page designs accordingly (or have them adjusted by a designer, in my case.) The code will be presented for ColdFusion and PHP.</p>
<p>First, I create a table in my database to store the information. The following is for SQL Server:</p>
<p class="code">CREATE TABLE BrowserSize (<br>
&nbsp;browsersize_id int IDENTITY (1, 1) NOT NULL ,<br>
&nbsp;browsersize_width int NULL ,<br>
&nbsp;browsersize_height int NULL ,<br>
&nbsp;browsersize_screenwidth int NULL ,<br>
&nbsp;browsersize_screenheight int NULL ,<br>
&nbsp;IP varchar (50) NULL ,<br>
&nbsp;pagename varchar (255) NULL <br>
) </p>
<p>The following is the equivalent for MySQL:</p>
<p class="code"><br>
  CREATE TABLE BrowserSize (<br>
&nbsp;browsersize_id int AUTO_INCREMENT PRIMARY KEY NOT NULL ,<br>
&nbsp;browsersize_width int NULL ,<br>
&nbsp;browsersize_height int NULL ,<br>
&nbsp;browsersize_screenwidth int NULL ,<br>
&nbsp;browsersize_screenheight int NULL ,<br>
&nbsp;IP varchar (50) NULL ,<br>
&nbsp;pagename varchar (255) NULL <br>
); </p>
<p>You could also add a timestamp field, if you want to track times.</p>
<p>Next, I create a server-side page to grab the information and pass it to the database. The information will be passed in the URL. The code is self-explanatory. Basically, we pass width, height, screenwidth,  screenheight, and page location in the URL, and insert it into our database table, along with the IP address of the user. The following is for ColdFusion:</p>
<p class="code">&lt;cfparam name=&quot;url.width&quot; default=0&gt;<br>
&lt;cfparam name=&quot;url.height&quot; default=0&gt;<br>
&lt;cfparam name=&quot;url.screenwidth&quot; default=0&gt;<br>
&lt;cfparam name=&quot;url.screenheight&quot; default=0&gt;<br>
&lt;cfparam name=&quot;url.pagename&quot; default=&quot;&quot;&gt;<br>
&lt;cfset url.width = val(url.width)&gt;<br>
&lt;cfset url.height = val(url.height)&gt;<br>
&lt;cfset url.screenwidth = val(url.screenwidth)&gt;<br>
&lt;cfset url.screenheight = val(url.screenheight)&gt;<br>
&lt;cfset ip = cgi.REMOTE_ADDR&gt;<br>
<br>
&lt;cftry&gt;<br>
&lt;cfquery datasource=&quot;yourdsn&quot;&gt;<br>
INSERT INTO browserSize <br>
(browsersize_width<br>
, browsersize_height<br>
, browsersize_screenwidth<br>
, browsersize_screenheight<br>
, IP<br>
, pagename)<br>
VALUES<br>
(#url.width#<br>
,#url.height#<br>
,#url.screenwidth#<br>
,#url.screenheight#<br>
,'#ip#'<br>
,'#url.pagename#')<br>
&lt;/cfquery&gt;<br>
&lt;cfcatch&gt;<br>
&lt;!--- do nothing ---&gt;<br>
&lt;/cfcatch&gt;<br>
&lt;/cftry&gt;  </p>
<p>The following is for PHP:</p>
<p class="code">&lt;?php<br>
$_GET[&quot;width&quot;] = isset($_GET[&quot;width&quot;]) ? intval($_GET[&quot;width&quot;]) : 0;<br>
$_GET[&quot;height&quot;] = isset($_GET[&quot;height&quot;]) ? intval($_GET[&quot;height&quot;]) : 0;<br>
$_GET[&quot;screenwidth&quot;] = isset($_GET[&quot;screenwidth&quot;]) ? intval($_GET[&quot;screenwidth&quot;]) : 0;<br>
$_GET[&quot;screenheight&quot;] = isset($_GET[&quot;screenheight&quot;]) ? intval($_GET[&quot;screenheight&quot;]) : 0;<br>
$pagename = isset($_GET['pagename']) ? $_GET['pagename'] : &quot;&quot;;<br>
$ip = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : &quot;&quot;;<br>
<br>
$conn = mysql_connect(&quot;localhost&quot;, &quot;username&quot;, &quot;password&quot;);<br>
$query_rs = sprintf(&quot;INSERT INTO browserSize <br>
(browsersize_width<br>
, browsersize_height<br>
, browsersize_screenwidth<br>
, browsersize_screenheight<br>
, IP<br>
, pagename)<br>
VALUES (%s, %s, %s, %s, '%s', '%s')&quot;<br>
,$_GET[&quot;width&quot;]<br>
,$_GET[&quot;height&quot;]<br>
,$_GET[&quot;screenwidth&quot;]<br>
,$_GET[&quot;screenheight&quot;]<br>
,$ip<br>
,$pagename);<br>
mysql_select_db(&quot;cwtest&quot;);<br>
$rs = mysql_query($query_rs); <br>
?&gt;</p>
<p>Both of the pages can be run in the browser to test (saved as getBrowserSize.cfm or getBrowserSize.php respectively). If the values of 0 are inserted in the database, everything is working. I'll post the client-side AJAX code in <a href="http://www.tom-muck.com/blog/index.cfm?newsid=100">Part 2</a>.</p>
]]></description> 
			<link>http://www.tom-muck.com/blog/index.cfm?newsid=99</link>
			<guid isPermaLink="true">http://www.tom-muck.com/blog/index.cfm?newsid=99</guid>
			<pubDate>Fri, 18 Nov 2005 12:18:18 GMT</pubDate>
		</item>
		<item>
			<title>SQL function to chop a field by number of words</title>
			<description><![CDATA[<p>I was prompted by a question on the CMX forums today to finally break down and write a function to return a number of words from a database field, which I've been meaning to do for a long time. There are script examples on the web for ASP and ColdFusion code to truncate a specific database field to a certain number of words (split at the word rather than mid-word, as the LEFT function does), but there is no easy way to do it in SQL, unless you use a loop. The following function will truncate any field to a specific number of words. Pass in the string you want to parse, and the number of words to return. </p><p class="code">CREATE FUNCTION fnGetNumberOfWords (<br />&nbsp;&nbsp;@stringToSplit varchar(8000), <br />&nbsp;&nbsp;@numberOfWords int<br />)<br /><br />RETURNS varchar(8000) AS <br /><br />BEGIN <br /><br />DECLARE @currentword varchar(8000)<br />DECLARE @returnstring varchar(8000)<br />DECLARE @wordcount int<br />SET @wordcount = 0<br />SET @returnstring = ''<br />SET @currentword = ''<br />SET @stringToSplit = ltrim(rtrim(@stringToSplit))<br />Declare @index int<br /><br />WHILE @wordcount &lt; @numberOfWords AND len(@stringToSplit) &gt; 0<br />&nbsp;&nbsp;BEGIN<br />&nbsp;&nbsp;&nbsp;&nbsp;Select @index = CHARINDEX(' ', @stringToSplit)<br />&nbsp;&nbsp;&nbsp;&nbsp;if @index = 0 <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BEGIN<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SELECT @currentword = ltrim(rtrim(@stringToSplit))<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SELECT @wordcount = @numberOfWords <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;END<br />&nbsp;&nbsp;&nbsp;&nbsp;else <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BEGIN<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IF (len(@stringToSplit) - @index &gt; 0) BEGIN<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SELECT @currentword = ltrim(rtrim(LEFT(@stringToSplit, @index-1)))--the new&nbsp;shortened string<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SELECT @stringToSplit = RIGHT(@stringToSplit,LEN(@stringToSplit) - @index) -- the rest<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;END<br />&nbsp;&nbsp;&nbsp;&nbsp;END<br />&nbsp;&nbsp;SELECT @returnstring = @returnstring + ' ' + @currentword<br />&nbsp;&nbsp;SELECT @wordcount = @wordcount + 1 <br />END<br /><br />SET @returnstring = LTRIM(@returnstring)<br />RETURN @returnstring<br /><br />END</p><p>Call it like this:</p><p class="code">SELECT dbo.fnGetNumberOfWords(MyField, 10) FROM mytable</p><p>(returns first 10 words from MyField) </p><p>The advantage to doing it in the database rather than on the web page, is that you are only returning a small portion of the field to the web page, rather than the entire field. This can speed up the query. A few preliminary tests show that the smaller number of words you return, the quicker the query will execute. In other words, if your query returns a field that can contain up to 8000 characters in it (like a blog entry, for example) and you only need the first 50 words for a summary, the query to return the 50 words will be faster than a query that returns the whole field. Also, your scripted page will execute faster because it is simply displaying the field and not performing any further logic, looping, or parsing on the field. </p><p>I talked about user-defined SQL functions in one of my articles at Community MX as well:</p><p><a target="_blank" href="../../abstract.cfm?cid=9FE64">Using CSV Strings in SQL Server: Part 2</a> </p><p>Note that the function does not work on text or ntext data types. I hope you find it useful.</p> 
<p>Cross posted at <a href="http://www.communitymx.com/blog/?blogger=1">CMXtraneous</a></p>]]></description> 
			<link>http://www.tom-muck.com/blog/index.cfm?newsid=72</link>
			<guid isPermaLink="true">http://www.tom-muck.com/blog/index.cfm?newsid=72</guid>
			<pubDate>Wed, 22 Jun 2005 04:30:16 GMT</pubDate>
		</item>
		<item>
			<title>TODCON Sessions and update</title>
			<description><![CDATA[<p>I have posted my presentations from the latest <a href="http://www.todcon.org/">TODCON</a> conference at <a href="../../sessions/">http://www.tom-muck.com/sessions/</a>. Thanks to all those who attended the sessions.</p><p>TODCON was a great experience, as usual, and I love the fact that it was in Las Vegas. Holding a conference in Las Vegas pretty much guarantees that no sleep will be had. Between the sessions, the eating and drinking, the socializing, the shows, and the gambling, there is little time for sleep. I picked up a cold while I was there, probably from the smoke, dry air, and lack of sleep, but it was a fun time. </p><p>One of the sessions that I particularly enjoyed was by <a href="http://www.lucidus.net/">Neil Giarratana</a> on using open source software. He showed a presentation of <a href="http://www.mambo-foundation.org/">Mambo</a>, the open source content management system for PHP that was awesome. I may be converting some sites to use the system. As part of Mambo, he showed an amazing online HTML editor that is also open source: <a href="http://tinymce.moxiecode.com/">TinyMCE</a>. This editor blows away all the other HTML editors I've tried in the past, as it seems to work great with Firefox and Mac browsers. I'll have to do some more experimentation with it.</p><p>Dan and Angela from <a href="http://www.cartweaver.com/">Cartweaver</a> gave away a ton of prizes, including copies of Cartweaver, a year of hosting, and a whole box of books. There were also major giveaways from <a href="http://www.webassist.com/">Webassist</a>, <a href="http://www.inteaktonline.com/">Interakt</a>, <a href="http://www.kaosweaver.com/">Kaosweaver</a>, and of course from <a href="http://www.communitymx.com/author.cfm?cid=1000">Ray West</a>, who puts on the TODCON conferences. <a href="http://www.csfgraphics.com/">Chris Flick</a> was there doing the cartooning thing, of which he is the master. </p><p>What would Vegas be without gambling. I did a little, but played only Poker. I am not a big gambler and especially don't like playing against the house, so I avoid games like Roulette and Blackjack, and especially the slot machines. Poker, on the other hand, pits you against other players, and a player with a good knowledge of the game can do quite well. Unfortunately, I didn't. You know what they say -- what happens in Vegas, stays in Vegas. In this case, it was my money that stayed there. We played mostly at the <a href="http://www.excalibur.com/index2.php">Excalibur</a>, where the conference was, but also tried out the Bellagio, where I have played last time I was there, and saw a few familiar faces in the card room (Johnny Chan, Mel Judah, and Sami Farha, from the WSOP and WPT television shows.) It was fun. I left a few bucks shorter, but it was worth it. </p><p>Tuesday night a group of us went to the <a href="http://www.zumanity.com/">Zumanity</a> show at the New York, New York casino. What an amazing performance. Some of the performers were tremendous athletes who made it look easy to fly around the arena on a silk scarf or do handstands on one hand on the edge of a plexiglass pool while doing a split. It really had to be seen to be believed. The costumes were great too. ;-) If you saw the show, you know what I mean. </p><p><a href="../index.cfm?newsid=58">Previously...</a></p><p>More TODCON links:</p><p><a href="http://www.tomontheweb4.ca/DeathMatch/DeathMatch.htm">TODCON Death Match</a></p><p><a href="http://www.ourwebsite.org/storage/Todcon/">Some pics... </a></p><p><a href="http://www.markme.com/cantrell/archives/007638.cfm">Christian Cantrell's blog </a></p><p><a href="http://brainfrieze.net/weblog.php?id=D20050428">Kim's blog</a></p>
<p><strong>Update 5/4/2005:</strong> More TODCON links...</p>
<p><a href="http://www.swanilda.com/todcon_gallery/">Pics from Sheri</a></p>
<p><a href="http://www.communitymx.com/blog/index.cfm?newsid=468">Chris Flick on TODCON</a></p>
<p><a href="http://www.yapiodesign.com/blog/?p=36">John Olson on TODCON</a></p>
<p>Chris Flick's TODCON strip starts <a href="http://www.communitymx.com/flicks/03292005.cfm">here</a></p>
<p><a href="www.newmediaservices.ca/jim_babbage/todcon_05/">Pics from Jim Babbage</a></p>
<p><strong>Edited 10/1/2007</strong>: Fixed link to Mambo.]]></description> 
			<link>http://www.tom-muck.com/blog/index.cfm?newsid=60</link>
			<guid isPermaLink="true">http://www.tom-muck.com/blog/index.cfm?newsid=60</guid>
			<pubDate>Sat, 30 Apr 2005 23:20:10 GMT</pubDate>
		</item>
		<item>
			<title>Bad practice in SQL</title>
			<description><![CDATA[ <p>I have noticed over the years a very bad practice in using the SQL language: SELECT * FROM mytable. This is one of the worst things you can do for performance of your application. The performance hit is roughly equivalent to the percentage of fields that you don't need on your page. For example, if you only need 2 fields for your web page, but you are doing SELECT * and retrieving 10 fields, the statement will be about 80% slower than if you only selected the 2 fields in the statement:</p>
 <blockquote>
  <p>SELECT * from mytable 100ms</p>
  <p>SELECT fname, lname FROM mytable 20ms</p>
 </blockquote>
 <p>Say you have 10 fields in your table. You might think that you are being more efficient by writing this:</p>
 <blockquote>
  <p>SELECT * FROM MyTable</p>
 </blockquote>
 <p>instead of this:</p>
 <blockquote>
  <p>SELECT fname, lname, address, city, state, zip, phone, email FROM mytable.</p>
 </blockquote>
 <p>Since I am using 8 fields in the statement, I am not retrieving data for 2 fields. The timed results might look like this:</p>
 <blockquote>
  <p>1st statement: 100ms</p>
  <p>2nd statement 80ms</p>
 </blockquote>
 <p>This is true for the majority of databases and scripting languages across the board (PHP, ColdFusion, ASP, etc). I've even seen pages where a user will write SELECT * FROM mytable and use only one field, where the database table has 20-30 fields in it. This is outrageous, in that the performance penalty is 95-97%. In a busy web application, this can be disastrous. </p>
 <p>As a side note, when people send me pages to look at, they are very hard to debug when they use SELECT *. For one thing, I do not have access to your database, so I have to recreate the SQL statement to work with one of my sample databases. It's much easier to do when I can see what fields I need to use. </p>

        ]]></description> 
			<link>http://www.tom-muck.com/blog/index.cfm?newsid=36</link>
			<guid isPermaLink="true">http://www.tom-muck.com/blog/index.cfm?newsid=36</guid>
			<pubDate>Sun, 07 Nov 2004 14:45:59 GMT</pubDate>
		</item>
	</channel>
</rss>


