N S

Novick Software Management • Design • Programming • Training • Consulting

   

 

 

SQL Server T-SQL User-Defined Function of the Week

A Readable Data Type from System Tables Columns

Volume 1 Number 18   March 18, 2003

Sign up for this newsletter at:
http://www.novicksoftware.com/UdfOfWeek/UDFofWeekSignup.htm
I've written a new article on Indexed Views and it will be 
published later this week at the web site www.DatabaseJournal.com
As part of that article I included a UDF, 
udf_View_ColumnsIndexableTAB, that shows which columns in a 
view are eligible for indexing. It'll be next week's UDF of the 
week.  But before I show you that UDF, I have to show you this 
week's UDF.

This week's UDF is udf_SQL_DataTypeString.  It can be used to 
construct a data type that a human DBA or programmer is familiar 
with.  Types such as "int" or "varchar(283)".  The 
UDF is necessary because SQL Server doesn't store data types in 
the form that we'd like to see.

Instead of storing a data type as a single column with values
such as "numeric (18,3)", the INFORMATION_SCHEMA views and 
systems tables store at the four columns shown in this table:

Column Name              Data Type    Descriptions
------------------------ ------------ ---------------------------
DATA_TYPE                nvarchar(128)Base data type: int, char
CHARACTER_MAXIMUM_LENGTH smallint     Max length in chars
NUMERIC_PRECISION        tinyint      Significant digits stored
NUMERIC_SCALE            tinyint      Digits to the right of 
                                      the decimal                                     

There are other columns with information about the data 
such as DATETIME_PRECISION and CHARACTER_SET_NAME but they're 
not needed to reconstruct the data type that we're familiar
with.

Here's the create function script:
/-------- Copy from below this line ----------------------------\
SET QUOTED_IDENTIFIER ON 
SET ANSI_NULLS ON 
GO

CREATE    FUNCTION dbo.udf_SQL_DataTypeString (

    @BaseDataType nvarchar(128) -- base name like int, numeric
  , @Character_Maximum_Length int
  , @Numeric_Precision int
  , @Numeric_Scale int
)  RETURNS nvarchar(24) -- Data type name like 'numeric (15, 3)'
   WITH SCHEMABINDING
/*
* Returns a data type with full length and precision information
* based on fields originally queried from 
* INFORMATION_SCHEMA.ROUTINES or from SQL_VARIANT_PROPERTIES.  
* This function is intended to help when reporting on functions 
* and about data.
*
* Example:
SELECT ROUTINE_NAME as [Function]
  , dbo.udf_SQL_DataTypeString (Data_Type
    , Character_Maximum_Length, Numeric_Precision, Numeric_Scale)
    as [Data Type]  FROM information_schema.routines
      WHERE ROUTINE_TYPE='FUNCTION'
*
* © Copyright 2003 Andrew Novick http://www.NovickSoftware.com
* You may use this function in any of your SQL Server databases
* including databases that you sell, so long as they contain 
* other unrelated database objects. You may not publish this 
* UDF either in print or electronically.
* Published in T-SQL UDF of the Week Newsletter Vol 1 #18
http://www.NovickSoftware.com/UDFofWeek/UDFofWeek.htm
****************************************************************/
AS BEGIN

RETURN LTRIM(RTRIM(@BaseDataType))
      + CASE WHEN @BaseDataType in ('char', 'varchar'
                                   , 'nvarchar', 'nchar')
             THEN '(' 
                 + CONVERT (varchar(4)
                          , @Character_Maximum_Length)
                 + ')'
             WHEN @BaseDataType in ('numeric', 'decimal')
             THEN '(' 
                 + Convert(varchar(4), @Numeric_Precision) 
                 + ' ' + convert(varchar(4), @Numeric_scale)
                 + ')'
             ELSE '' -- empty string
             END
END

GRANT EXEC, REFERENCES ON dbo.udf_SQL_DataTypeString to [PUBLIC]
GO
\-------- Stop copying above this line -------------------------/

There are several differences between this week's CREATE FUNCTION
script and those in past newsletters:

  - SET statements at the beginning of scripts
  - WITH SCHEMABINDING clause
  - GRANT of the REFERENCES permission
  
All three of these are required if a computed column that 
invokes a UDF is to be used by an indexed view.  Now I'm the 
first to admit that the circumstances for using indexed views
are limited. But these three factors improve the UDF and I'm 
going to be more careful about including them when they're 
appropriate.

Over the next few weeks you'll see a lot in this space about 
factors that influence indexed views.  And by-the-way I'll send
out a notice when the article becomes available.

This week lets discuss the SET statements at the top of the
script:

SET QUOTED_IDENTIFIER ON 
SET ANSI_NULLS ON 
GO

Indexed views require that a group of 7 session options be set
to consistent values (The complete table is in the article). 
Two of them are QUOTED_IDENTIFIER and ANSI_NULLS, both of which 
must be ON.

These parameters can't be set in a UDF at run time.  The UDF 
executes with the setting what was in effect at parse time.
That is, when the CREATE FUNCTION or ALTER FUNCTION script
was last run.

If you ever want to use indexed views, the prudent thing to do 
is to set these two session options ON at the start of every 
UDF script. 

QUOTED_IDENTIFIER is pretty common and won't require much
adjustment on your part.  ANSI_NULLS is another story.  It 
affects the way that comparisons to NULL are performed.  If 
your code hasn't been tested with this setting ON, don't switch
until you can retest.

To make an interesting result I created the UDF in the Northwind 
sample database. Lets try our UDF of the week and see what it 
shows:
/-------- Copy from below this line ----------------------------\
SELECT DISTINCT TOP 15
       COLUMN_NAME as [Column]
     , dbo.udf_SQL_DataTypeString (Data_Type
           , Character_Maximum_Length, Numeric_Precision
           , Numeric_Scale) AS [Data Type]  
    FROM INFORMATION_SCHEMA.COLUMNS
GO
\-------- Stop copying above this line -------------------------/
(Results)
Column                  Data Type          
----------------------- -------------------
OrderID                 int                
CustomerID              nchar(5)           
ReorderLevel            smallint           
Discontinued            bit                
CategoryName            nvarchar(15)       
ProductID               int                
ProductName             nvarchar(40)       
EmployeeID              int                
OrderDate               datetime           
RequiredDate            datetime           
ShippedDate             datetime           
ShipVia                 int                
Freight                 money              
ShipName                nvarchar(40)       
ShipAddress             nvarchar(60)       

udf_SQL_DataTypeString is similar to the UDF from issue #12,
udf_SQL_VariantToDatatypeName.  In fact that function, which
works on sql_variant data, could have used this week's UDF 
instead of including the code inline.

I'll let you know when the article is available.  The existence
of Indexed views in SQL Server 2000 influenced many of the 
choices the SQL Server development team made for UDFs.  There
will be more about these factors in the upcoming weeks.

Please don't forget to share this issue with anyone interested
in SQL Server.  Thanks.

+--------------------------------------------------------------+
Do you have a great UDF that you'd like to share?  Or maybe you
have a T-SQL problem that you think could be solved by a UDF
but you don't know how? Send them to:

UDF@NovickSoftware.com 

and they might be published in this newsletter.  I try and 
respond to every request that I get.  

Thanks,  
Andrew Novick
+--------------------------------------------------------------+


RSS as HTML

Personal Blog

 
New Tips:

Loading SQL Profiler trace (.trc) files with fn_trace_gettrace

Upcoming
Presentations:

Big Data:
Working with Terabytes
in SQL Server

July 9 '08


Full Schedule