Feeds:
Posts
Comments

Archive for May, 2013

LTRIM and RTRIM are very useful functions when it comes to trim the left side or right side of the string spaces respectively but if you need to trim spaces from both sides , you need to create a user defined function. Here is the script of user defined function. I have been using these built-in and user defined functions for a long time. But a  few days ago, when I was migrating the data from legacy system to our new system, I came across with an issue i.e., I had to trim not only the spaces but also the characters as well.

Given below is the solution :

Left Trim :
Given below is the solution that can help you to trim(remove) the spaces and characters from the LEFT side of the string.

CREATE FUNCTION dbo.L_TRIM(@String VARCHAR(MAX), @Char varchar(5))
RETURNS VARCHAR(MAX)
BEGIN
RETURN SUBSTRING(@String,PATINDEX('%[^' + @Char + ' ]%'
,@String),DATALENGTH(@String))
END
GO
--In this example, the user defined function will remove spaces
--and * from the left side of the string
SELECT '  **  THIS IS A TEST STRING  ***  '
AS [String before Left trim]
, dbo.L_TRIM('  **  THIS IS A TEST STRING  ***  ','*')
AS [String After Left trim]
GO
--In this example, the user defined function will remove spaces
--and 0 from the left side of the string
SELECT ' 0001234' AS [String before Left trim]
, dbo.L_TRIM(' 0001234','0')  [String After Left trim]
GO
--OUTPUT

lefttrim1.1

Right Trim :
Given below is the solution that can help you to trim(remove) the spaces and characters from the RIGHT side of the string.

CREATE FUNCTION dbo.R_TRIM(@String VARCHAR(MAX), @Char varchar(5))
RETURNS VARCHAR(MAX)
BEGIN
RETURN REVERSE(SUBSTRING(REVERSE(@String)
,PATINDEX('%[^' + @Char + ' ]%'
,REVERSE(@String)),DATALENGTH(@String)))
END
GO
SELECT '  **  THIS IS A TEST STRING  ***'
AS [String before Right trim]
, dbo.R_TRIM('  **  THIS IS A TEST STRING  ***  ','*')
AS [String after Right trim]
GO
SELECT '12340000 ' AS [String before Right trim]
, dbo.R_TRIM('12340000 ','0') AS [String after Right trim]
GO
--OUTPUT

righttrim1.2

Trim :
Given below is the solution that can help you to trim(remove) the spaces and characters from the BOTH sides of the string.

CREATE FUNCTION dbo.TRIM(@String VARCHAR(MAX), @Char varchar(5))
RETURNS VARCHAR(MAX)
BEGIN
RETURN SUBSTRING(@String,PATINDEX('%[^' + @Char + ' ]%',@String)
,(DATALENGTH(@String)+2 - (PATINDEX('%[^' + @Char + ' ]%'
,REVERSE(@String)) + PATINDEX('%[^' + @Char + ' ]%',@String)
)))
END
GO
SELECT '  **  THIS IS A TEST STRING  ***  '
AS [String before trim]
, dbo.TRIM('  **  THIS IS A TEST STRING  ***  ','*')
AS [String after trim]
GO
SELECT ' 000012340000 ' AS [String before trim]
, dbo.TRIM(' 000012340000 ','0') AS [String after trim]
GO
--OUTPUT

trim1.3

Read Full Post »

A few days ago, I wrote an article about Multiple ways to convert month number to month name. Recently, I received a query inquiring how to convert Month name to Month number.

Let me create a sample to demonstrate this solution.

USE tempdb
GO
CREATE TABLE tbl_MonthName
(
[ID] INT,
[MONTH_NAME] VARCHAR(50)
)
GO

INSERT INTO tbl_MonthName
SELECT 1, N'January' UNION ALL
SELECT 2, N'February' UNION ALL
SELECT 3, N'March' UNION ALL
SELECT 4, N'April' UNION ALL
SELECT 5, N'May' UNION ALL
SELECT 6, N'June' UNION ALL
SELECT 7, N'July' UNION ALL
SELECT 8, N'August' UNION ALL
SELECT 9, N'September' UNION ALL
SELECT 10, N'October' UNION ALL
SELECT 11, N'November' UNION ALL
SELECT 12, N'December'
GO

SELECT * FROM tbl_MonthName
GO
--OUTPUT

monthnametono1.1

Method 1 :
In this method, we will use the Old approach. In this approach, we need to manually concatenate day with month name and year to make a complete date of that particular month using additional single quotes and then get the Month number from this concatenate date using MONTH function. Given below is the script.

--This script is compatible with SQL Server 2005 and above.
USE tempdb
GO
SELECT
[ID]
,[MONTH_NAME]
,MONTH('1' + [MONTH_NAME] +'00') AS [MONTH_NUMBER]
FROM tbl_MonthName
GO
--OUTPUT

monthnametono1.2

Method 2 :
In this method, we will use the New approach. In this approach, we need to automatically concatenate day with month name and year to make a complete date of that particular month using CONCAT function (without any single quotes) and then get the Month number from this concatenate date using MONTH function. Given below is the script.

--This script is compatible with SQL Server 2012 and above.
USE tempdb
GO
SELECT
[ID]
,[MONTH_NAME]
,MONTH(CONCAT(1,[MONTH_NAME],0)) AS [MONTH_NUMBER]
FROM tbl_MonthName
GO
--OUTPUT

monthnametono1.3

Conclusion :
In both methods, we used different approaches using manual concatenation and automatic concatenation using CONCAT function. I would recommend Method 2 because in my earlier article I found that  CONCAT function is much faster than the normal concatenations.

Read Full Post »

‘Dynamically order by’ clause of any query result set is an old topic and has been discussed on multiple forums. Mostly the solution is being achieved using CASE statement. But what encourages me to write this article is that we can achieve the same with shorter code using  IIF CHOOSE logical functions shipped with SQL Server 2012.

Method 1 :

In this method we will use the traditional approach and dynamically order by the query result set using CASE statement. Given below is the script.

USE AdventureWorks2012
GO
DECLARE @ORDERBY INT
SET @ORDERBY=3

SELECT
BusinessEntityID
, FirstName
, LastName
, JobTitle
FROM HumanResources.vEmployee
ORDER BY
CASE
WHEN @ORDERBY=1 THEN FirstName
WHEN @ORDERBY=2 THEN LastName
WHEN @ORDERBY=3 THEN JobTitle
END
GO
--OUTPUT

dynamicorderby1.1

Method 2 :
In this method we will use a NEW approach and dynamically order by the query result set using IIF logical function shipped in SQL Server 2012. Given below is the script.

USE AdventureWorks2012
GO
DECLARE @ORDERBY INT
SET @ORDERBY=3

SELECT
BusinessEntityID
, FirstName
, LastName
, JobTitle
FROM HumanResources.vEmployee
ORDER BY
IIF(@ORDERBY=1,FirstName,IIF(@ORDERBY=2,LastName,JobTitle))
--OUTPUT

dynamicorderby1.1

Method 3 :
In this method we will use yet another NEW approach and dynamically order by the query result set using CHOOSE logical function shipped in SQL Server 2012. Given below is the script.

USE AdventureWorks2012
GO
DECLARE @ORDERBY INT
SET @ORDERBY=3

SELECT
BusinessEntityID
, FirstName
, LastName
, JobTitle
FROM HumanResources.vEmployee
ORDER BY
CHOOSE(@ORDERBY,FirstName,LastName,JobTitle)
GO
--OUTPUT

dynamicorderby1.1

Conclusion :
In all of the above methods we used different methods, functions, approaches but the output will remain same and also it will not impact performance. It just reduces the line of code.

Read Full Post »

I came across this Default FILESTREAM filegroup is not available in database ‘%.*ls’ error today, while I was working on a table using filestream. This error message is part of SQL Server since 2005 version.

Let me explain this error in detail :

Message Number: 1969

Severity : 16

Error Message: Default FILESTREAM filegroup is not available in database ‘%.*ls’.

Error Generation:

Let me create a database and table to demonstrate this error message.

--Create a database
USE master ;
GO
CREATE DATABASE Musicdb
ON
(NAME = Musicdb_dat,
FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\DATA\Musicdbdat.mdf')
LOG ON
( NAME = Musicdb_log,
FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\DATA\Musicdblog.ldf') ;
GO

--Create a table
USE Musicdb
GO
CREATE TABLE tbl_music
(
[ID] uniqueidentifier not null rowguidcol
constraint PK_music primary key clustered,
[Music_files] varbinary(max) filestream
)
--OUTPUT

Msg 1969, Level 16, State 1, Line 1
Default FILESTREAM filegroup is not available in database ‘Musicdb’.

Ooopps…… I am unable to create the table. Lets fix it.

Resolution:

If you look at the error, it says that there is no default filestream in the filegroup but there is a possibility that even filestream is not available in the filegroup. Lets now resolve this issue step by step.

Step 1 :
In this step, lets check in the Musicdb database if filestream is available in the filegroup or NOT.

SELECT database_id,type_desc,name,physical_name
FROM sys.master_files WHERE database_id=DB_ID('Musicdb')
--OUTPUT

Errormsg1969.1.1

Step 2 :
In step 1, you can see that not a single filestream is available in the filegroup. So first lets create a filestream in filegroup. Given below is the script.
Note : if filestream is already available in the filegroup then do not proceed with Step 2 . Go to Step 3 directly.

USE [master]
GO
ALTER DATABASE Musicdb
ADD FILEGROUP [MyMusicAlbum]
CONTAINS FILESTREAM
GO
ALTER DATABASE Musicdb
ADD FILE
(
NAME = N'Album01',
FILENAME = N'C:\music\Album01.ndf'
)
TO FILEGROUP [MyMusicAlbum]
GO

Step 3 :
In step 1 if filestream is available in filegroup then without going to step 2, just execute the given below script and it will make your filestream as a default filestream in the filegorup.

USE Musicdb
GO
IF NOT EXISTS (SELECT name FROM sys.filegroups WHERE is_default=1 AND name = N'MyMusicAlbum')
ALTER DATABASE Musicdb MODIFY FILEGROUP [MyMusicAlbum] DEFAULT
GO

Step 4 :
Lets create the table again.

--Create a table
USE Musicdb
GO
CREATE TABLE tbl_music
(
[ID] uniqueidentifier not null rowguidcol
constraint PK_music primary key clustered,
[Music_files] varbinary(max) filestream
)
--OUTPUT

Command(s) completed successfully.

Conclusion :
Remember, whenever you come across this error, you just need to either create a filestream in a filegroup or make the existing filestream as a default in filegroup.

Read Full Post »

In my earlier article, I implemented a lot of excel financial functions including PMT function using CLR  and I found CLR implementation is the effective way to implement excel functions in SQL Server. However you can develop the same functionality in SQL using User Defined Function as well.

Given below is the script of PMT financial function in SQL Server with examples. Basically PMT function not only calculates the future value of any investment on an interest rate, it also calculates the payments for a loan.

CREATE FUNCTION UDF_PMT
(@InterestRate  NUMERIC(18,8), --Rate is the interest rate per period.
@Nper          INT,           --Nper is the total number of payment
--periods in an annuity.
@Pv            NUMERIC(18,4), --Pv is the present value, or the
--lump-sum amount that a series of
--future payments is worth right now.
--If pv is omitted, it is assumed to be
--0 (zero). PV must be entered as a
--negative number.
@Fv            NUMERIC(18,4), --Fv is the future value, or the
--lump-sum amount that a series of
--future payments is worth right now.
--If pv is omitted, it is assumed to
--be 0 (zero). PV must be entered as a
--negative number.
@Type	         BIT            --Type is the number 0 or 1 and
--indicates when payments are due.
--If type is omitted, it is assumed
--to be 0 which represents at the end
--of the period.
--If payments are due at the beginning
--of the period, type should be 1.
)
RETURNS NUMERIC(18,2) --float
AS
BEGIN
DECLARE  @Value NUMERIC(18,2)
SELECT @Value = Case
WHEN @Type=0
THEN Convert(float,@InterestRate / 100)
/(Power(Convert(float,(1 + @InterestRate / 100)),@Nper)-1)
* -(@Pv*Power(Convert(float,(1 + @InterestRate / 100)),@Nper)
+@Fv)

WHEN @Type=1
THEN Convert(float,@InterestRate / 100) /
(Power(Convert(float,(1 + @InterestRate / 100)),@Nper)-1)
* -(@Pv*Power(Convert(float,(1 + @InterestRate / 100)),@Nper)
+@Fv)
/(1 + Convert(float,(@InterestRate / 100)))

END
RETURN @Value
END
GO
SELECT dbo.UDF_PMT(0.625,24,5000,0,0)
--OUTPUT in SQL
--($225.00)
--Equivalent function in excel
--=PMT(7.5%/12, 2*12, 5000, 0, 0)
--OUTPUT in Excel
--($225.00)
GO
SELECT dbo.UDF_PMT(0.11538461,208,8000,0,1)
--OUTPUT in SQL
--($43.23)
--Equivalent function in excel
--=PMT(6%/52, 4*52, 8000, 0, 0)
--OUTPUT in Excel
--($43.23)
GO
SELECT dbo.UDF_PMT(5.25,10,6500,0,0)
--OUTPUT in SQL
--($852.03)
--Equivalent function in excel
--=PMT(5.25%/1, 10*1, 6500, 0, 0)
--OUTPUT in Excel
--($852.03)
GO
SELECT dbo.UDF_PMT(0.666666667,36,5000,-1000,1)
--OUTPUT in SQL
--($131.14)
--Equivalent function in excel
--=PMT(8%/12, 3*12, 5000, -1000, 0)
--OUTPUT in Excel
--($131.14)
GO

Appreciate your valuable feedback about this function.

Reference : Castle
Techonthenet

Read Full Post »

SQL Server 2012 came up with a lot of new enhancements, breaking changes & behavioral changes. In my earlier article, I had discussed about one of the nice behavior changes in exists function. Today, we will discuss another behavioral change in Sqlcmd Utility when XML mode is on. This utility is one of the handy utilities when you wish to execute transact SQL or any other SQL operations on command prompt.

Given below are sqlcmd utility behavioral changes in earlier version SQL Server vs 2012.

S.No

Behavior in Earlier versions

Behavior in SQL Server 2012

1

If any string data having a single quote will be replaced with the & apos; escape sequence, it will not remain a valid XML and an XML parser will not give the same result.

If any string data having a single quote will not be replaced with & apos; escape sequence, it will remain a valid XML and an XML parser will give the same result.

2

If any column of a table having money data values with no decimal value, it will be converted to integer.

If any column of a table having money data values with no decimal value, it will show the 4 decimal digits.

Let me create an example to demonstrate this behavior changes.

--Create table
CREATE TABLE [dbo].[tbl_Employee]
(
[EmployeeID] [int] IDENTITY(1,1) NOT NULL,
[First Name] [nvarchar](50) NULL,
[Last Name] [nvarchar](50) NULL,
[Salary] [money] NOT NULL
)
GO
--Insert record into table
INSERT INTO dbo.tbl_Employee ([First Name],[Last Name],[Salary])
VALUES ('Reuben','D''sa',5000)
GO
--Browse table
SELECT [Last Name],[Salary] FROM dbo.tbl_Employee
--OUTPUT

Bchange1.1

Lets execute the above query in sqlcmd utility with XML mode on as shown in the given below picture.
Bchange1.3

Reference : MSDN

Read Full Post »

Bit (Boolean) data type keeps result in 1 or 0 form in the SQL Server database. But most of the time we need to display 0 as FALSE and 1 as TRUE in front end applications. Given below is the shortest possible solution using IIF function in SQL Server 2012.

You can also convert it using CASE statement but the syntax will be longer.

Given below are the two methods you can use to convert Boolean(bit) data type into string.

Method 1:
In this method, we will use IIF function to convert boolean(bit) to string. IIF function is a new logical function shipped with SQL Server 2012.

--This script is compatible with SQL Server 2012 and above.
USE AdventureWorks2012
GO
SELECT
[BusinessEntityID]
, [Name]
, [ActiveFlag]
, IIF([ActiveFlag]=1,'TRUE','FALSE') AS [ActiveFlag As String]
FROM [Purchasing].[Vendor]
GO
--OUTPUT

boolean_to_string1.1

Method 2:
In this method, we will use CASE statement to convert boolean(bit) to string.

--This script is compatible with SQL Server 2005 and above.
USE AdventureWorks2012
GO
SELECT
[BusinessEntityID]
, [Name]
, [ActiveFlag]
, CASE WHEN [ActiveFlag]=1 THEN 'TRUE'
WHEN [ActiveFlag]=0 THEN 'FALSE'
END AS  [ActiveFlag As String]
FROM [Purchasing].[Vendor]
GO
--OUTPUT

boolean_to_string1.2

Conclusion :
The only difference between the above two methods is the Syntax. Apart from this, each and every activity, including the performance, will be the same. As per MSDN, IIF is a shorthand form for writing a CASE expression.

Read Full Post »

In my earlier article, I wrote about the new feature of SQL Server 2012 namely Insert Snippet. This is quite a handy feature which can save a lot of your time. This feature is shipped with a lot of standard objects snippet with the customization options.

But there were two big questions in front of me. 1) Why do we need to customize it? 2) How can this save our time ?
Answer :The reason for customization is that, each and every company follows some standards to create standard object (Table, view etc.) of SQL Server. For example the name of the table should start with tbl_ or there might be some additional fixed column with some default values in each table etc etc. Therefore, once you customize the snippet as per your requirements, then you can use it every time.

Prerequisite: How to insert the Snippet in SQL Server 2012.

Solution :
Let me explain, how to customize the standard object table snippet Step by Step :

Step 1 :

First of all, I will take you to the path where snippet of the standard objects resides in the SQL Server 2012.

To find the path you need to go to Tools and select Code Snippets Manager as shown in the picture below.

customizesnippet1.1

Once you open the Code Snippets Manager, select the appropriate object and you can see the path of that particular object as shown in the picture below.
customizesnippet1.2

Step 2 :
As you can see the path of table snippet in the above picture, the next step is to browse the table path and find the standard table snippet namely Create Table.snippet.

customizesnippet1.3.1

Step 3 :

Now, you are in the standard table snippet folder,  so you need to make a copy of the standard table object namely “Create Table.snippet” and rename it to “Create Sample Table.snippet”  as shown in the picture below.

customizesnippet1.3.2

Step 4 :

Open the newly created file Create Sample Table.snippet, first and foremost, change the title as highlighted in the given below picture.

customizesnippet1.4

Step 5 :

Once you change the title copy the selected area as shown in the picture below and paste twice after <Literal> because we need to add two additional columns of the table in the snippet.

customizesnippet1.5

Step 6 :

Lets rename the column name and data type of the columns ([Creation_Datetime] & [Created_By] having data type datetime and nvarchar(50) respectively as shown in the picture below.

 customizesnippet1.6

Step 7 :

Once you create the additional column, do not close the file, just scroll down a little bit in the same file and copy the selected area ($Column2$ $datatpe2$) and paste it twice and then rename it to $column3$ & 4$ and $datatype 3$ & 4$ respectively as shown in the picture below.

customizesnippet1.7

Step 8 :

Once all the changes are done in Create Sample Table.snippet save and close the file and open SQL Server Management Studio.

Step 9 :

Lets insert the customized table snippet in Query window as shown below.

customizesnippet1.8

customizesnippet1.9

  

Read Full Post »

In my earlier article, I discussed that if you need to use result set with any stored procedure then you cannot insert the result set into a table. It generates the error no 102,  Incorrect syntax near ‘SETS’. After I posted this article, I kept looking for the solution, because it is a very handy feature of SQL Server 2012. At last, I found the solution. The details of this feature are available here.

Let me discuss the Solution step by step.

Step 1 :
In Step 1, we need to create a table and stored procedure to explain the problem and solution.

USE AdventureWorks2012
GO
CREATE TABLE #temp
(
[DepartmentID] int,
[Name] varchar(50)
)
GO

CREATE PROCEDURE SP_ResultSet_Example1
AS
SELECT [DepartmentID]
,[Name]
FROM [HumanResources].[Department]
ORDER BY DepartmentID
GO

Step 2 :
In Step 2, I will show you the error if you try to execute the stored procedure (SP_ResultSet_Example1) using result set and insert the result set into the table (#temp).
Given below is the script.

USE AdventureWorks2012
GO
INSERT INTO #temp
EXEC SP_ResultSet_Example1
WITH RESULT SETS
(
([Department ID] INT NOT NULL,
[Department name] NAME NOT NULL));
GO
--OUTPUT

Msg 102, Level 15, State 1, Line 3
Incorrect syntax near ‘SETS’.

Ooopps…… I am unable to execute it.

Step 3 :
In Step 3, I will try to execute the stored procedure (SP_ResultSet_Example1) using result set and insert the result set into the table (#temp) successfully.
Given below is the script.

USE AdventureWorks2012
GO
DECLARE @Sql AS NVARCHAR(200)
SET @Sql='
EXEC SP_ResultSet_Example1
WITH RESULT SETS
(
([Department ID] INT NOT NULL,
[Department name] NAME NOT NULL));'

INSERT INTO #temp EXEC (@Sql)
GO
SELECT * FROM #temp
GO
--OUTPUT

insertintowithresultset1.1

Read Full Post »

In my earlier articles, I wrote about new enhancement of FORCESEEK table hint. It is a very handy feature when you have more than one index on the table and you want to select not only the index but also the column name of the index in FORCESEEK table hint. But you should be very careful about the syntax and parameters of this table hint. Recently, I came across a new error message related to FORCESEEK table hint.

Let me explain this error in detail :

Message Number: 365

Severity : 16

Error Message: The query processor could not produce a query plan because the FORCESEEK hint on table or view ‘%.*ls’ specified more seek columns than the number of key columns in index ‘%.*ls’.

Error Generation:

Let me create a NONCLUSTERED INDEX to demonstrate this error.

USE [AdventureWorks2012]
GO
CREATE NONCLUSTERED INDEX [IX_PurchaseOrderDetail_Sample]
ON Purchasing.[PurchaseOrderDetail]
([PurchaseOrderID]
,[ProductID] ASC)
WITH (DROP_EXISTING = OFF) ON [PRIMARY]

Once you create the index, lets write a query using the above index and forceseek table hint.

USE [AdventureWorks2012]
GO
SELECT * FROM Purchasing.PurchaseOrderHeader AS H
INNER JOIN Purchasing.PurchaseOrderDetail AS D
WITH (FORCESEEK
(IX_PurchaseOrderDetail_Sample
(PurchaseOrderID
,ProductID
,OrderQty
)))
ON H.PurchaseOrderID =D.PurchaseOrderID
WHERE
D.ProductID>100
AND D.PurchaseOrderID>10
AND D.OrderQty>0

Msg 365, Level 16, State 1, Line 1
The query processor could not produce a query plan because the FORCESEEK hint on table or view ‘PurchaseOrderDetail’ specified more seek columns than the number of key columns in index ‘IX_PurchaseOrderDetail_Sample’.

Ooopps…… I am unable to execute it.

Resolution:

If you look at the above query, you will realize that there are two key columns ([PurchaseOrderID], [ProductID]) available in IX_PurchaseOrderDetail_Sample index. But what you are trying to do in the above query is that you included an additional column [OrderQty] that is not part of the index.

Lets remove the Order Qty (line # 7) column from the FORCESEEK table hint and execute the query again.

USE [AdventureWorks2012]
GO
SELECT * FROM Purchasing.PurchaseOrderHeader AS H
INNER JOIN Purchasing.PurchaseOrderDetail AS D
WITH (FORCESEEK
(IX_PurchaseOrderDetail_Sample
(PurchaseOrderID
,ProductID

)))
ON H.PurchaseOrderID =D.PurchaseOrderID
WHERE
D.ProductID>100
AND D.PurchaseOrderID>10
AND D.OrderQty>0
--OUTPUT

erromessage365.1.1

Conclusion :

Remember, whenever you use FORCESEEK hint on a table and define the index name and column name as well, the column name must be part of the Index key columns.

Read Full Post »

« Newer Posts - Older Posts »