Thursday, November 6, 2008

What is SQL Server Index?

  

SQL Server Indexes are the objects used to optimize the performance of queries. It makes SQL queries select, update or delete run faster. The whole purpose of indexes is to make finding data rows in a table easier.

 

If a table is created with no indexes, then the data rows are not stored in any particular order. This structure is called a heap.

If a clustered index is created in a table, then data rows are stored in sorted order on the clustered index key making easier to find rows.

But a nonclustered index is rather complex. The data rows are not stored in order based on the noclustered key rather each nonclustered key value entry has pointers to the data rows containing the key value.

 

Here, I want to elaborate mainly the difference between using an index and not using it.

For that I first, created two tables one without index and other with index. Then, I inserted some sample data in both of them.

The SQL query used is shown below.

 

create table tbl_test_index ( pid int, cname varchar(50))

insert into tbl_test_index values (1, 'Nepal')

insert into tbl_test_index values (2, 'India')

 

create table tbl_test_index2 ( pid int , cname varchar(50))

create index test_index2 on tbl_test_index2(pid)

insert into tbl_test_index2 values (1, 'Nepal')

insert into tbl_test_index2 values (2, 'India')

 

Then, I tried to find out the cost of running a select query with order by clause.

For that I highlighted the following query and clicked "Display Estimated Execution Plan (Ctrl + L)" button on tool bar of SQL Query Analyzer to display execution plan.

 

select    *

from     tbl_test_index

order by 1

 

The output is shown below.

 

Fig: SQL Execution Plan 1

 

Then, I highlighted following query in query analyzer and viewed execution plan.

select    *

from     tbl_test_index2

order by 1

 

 

Fig: SQL Execution Plan2

 

From the first figure for case without index, following things can be obtained.

Total CPU cost = 0.0009

Total I/O cost = 0.0487

From the second figure for case with index, following things can be obtained.

Total CPU cost = 0.000812

Total I/O cost = 0.04375

Also, the estimated cost is lower in second case.

From these results, it can be concluded the use of indexes optimizes the performance of SQL Queries or makes queries faster.

However, the choice of column as key should be made carefully depending upon maximum use of the column.

 

Read more...

Tuesday, November 4, 2008

Remove comments from SQL Server Stored Procedure.

 

Comments have been a part of SQL Server procedures. If you work in ERP applications or other huge applications, then comments are even more important. They are kept for multiple purposes like to mark author, dates, causes, implications. But often, huge comments become so disgusting that, it becomes difficult to study the stored procedures. In multiple thousand line stored procedures in huge applications, it is also likely that working code is quite little while comment portion is large. I have tried here to create a stored procedure which removes comment portion from a given stored procedure.

 

The procedure works for if the length of procedue is less than 8000. Also, I have considered only multiline comment marks for developing this procedue. That means, I have considered that no single line comment in the procedure exists. The single line comment can also be removed by a single loop. I will write about that in my next post.

 

First I created a test procedure with name sp_Proc1.

Here is the code.

 

create proc sp_proc1   
as   
begin   
 create table ##tt   
 (   
  col1 varchar(200)   
 )   

/*
/*

/* insert */ into ##tt values ('test data')  */
*/

select * 
from ##tt  
end   

 

Then I created the procedure to remove comments.

This procedue just uses sp_helptext , a built function, to get the text of given input stored procedure with comments.

It puts that in a temporary table and then a single variable @sp_text. 

Then it loops through each character of that variable and removes the content between opening comment mark '/*' and closing comment mark '*./'. The procedure also works for comments inside comments. It also takes care of line returns by using custom string ('\n') to represent new line.

The code goes like this.

 

 

 /* drop table #tbl_sp_text */
create table #tbl_sp_text ( id int identity(1,1), sp_text varchar(8000))

delete from #tbl_sp_text
insert into #tbl_sp_text
exec sp_helptext  sp_proc1 /* procedure name */

declare @sp_text varchar(8000), @sp_text_row varchar(8000), @sp_no_comment varchar(8000)
declare @c char(1)
declare @i int, @rowcount int
set @sp_text = ''

select @sp_text = @sp_text + case when len(@sp_text) > 0 then '\n' else '' end + sp_text
from #tbl_sp_text


select  @i  = 1
select @rowcount = len(@sp_text)
declare @comment_count int
select @comment_count  = 0
select @sp_no_comment = ''

while @i <= @rowcount
begin
 if substring(@sp_text,@i,2) = '/*'
  select @comment_count = @comment_count + 1
 else if substring(@sp_text,@i,2) = '*/'  
  select @comment_count = @comment_count - 1  
 else if @comment_count = 0
  select @sp_no_comment = @sp_no_comment + substring(@sp_text,@i,1)

 if substring(@sp_text,@i,2) = '*/' 
  select @i = @i + 2
 else
  select @i = @i + 1
end


/* drop table #tbl_sp_no_comments */
create table #tbl_sp_no_comments ( sp_text varchar(8000))

while len(@sp_no_comment) >0
begin
 insert into #tbl_sp_no_comments
 select substring( @sp_no_comment, 0, charindex('\n', @sp_no_comment))
 select @sp_no_comment = substring(@sp_no_comment, charindex('\n',@sp_no_comment) + 2, len(@sp_no_comment))
end

select *
from #tbl_sp_no_comments

 

Read more...

Monday, November 3, 2008

Convert Amount into Words according to English Numbering Style.

If you are application programmer, you may require displaying amount figures in words for user ease.  For example, if the amount figure is $12445.41 then, in words, it becomes Twelve Thousand Four Hundred Forty Five and Forty One Cents Only.

 

There may be some built-in functions in applications. Here, I have created MS SQL functions to calculate amount in words. This function works for amount up to 999,999,999.99 i.e. Nine Hundred Ninety Nine Million Nine Hundred Ninety Nine Thousand Nine Hundred Ninety Nine and Ninety Nine Cents Only. If you require, you can change the units by replacing the words.

 

First, I create a function which converts number less than 10 to words. The function goes like this.

 

CREATE    Function dbo.fConvertDigit(@decNumber decimal)

returns varchar(6)

as

Begin

declare

@strWords varchar(6)

            Select @strWords = Case @decNumber

                When '1' then 'One'

                When '2' then 'Two'

                When '3' then 'Three'

                When '4' then 'Four'

                When '5' then 'Five'

                When '6' then 'Six'

                When '7' then 'Seven'

                When '8' then 'Eight'

                When '9' then 'Nine'

                Else ''

            end

return @strWords

end

 

Then, I created a function to convert number less than 100 to words using above function.

 

CREATE    Function dbo.fConvertTens(@decNumber varchar(2)) 

returns varchar(30) 

as 

Begin 

declare 

@strWords varchar(30) 

-- If amt is between 10 and 19

If Left(@decNumber, 1) = 1  

begin 

 Select @strWords = Case @decNumber 

     When '10' then 'Ten' 

     When '11' then 'Eleven' 

     When '12' then 'Twelve' 

     When '13' then 'Thirteen' 

     When '14' then 'Fourteen' 

     When '15' then 'Fifteen' 

     When '16' then 'Sixteen' 

     When '17' then 'Seventeen' 

     When '18' then 'Eighteen' 

     When '19' then 'Nineteen' 

 end 

end 

else  -- if amt is between 20 and 99

begin 

 Select @strWords = Case Left(@decNumber, 1) 

     When '0' then ''   

     When '2' then 'Twenty ' 

     When '3' then 'Thirty ' 

     When '4' then 'Forty ' 

     When '5' then 'Fifty ' 

     When '6' then 'Sixty ' 

     When '7' then 'Seventy ' 

     When '8' then 'Eighty ' 

     When '9' then 'Ninety ' 

 end 

 Select @strWords = @strWords + dbo.fConvertDigit(Right(@decNumber, 1)) 

end 

 --Convert ones place digit. 

  

return @strWords 

end 

 

  Now, I created function to convert numbers less than 1000 to words using above functions.

 

CREATE Function dbo.fConvertHundreds (@decNumber varchar(3)) 

returns varchar(200) 

as  

Begin 

declare @strWords varchar(200) 

 

 Select @strWords = Case left(@decNumber,1) 

     When '1' then 'One' 

     When '2' then 'Two' 

     When '3' then 'Three' 

     When '4' then 'Four' 

     When '5' then 'Five' 

     When '6' then 'Six'  

     When '7' then 'Seven' 

     When '8' then 'Eight' 

     When '9' then 'Nine' 

     Else '' 

 end 

  

 if ltrim(rtrim(@strWords)) <> '' and @strWords is not null 

  select @strWords = @strWords + ' Hundred '+ dbo.fconvertTens(right(@decNumber,2)) 

 else 

  select @strWords = dbo.fconvertTens(right(@decNumber,2)) 

  

return @strWords 

end 

 

Finally, I created the function to convert amount less than 999,999,999.99 to words using above 3 functions. The basic concepts used are follows.

If the input amt contains decimal (.), then take two numbers after decimal and convert them to words using fConvertTens() function.

If the input amt contains 3 or less characters before decimal, then the amt will be less than 1000, so, use fConvertHundreds() function.

If the input amt contains 4 to 6 characters before decimal, then amt in words will contain thousand parts and hundred parts only. Use fConvertHundreds() for final 3 numbers to calculate hundreds part and numbers before that to calculate thousand parts.

If the input amt contains 7 to 9 characters before decimal, then amt in words will contain million part, thousand part and hundred part. Use fConvertHundreds() for final 3 numbers to calculate hundreds part, and 3 numbers before that to calculate thousands part and remaining numbers before 6 numbers to calculate million part.

 

The function is given below.

CREATE function dbo.fNumToWords

(@decNumber decimal(12, 2)) 

returns varchar(300) 

As 

Begin 

Declare 

 @strnum varchar(100), 

 @strCents varchar(100), 

 @strWords varchar(300), 

 @intIndex integer 

 

 

Select @strnum = Cast(@decNumber as varchar(100)) 

Select @intIndex = CharIndex('.', @strnum) 

select @strCents = '' 

 

if(@decNumber>999999999.99) 

BEGIN  

 RETURN '' 

END 

 

If @intIndex > 0  

begin 

 Select @strCents = dbo.fConvertTens(Right(@strnum, Len(@strnum) - @intIndex)) 

 Select @strnum = SubString(@strnum, 1, Len(@strnum) - 3) 

 If Len(@strCents) > 0 Select @strCents = @strCents + ' Cents' 

end 

 

declare @trail_zeros  varchar(3) 

declare @strthousands varchar(3) 

declare @strMillions varchar(3) 

 

set @trail_zeros = '000' 

 

if len(@strnum) <= 3 

begin 

 select @strWords = dbo.fConvertHundreds(left(@trail_zeros,3-len(right(@strnum,3)))+ right(@strnum,3)) 

end  

if len(@strnum) >= 4 and len(@strnum) <=6 

begin 

 select @strthousands = left(@trail_zeros,3 - len(left(right(@strnum,6),len(@strnum)-3))) + left(right(@strnum,6),len(@strnum)-3) 

 select @strWords = dbo.fConvertHundreds(@strthousands) + ' Thousand ' + dbo.fConvertHundreds(left(@trail_zeros,3-len(right(@strnum,3)))+ right(@strnum,3)) 

end 

if len(@strnum) >= 7 and len(@strnum) <=9 

begin 

 select @strMillions = left(@trail_zeros,3-len(left(@strnum,len(@strnum)-6))) + left(@strnum,len(@strnum)-6) 

 select @strthousands = left(right(@strnum,6),3) 

 select @strWords = dbo.fConvertHundreds(@strMillions) + ' Million ' + dbo.fConvertHundreds(@strthousands) + ' Thousand ' + dbo.fConvertHundreds(left(@trail_zeros,3-len(right(@strnum,3)))+ right(@strnum,3)) 

end 

 

if @strCents <> '' 

 select @strWords = @strWords + ' and ' + @strCents + ' Only' 

else 

 select @strWords = @strWords + ' Only' 

 

return  @strWords 

 

end 

 

Read more...

Saturday, November 1, 2008

Paging in Web Applications (SQL Trick)

Web Applications here means the applications that are used through browsers. Web applications are tougher to develop and use as compared to windows applications. In this topic, I want to put focus on paging techniques for web applications.

If you are a web programmer, you may be having problem to display a large report or data in a data grid with paging. Paging is required as it might not be practical to display large no. of rows in a single grid. User will have to keep scrolling down and up. Also, a large memory will be constantly consumed. There are no. of ways to use paging in data grids. Here, I would like to focus on data grid displayed by pulling data from RDBMS database like SQL Server.

Suppose you are running a stored procedure (say sp_report1) to pull data from SQL Server database and it returns all data (say 5000 rows) at one time. A simple way would be to pass two input variables like @from_row_no and @to_row_no to the procedure and modify it to return rows of data from @from_row_no to @to_row_no.

This also can be done two ways. In first way, you put all output data in a temporary table which also contain an identity column. Then, while running final select statement from that final output table, use conditions to display from @from_row_no to @to_row_no.

Here is an example.

Create proc sp_Report1

( @from_row_no int, @to_row_no int)

as

begin

create table #tbl_final_display

(

id  int identity(1,1),

col1 varchar(30),

col2 varchar(30)

)

/* now insert data into #tbl_final_display as per your requirement */

-- for final select

select *

from #tbl_final_display

where id >= @from_row_no

and id <= @to_row_no

end

If you have to display results in order then you will have to pass ordering column name also to stored procedure. If the data in #tbl_final_display is going in single statement you can use order by clause in that statement with passed input variable. If not, you will have to create two temporary tables. First one to just hold the data and second one to copy data from first one by using order by clause. The second table will obviously have one more column than first one for id field. This process becomes tedious when there are many sortable columns as many if else conditions are required.

Here "id int identity(1,1)" means that id column of the table will have datatype int and the values will be automatically inserted while inserting rows in that column. The values will start from 1 and will keep on increasing by 1 for each new row.

In Second Method, you need to use the permanent table in which data are kept temporary. The stored procedure will just populate the table. Final select from table is run from front end like this.

For example to retrieve data from table tbl_final_display from row 9 to row 14 order by first field, you can use this query.

select    *
from    (
    select    top 6 *
    from    (
        select    top 14 *
        from    tbl_final_display
        order by 1
    ) tmp
    order by 1 desc
    ) tmp
order by 1

It uses multiple sub-queries and multiple select. First it selects 14 rows (@to_row_no) and then selects last 6 rows from that select. This has to be done from front end as while using "top 6 " we cannot pass variable instead of 6. It may be tedious for programmers to handle from front-end as much additional care must be taken. Such as provide fields to distinguish multiple users to eradicate the chances of wrong output when same report is used by multiple users.

Read more...