Category Archives: Imported from BeyondRelational

These posts are imported from my old blog page: http://beyondrelational.com/modules/2/blogs/77/nakuls-blog.aspx

SQL Server – Know your query SET options – Properties window and DBCC USEROPTIONS


The properties and behaviour of each session to a SQL Server instance can be handled individually via use of various SET options. As we multi-task during the day, we tend to forget the exact SET options that we choose for a particular user session. The focus of the day is how to retrieve this information programmatically, i.e. through T-SQL.

Retrieving SET options for a given session

To quickly review the SET options for a given session, we have two methods:

  1. Properties Window
  2. Using DBCC commands
    • For those would be interested to get these SET options via code, the DBCC comes to our rescue
    • Let’s take a quick look at this method to get the user options

Demo

Using the Properties window to get the user options involves executing a query workload and retrieving the actual execution plan. DBCC, on the other hand allows us to fetch the user options without any workload and without retrieving the execution plan.

For this demonstration, let’s make a small change to the default user options, and run the DBCC statement in the query below.

--Change the connection option
SET ANSI_NULLS OFF

--Retrieve the user options for this connection
DBCC USEROPTIONS
GO

Now, let’s roll back the change (or launch a new session by creating a new query editor window), and run the following statement.

--Change the connection option
SET ANSI_NULLS ON

--Retrieve the user options for this connection
DBCC USEROPTIONS
GO

Comparing the outputs:

Comparing the output from the two sessions, we can see that the user options that are turned OFF do not appear on the list.

ANSI_NULLS OFF   ANSI_NULLS ON  
Set option Value Set option Value

textsize

2147483647

textsize

2147483647

language

us_english

language

us_english

dateformat

mdy

dateformat

mdy

datefirst

7

datefirst

7

lock_timeout

-1

lock_timeout

-1

quoted_identifier

SET

quoted_identifier

SET

arithabort

SET

arithabort

SET

ansi_null_dflt_on

SET

ansi_null_dflt_on

SET

ansi_warnings

SET

ansi_warnings

SET

ansi_padding

SET

ansi_padding

SET

  ansi_nulls SET

concat_null_yields_null

SET

concat_null_yields_null

SET

isolation level

read committed

isolation level

read committed

Reference

Books On line page for DBCC USEROPTIONS – http://msdn.microsoft.com/en-us/library/ms180065.aspx

Until we meet next time,

Be courteous. Drive responsibly.

Subscribe to my posts via E-mail: Subscribe here | Read my posts via your favourite RSS reader: Click Here!

SQL Server – Performance tuning – DBCC FLUSHPROCINDB – Flush procedures of a particular database from cache


Performance tuning & analysis during new development has it’s own unique challenge – on one hand as database developers, we need a clean procedure cache and on the other hand, we do not want to impact the performance of other databases on the same instance.

To start from a cold procedure cache, we know that we can use the DBCC FREEPROCCACHE. However, this would completely clear out the procedure cache – impacting others who may be working on the same instance, but on other databases. We can supply the plan/query handle or pool name to reduce the impact of the DBCC FREEPROCCACHE, but because the plan handle changes each time the plan is regenerated, a lot more manual steps become involved in the process.

To restrict the clearing of the procedure cache to the current database only, we can use the DBCC FLUSHPROCINDB command. The DBCC FLUSHPROCINDB command only accepts one parameter – the database ID.

For example, the following command will flush the procedures in cache for the AdventureWorks2008R2 database on my SQL Server instance.

/*
!!!!WARNING!!!!
This script is provided AS-IS and without warranty.
The author, Nakul Vachhrajani; the website, BeyondRelational.com
and Microsoft Corproation are not responsible for any damage caused
by misuse of this script
*/

--Fetch the DB_ID for the required database
USE AdventureWorks2008R2
GO
SELECT DB_ID() AS AdventureWorks2008R2DBID

--Clear out the procedure cache for the AdventureWorks2008R2 database ONLY
DBCC FLUSHPROCINDB (5)

image

References

NOTE: To the best of my knowledge, this is an undocumented DBCC command. Please use it with utmost caution. If you find the official Books On Line documentation, please let me know and I will update the post to reflect the same.

Until we meet next time,

Be courteous. Drive responsibly.

Subscribe to my posts via E-mail: Subscribe here | Read my posts via your favourite RSS reader: Click Here!

SQL Server – Experiments with BIT data type – Is it really Boolean? Can values other than 0 and 1 be assigned?


Recently at the office, we ended up discussing the BIT data type (http://msdn.microsoft.com/en-us/library/ms177603.aspx). While we all understand that BIT is supposed to represents a Boolean value, we were not quite convinced. Boolean values are two-state – they can only be TRUE or FALSE (or 1 and 0). However, we have seen BIT to be NULL a couple of times. Therefore, the questions that we had in our minds were:

  1. Is BIT really a two-state data type? Or is it tri-state (for those not familiar with the world of electronics, the tri-state is a state that’s undefined – it’s neither ON nor OFF)
  2. Do we need to use only a 0 or a 1 to initialize the BIT data type?
  3. Can we use ‘TRUE’ and ‘FALSE’ with the BIT datatype?

Finally, we decided to perform a little experiment. We prepared a small script covering the possible scenarios and checked their outputs. The script is available for your reference below:

--Declare the variable
DECLARE @bit BIT

--Check for default value
SELECT @bit AS DefaultValue

--Set to a positive value, other than 0 or 1
SET @bit = 99;
SELECT @bit AS [PositiveValue];

--Set to a negative value
SET @bit = -99;
SELECT @bit AS [NegativeValue];

--Set to a decimal value > 0 and < 1
SET @bit = 0.25;
SELECT @bit AS [DecimalValue025];

--Set to a decimal value > 0 and < 1
SET @bit = 0.50;
SELECT @bit AS [DecimalValue050];

--Set to a decimal value > 0 and < 1
SET @bit = 0.75;
SELECT @bit AS [DecimalValue075];

--Set to a string value - TRUE
SET @bit = 'TRUE';
SELECT @bit AS [StringTRUE],
       CASE
           WHEN @bit = 'TRUE'
           THEN 'Yes'
           ELSE 'No'
       END AS  [StringComparison];

--Set to a string value - FALSE
SET @bit = 'FALSE';
SELECT @bit AS [StringFALSE],
       CASE
           WHEN @bit = 'TRUE'
           THEN 'Yes'
           ELSE 'No'
       END AS  [StringComparison];

The results that came up resolved all our questions.

Results from my experiments with the BIT datatype. (Image (c)SQLTwins, nakulvachhrajani.com)
Experimenting with the BIT datatype in SQL Server

Conclusion

The above experiment helped us conclude that:

  1. BIT values are not completely Boolean, they are tri-state with NULL, 0 and 1 being possible values
  2. If left unassigned, the value is NULL
  3. If assigned with a value anything other than 0 or NULL, the value is taken as 1
  4. You can use a ‘TRUE’ and ‘FALSE’ string values with the BIT data type
  5. Because the default value of BIT is NULL, always assign your BIT variables! (for that matter, always assign a default value to any variable!)

I trust you found the above experiment interesting. I would welcome your ideas for future experiments.

Until we meet next time,

Be courteous. Drive responsibly.

SQL Server – Storage requirements – CHAR v/s VARCHAR – choose the correct data-type


I work with academia frequently, and therefore, get a chance to interact with students and experience the issues they face first hand. I recently had a very interesting experience during one of these visits. I will try to present the experience as a story.

The Problem

One of the students had developed a system which involved some database interaction. As I was reviewing the system, I noticed an issue with the table design. The design that the student had come up with was something like the following:

USE tempdb
GO
--Create the table, with fixed length columns
CREATE TABLE CityMaster (CityId      INT IDENTITY (1,1),
                         CityName    CHAR(50),
                         StateName   CHAR(50),
                         CountryName CHAR(50)
                        )
GO

(The design above is not exactly what he had, but you get the general idea.)

The problem that I had was with his choice of data types. He had chosen a fixed-length data type – CHAR.

I explained that CHAR, being a fixed-length data type, pads a string with trailing blanks when the data is stored to achieve the fixed-length. Variable length data types (e.g. VARCHAR), on the other hand, do not pad trailing blanks, and therefore, have a variable space requirement.

To explain this with an example, the simplest thing for me to do was to insert some test values in his database. Once the test data was ready, I ran the following query, which uses the DATALENGTH() and LEN() functions. For those who came in late,

  • DATALENGTH = Returns the number of bytes used to represent an expression
    • This can be used to estimate the storage space that SQL Server has taken up for an expression
  • LEN = Returns the number of characters of the specified string expression, excluding trailing blanks
USE tempdb
GO                        
--Insert test data
INSERT INTO CityMaster (CityName, StateName, CountryName)
VALUES ('Ahmedabad','Gujarat',    'India'),
       ('Mumbai'   ,'Maharashtra','India'),
       ('New Delhi','Delhi'      ,'India'),
       ('Bengaluru','Karnataka'  ,'India')
GO

USE tempdb
GO
--View the length and the data length for the data in the table
SELECT CityMaster.CityId,
       CityMaster.CityName,    LEN(CityMaster.CityName)    AS CityNameLength,    DATALENGTH(CityMaster.CityName)  AS CityNameDataLength,
       CityMaster.StateName,   LEN(CityMaster.StateName)   AS StateNameLength,   DATALENGTH(CityMaster.StateName) AS StateNameDataLength,
       CityMaster.CountryName, LEN(CityMaster.CountryName) AS CountryNameLength, DATALENGTH(CityMaster.CountryName) AS CountryNameDataLength
FROM CityMaster
GO

image

Using the output of the above query, it became easy for me to explain that because CHAR consumes the full 50 characters, the SQL Server consumes a space of 50 + 50 + 50 = 150 bytes irrespective of the amount of data actually being requested for storage.

The solution

As a solution, I recreated the table with the following design:

USE tempdb
GO
--Create the table, with variable length columns
CREATE TABLE CityMaster_v2 (CityId      INT IDENTITY (1,1),
                            CityName    VARCHAR(50),
                            StateName   VARCHAR(50),
                            CountryName VARCHAR(50)
                           )
GO

Again, I inserted the test data and ran the length determination queries.

USE tempdb
GO                          
--Insert test data
INSERT INTO CityMaster_v2 (CityName, StateName, CountryName)
VALUES ('Ahmedabad','Gujarat',    'India'),
       ('Mumbai'   ,'Maharashtra','India'),
       ('New Delhi','Delhi'      ,'India'),
       ('Bengaluru','Karnataka'  ,'India')
GO

USE tempdb
GO
--View the length and the data length for the data in the table
SELECT CityMaster_v2.CityId,
       CityMaster_v2.CityName,    LEN(CityMaster_v2.CityName)    AS CityNameLength,    DATALENGTH(CityMaster_v2.CityName)  AS CityNameDataLength,
       CityMaster_v2.StateName,   LEN(CityMaster_v2.StateName)   AS StateNameLength,   DATALENGTH(CityMaster_v2.StateName) AS StateNameDataLength,
       CityMaster_v2.CountryName, LEN(CityMaster_v2.CountryName) AS CountryNameLength, DATALENGTH(CityMaster_v2.CountryName) AS CountryNameDataLength
FROM CityMaster_v2
GO

The results spoke for themselves, and he quickly grasped the concept that I was trying to put forward, i.e. variable length datatypes have varying storage requirements, making them more efficient from a storage perspective.

image

ALTER TABLE…ALTER COLUMN will not help in releasing space from fixed-length data-types

Because the concept was clear, he proceeded with converting his existing table designs to use variable-length data types using the ALTER TABLE…ALTER COLUMN statement.

USE tempdb
GO
--Changing CHAR to VARCHAR will NOT release space back once the allocations have been done
ALTER TABLE CityMaster
    ALTER COLUMN CityName VARCHAR(50)

ALTER TABLE CityMaster
    ALTER COLUMN StateName VARCHAR(50)
    
ALTER TABLE CityMaster
    ALTER COLUMN CountryName VARCHAR(50)
GO

However, he found that the ALTER TABLE…ALTER COLUMN statement did not make a difference to the storage requirements.

image

The reason, I explained, was that once SQL Server has added trailing spaces, it is no longer able to distinguish between an intentionally padded and an automatically padded version of the same string (e.g. ‘BeyondRelational.com ‘ (3 spaces) from ‘BeyondRelational.com’ (no spaces)) and therefore, SQL Server will not be able to release the space allocated to the trailing spaces back.

The only option for him was to drop-and-recreate the tables and then re-insert all his test data.

Now that we were all set with the concept, I pointed him to one of Vinod Kumar’s (blog|twitter) posts: http://blogs.extremeexperts.com/2003/10/10/using-varchars/

Conclusion

There are not one, but 2 very good reasons why I shared this experience with the community.

  1. The prime reason being that this post might be helpful to students trying to understand and explore databases, and inculcate in them a habit of choosing the correct data-types
  2. I have seen production code with such issues, such as the incorrect choice of data-types. For us in the industry, this is a capital mistake that should not be made, ever

Reference

Until we meet next time,

Be courteous. Drive responsibly.

Subscribe to my posts via E-mail: Subscribe here | Read my posts via your favourite RSS reader: Click Here! | Tweet to @nakulv_sql

SQL Server – T-SQL – Different ways to generate a comma-separated string from a table


Recently, someone in the team faced a fairly common requirement – to generate a comma-separated string from values stored in a table. This being the last post of the year, I thought of sharing the 2 most-commonly used methods I know of implementing this requirement.

Do you know any other? If you share it on this post, I will publish it with due credit on my blog.

----------------------------------------------------
--WARNING: THIS SCRIPT IS PROVIDED AS-IS AND WITHOUT
--         WARRANTY.
--         FOR DEMONSTRATION PURPOSES ONLY       
----------------------------------------------------
--Step 01: Generate Temp table to store source data
DECLARE  @NamesTable TABLE (Id INT,
                            Name NVARCHAR(50))

--Step 02: Generate test data
INSERT INTO @NamesTable VALUES (1,'A'),
                               (2,'D'),
                               (2,'C'),
                               (3,'E'),
                               (3,'H'),
                               (3,'G')

--Option 01: My favourite
DECLARE @listStr VARCHAR(MAX) --DO NOT initialize this one!

SELECT @listStr = COALESCE(@listStr + ',' ,'') + nt.Name
FROM @NamesTable nt

SELECT @listStr

--Option 02: Using XML
; WITH CommaSeparatedXML (CommaSeparatedXML)
AS (SELECT CAST((SELECT (',' + nt.Name)
                 FROM @NamesTable nt
                 FOR XML PATH('')) AS NVARCHAR(MAX))
   )
SELECT SUBSTRING(CommaSeparatedXML, 2, LEN(CommaSeparatedXML))
FROM CommaSeparatedXML
GO

Until we meet next time,

Be courteous. Drive responsibly.