Showing posts with label T-SQL. Show all posts
Showing posts with label T-SQL. Show all posts

Sunday, January 23, 2011

System.Data.SqlClient.SqlError -When Restoring a Database to SQL Server 2008

Today my friend tried to restore a database backup to SQL Server 2008 and he got the following error


Msg 3176, Level 16, State 1, Line 1 File 'D:\SQL Server 2008 DBs\test01.mdf' is claimed by 'SCTA_ORG_SECOND'(3) and 'SCTA_ORGANIZATION'(1). The WITH MOVE clause can be used to relocate one or more files.
Msg 3013, Level 16, State 1, Line 1
RESTORE DATABASE is terminating abnormally

let's resolve this error then we will explain the reason of this error so at the same screen used for restoring database of Microsoft SQL Server Management Studio 




select Script Action to New Query Window as the above image so you will get the following T-SQL and here we will know the reason


RESTORE DATABASE [test01] FROM  DISK = N'D:\SCTA_Org2.bak' WITH  FILE = 1,
    MOVE N'SCTA_ORGANIZATION' TO N'D:\SQL Server 2008 DBs\test01.mdf',
    MOVE N'SCTA_ORG_SECOND' TO N'D:\SQL Server 2008 DBs\test01.MDF',
    MOVE N'SCTA_ORGANIZATION_log' TO N'D:\SQL Server 2008 DBs\test01_1.ldf',
    NOUNLOAD,  STATS = 10
GO

so you will notice there are two files of mdf with the same name so just change the name of second one to test02 or to test01.ndf ( different extension) then run the command and it's successfully restored.

so the logical answer for this error first test01.mdf is a primary data file and the second is the secondary data file but the extension and name are same so that way you have to change the name or extension of second file with any other name or extension.

Note: the extension is anything ( it can be .fad or .ndf but .ndf is best practice to determine what this file for for example .ldf for log file , .ndf  for secondary data files ..).

Finally : I think the original database backup come from SQL Server 2000 and maybe this behavior allowed in SQL Server 2000 or the name is a case sensitive ( test01.mdf not like test01.MDF).

Thursday, July 22, 2010

Regular expressions with SQL Server

All of us know the importance of using regular expressions to filter your results or check some specific rules if it exists or not for example to check if the inserted email in the correct format or to check if the inserted string has numbers or not ..Etc and of course you can use regular expressions in many ways with Microsoft technologies especially with SQL Server for example you can write CLR function or write it in your T-SQL code.

I will show you some examples of using Regular expressions with Arabic language in SQL Server:

--In this example we want to exclude from the results
--any Name has any charts excpet the arabic charts
DECLARE @Students TABLE (Name nvarchar(20))

INSERT @Students VALUES
(N'فادي'),
(N'أحمد ! فادي'),
(N'فادي أحمد عبدالوهابabc')

SELECT Name FROM @Students 
            WHERE Name Not Like N'%[^أ-ي]%'


And the result of the above query as the following:
Name

------------------------------
فادي

So the above result only returns the first inserted row because it's only the Name has Arabic charts only.


Note: PatIndex function Returns the starting position of the first occurrence of a pattern in a specified expression

--In this example we will see the power or PatIndex
--so we want to extract the arabic charts only
--from a sentense has arabic and english word
DECLARE @NAME nvarchar(50)
SET @NAME = N'Fadi فادي أحمد عبدالوهاب'

SELECT Substring(@Name,PATINDEX(N'%[أ-ي]%',@NAME),5000)


And the result of the above query as the following :
--------------------------------------------------
فادي أحمد عبدالوهاب

And the last example in .net code by using CLR fucntion

using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;

public partial class UserDefinedFunctions
{
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlString fnVerfiyIsContainsArabic(string input)
    {
        //First Define an Object from Regex and pass your Pattern
        System.Text.RegularExpressions.Regex regObj = 
  new System.Text.RegularExpressions.Regex("[^أ-ي]");

        //Second check if the input text Is match the pattern or not
        bool flag = regObj.IsMatch(input);
        if (flag)
            //if it's matching print the following text 
            return "It's not only Contains Arabic Charts :" + input;
        else
            //if it's not matching print the following text
            return "It's only Contains Arabic Charts :" + input;
    }
};


Then run the following queries:


I hope you get some benefits from reading this article and good luck.

Monday, May 31, 2010

Using ALL, Some and Any with Sub Queries

In case a sub Query return a single value , here you can write a normal query without using comparison operators like the following an example:

DECLARE @Employees Table (Id int , Name varchar(50) , Salary int)
INSERT INTO @Employees 
SELECT 1 ,'Fadi'  ,  10000  UNION ALL 
SELECT 2 ,'Shadi' ,  20000  UNION ALL 
SELECT 3 ,'Tareq' ,  30000  UNION ALL 
SELECT 4 ,'Rola'  ,  40000  UNION ALL 
SELECT 5 ,'Rana'  ,  50000  UNION ALL 
SELECT 6 ,'Rasha' ,  60000

-- we will use this query as sub Query return a single value
-- it's return the Average of Salary
SELECT AVG(Salary) FROM @Employees

SELECT * FROM @Employees
WHERE Salary > (SELECT AVG(Salary) FROM @Employees)


 The result of the above query:

35000


Id          Name                                               Salary
----------- -------------------------------------------------- -----------
4           Rola                                               40000
5           Rana                                               50000
6           Rasha                                              60000


What if your Sub Query returns a list of values, in this case you will need to use the comparison operators to compare each value return by a sub query so to simplify these queries let's see the benefits of ALL , Any and Some.

Let's start by ALL by this example:

-- we will use this query as sub Query 
SELECT Salary FROM @Employees WHERE Salary < 40000

SELECT * FROM @Employees
WHERE  Salary > ALL 
(SELECT Salary FROM @Employees WHERE Salary < 40000)
Salary
-----------
10000
20000
30000


Id          Name                                               Salary
----------- -------------------------------------------------- -----------
4           Rola                                               40000
5           Rana                                               50000
6           Rasha                                              60000


ALL keyword equal to AND comparison operator so it means the values return by a Query must be Salary > 30000 and Salary > 20000 and Salary > 10000.

Note: if the sub query return no rows, a comparison always true so means it will return all rows.

--we will use this query as sub Query 
-- this query will return no rows
SELECT Salary FROM @Employees WHERE Salary > 80000

SELECT * FROM @Employees
WHERE  Salary > ALL (SELECT Salary FROM @Employees WHERE Salary > 80000)
Salary
-----------


Id          Name                                               Salary
----------- -------------------------------------------------- -----------
1           Fadi                                               10000
2           Shadi                                              20000
3           Tareq                                              30000
4           Rola                                               40000
5           Rana                                               50000
6           Rasha                                              60000


ANY key word equal to OR comparison operator so it means the values return by a Query must be greater than one on any values 30000, 20000 and 10000.

Note: SOME is equal to ANY but SOME (ANSI Standard keyword).

--any one condition true return the value
SELECT * FROM @Employees
WHERE  Salary > ANY (SELECT Salary FROM @Employees WHERE Salary < 40000)

--any one condition true return the value
SELECT * FROM @Employees
WHERE  Salary > SOME (SELECT Salary FROM @Employees WHERE Salary < 40000)
Id          Name                                               Salary
----------- -------------------------------------------------- -----------
2           Shadi                                              20000
3           Tareq                                              30000
4           Rola                                               40000
5           Rana                                               50000
6           Rasha                                              60000


Note: if the sub query return no rows or null value, a comparison always false so means it will return nothing.

SELECT * FROM @Employees
WHERE  Salary > ANY (SELECT Salary FROM @Employees WHERE Salary > 80000)
Id          Name                                               Salary
----------- -------------------------------------------------- -----------

Friday, May 14, 2010

COALESCE and ISNULL functions

Both of these functions are similar which allow you to replace a null value of column with any expression belong to the same data type of this column except in one point, COALESCE has ability in case you need to pass a list of expressions to be replaced when the value comes NULL.

Let's see these examples:
DECLARE @Table Table
        (Col1 VARCHAR(10) NULL ,
         Col2 INT NULL,
         Col3 INT NULL)

INSERT INTO @Table
SELECT  'Microsoft',1,100 UNION ALL
SELECT  NULL,2,200        UNION ALL
SELECT  NULL,NULL,300     UNION ALL
SELECT  NULL,NULL,NULL 

--Both Functions are similar In Case of One Expression 
SELECT Col1 , ISNULL(Col1,'No Value') AS [Col1 with ISNULL] 
FROM @Table
SELECT Col1 , COALESCE(Col1,'No Value') AS [Col1 with COALESCE] 
FROM @Table

--COALESCE allows you to pass a list of expressions.
--When first Expression (in this case Col2 ) is null then 
--it will take the next expression (in this case Col3) and 
--replaced with Null value and so on.
SELECT Col1 , COALESCE(Col1,CAST(Col2 AS VARCHAR(10)),
                            CAST(Col3 AS VARCHAR(10))) AS [List of expressions wiht COALESCE] FROM @Table


Monday, April 26, 2010

A SELECT statement with TOP clause options

TOP clause can limits the numbers of rows return by the SELECT query .There are three options used with TOP clause in the SELECT Query:

1. TOP with number of rows
2. TOP with percentage of rows
3. TOP with ties

So let's see these examples to demonstrate the options:

USE AdventureWorks
GO

-- To return only 2 rows from the result set 
SELECT TOP 2 Name , ProductNumber ,ListPrice
FROM Production.Product
ORDER BY ListPrice

--TO return only the 2% from the result set 
SELECT TOP 2 PERCENT Name , ProductNumber ,ListPrice
FROM Production.Product
ORDER BY ListPrice

-- TO return only 2 rows from the result set 
-- plus : extra rows will returned if their values
-- (Based on ORDER BY column) match the vlaue of last row
SELECT TOP 2 WITH TIES Name , ProductNumber ,ListPrice
FROM Production.Product
ORDER BY ListPrice DESC


Monday, April 19, 2010

SQL Server - Case Sensivity and Accent sensivity determined by a Collation

Sql server 2005/2008 support Unicode characters so you can present your data in any language so for example if your Database collation is (a Latin) and you want to store Arabic language words you need to define a Unicode data type like(nchar,nvarchar,ntext) to stored your data properly and to avoid the question marks problem (???) except if your database collation is Arabic and in this case you does not need to define your columns as a Unicode type you can use the type like(char,varchar,text) to store Arabic language because in this case the collation is responsible to display your character language.
Note:
SQL Server 2005/2008 allows you to make collation choices at the server, database, column, and expression level.
Case Sensivity and accent sensivity determined by a Collation so let's see how we can define the case sensitive and accent sensitive by using collation for two main languages Arabic and English.

--This example show the Case sensitive for English Language
DECLARE @Name1 varchar(25) ='SQL SERVER' 
       ,@Name2 varchar(25) ='sql server' 

--Here do not consider the Case sensitive        
IF(@Name1 COLLATE SQL_Latin1_General_Cp1_CI_AS = @Name2 COLLATE SQL_Latin1_General_Cp1_CI_AS)
    RAISERROR('They are %s',10,1,'the same')
ELSE
    RAISERROR('They are %s',10,0,'not the same')

--Here do consider the Case sensitive
IF(@Name1 COLLATE SQL_Latin1_General_Cp1_CS_AS = @Name2 COLLATE SQL_Latin1_General_Cp1_CS_AS)
    RAISERROR('They are %s',10,1,'the same')
ELSE
    RAISERROR('They are %s',10,0,'not the same')

the result of the above script as follwoing:
They are the same
They are not the same

--This example show the Accent Sensitive for Arabic language
DECLARE @Name3 nvarchar(25) ='أ' 
       ,@Name4 nvarchar(25) ='آ' -- try to change it to ؤ – ئ – إ –ء-

--Here do not consider the Accent Sensitivity       
IF(@Name3 COLLATE Arabic_CS_AI= @Name4 COLLATE Arabic_CS_AI)
    RAISERROR('They are %s',10,1,'the same')
ELSE
    RAISERROR('They are %s',10,0,'not the same')

--Here do consider the Accent Sensitivity
IF(@Name3 COLLATE Arabic_CS_AS= @Name4 COLLATE Arabic_CS_AS)
    RAISERROR('They are %s',10,1,'the same')
ELSE
    RAISERROR('They are %s',10,0,'not the same')


the result of the above script as follwoing:
They are the same
They are not the same

SQL Server and short tips – 3

  • Sql server Express 2005/2008 utilizes max 1 GB for CPU as well as for Ram and 4 GB for max database size.
  • statistics about SQL Server 2008 :
    • Max Data storage per database (524272 TB)
    • Max Concurrent User Connection (32767)
    • Max Databases per server (32767)
    • Max tables per database (2147483647)
    • Max columns per table (1024)
  • To hide the system database in Object Explorer: SSMS >Tools>Options>Hide system objects in Object Explorer then restart Sql Server.
  • Keep in your administrative tools, Activity Monitor can offer to you the Recent Expensive queries.
  • To see how many pages ,extents and the extent Switches in your database tables use the following command: DBCC SHOWCONTIG
  • Case sensitivity in SQL Server:



--SQL Server 2005/2008 allows you to make collation choices 
--at the server, database, column, and expression levels
--Case Sensivity determined by Collation
--Oracle, by default is case sensitive whereas 
--SQL Server installations by default are case-insensitive

DECLARE @Name1 varchar(25) ='SQL SERVER' 
       ,@Name2 varchar(25) ='sql server' 

--Here do not consider the Case Sensitivity       
IF(@Name1 COLLATE SQL_Latin1_General_Cp1_CI_AS= @Name2 COLLATE SQL_Latin1_General_Cp1_CI_AS)
    RAISERROR('They are %s',10,1,'the same')
ELSE
    RAISERROR('They are %s',10,0,'not the same')

--Here do consider the Case Sensitivity
IF(@Name1 COLLATE SQL_Latin1_General_Cp1_CS_AS= @Name2 COLLATE SQL_Latin1_General_Cp1_CS_AS)
    RAISERROR('They are %s',10,1,'the same')
ELSE
    RAISERROR('They are %s',10,0,'not the same')   
-------------------------------------------------------------------- 
DECLARE @Name3 nvarchar(25) ='أ' 
       ,@Name4 nvarchar(25) ='إ' --try to change it to ء – آ - ؤ -ئ

--Here do not consider the Accent Sensitivity       
IF(@Name3 COLLATE Arabic_CI_AI= @Name4 COLLATE Arabic_CI_AI)
    RAISERROR('They are %s',10,1,'the same')
ELSE
    RAISERROR('They are %s',10,0,'not the same')

--Here do consider the Accent Sensitivity
IF(@Name3 COLLATE Arabic_CS_AS= @Name4 COLLATE Arabic_CS_AS)
    RAISERROR('They are %s',10,1,'the same')
ELSE
    RAISERROR('They are %s',10,0,'not the same')

Tuesday, April 6, 2010

Cannot resolve collation conflict for column 1 in SELECT statement

My friend has faced this problem against some queries so the cause of this problem as described in above title related to the Collation.

He has a table with columns with different collation at Column level for example a Table called "Students" with "FirstName" and "LastName" columns and each of these columns has a different collation so the "FirstName with SQL_Latin1_General_CP1_CI_AI collation " and "LastName with Arabic collation" and when he write the following query he got an error:

SELECT FirstName + ' ' +LastName
FROM dbo.Students


The solution for this problem is easy so rewrite the above query as following:

SELECT FirstName + ' ' + LastName  COLLATE SQL_Latin1_General_CP1_CI_AI LastName
FROM dbo.Students 


As well as if you try to write a query as the following:

SELECT * FROM dbo.Students 
WHERE FirstName = LastName


You will get the following error:

Msg 468, Level 16, State 9, Line 1
Cannot resolve the collation conflict between "Arabic_CI_AS" and "SQL_Latin1_General_CP1_CI_AI" in the equal to operation

To fix it, rewrite the above query as following:

SELECT * FROM dbo.Students 
WHERE FirstName = LastName COLLATE SQL_Latin1_General_CP1_CI_AI

Monday, March 15, 2010

SQL Server and Arabic language

I have read some articles written about how this products Office 2010 by default support not only Hijri Date but also UmAlQura Date and this good news.

This article show you some points related to Arabic language (which is my mother tongue) with Sql Server.

In general Sql server deals with any language in the same way except some issues become different from language to other language.
So let's begin by Collation and see how Sql Server stored data and retrieve data in Arabic language.

What is a Collation?

A collation determines the rules SQL Server uses to compare and sort character data and determines which characters can be stored in non-Unicode character data types. Collation can be specified at the instance level, database, column level or clause level.

So let's begin by this example to make the points clear:

CREATE TABLE [dbo].[Test](
--here stored data as ASCII char
    [Name_Char_Latain] [char](10) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
--here stored data as Unicode
    [Name_nChar_Latain] [nchar](10) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
--here stored data as ASCII char
    [Name_Char_Arabic] [char](10) COLLATE Arabic_CI_AI NULL,
--here stored data as Unicode
    [Name_nChar_Arabic] [nchar](10) COLLATE Arabic_CI_AI NULL
) ON [PRIMARY]

GO


If your database collation is SQL_Latin1_General_CP1_CI_AS and you tried to insert Arabic data you will get ??? Characters so there are two solutions for these problems: Change database collation or use N' '

You can change database collation as following:

USE [master]
GO
ALTER DATABASE [TestDb] COLLATE Arabic_100_CI_AI
GO


Or you can write your statement like this

INSERT dbo.test VALUES (N'بدر',N'بدر',N'بدر',N'بدر');

After we created the table then inserts these records

INSERT dbo.test VALUES ('بدر','بدر','بدر','بدر');
INSERT dbo.test VALUES ('احمد','احمد','احمد','احمد');
INSERT dbo.test VALUES ('أحمد','أحمد','أحمد','أحمد');
INSERT dbo.test VALUES ('أيات','أيات','أيات','أيات');


Then run these commands:

USE TestDb
GO

SELECT * FROM dbo.test ORDER BY Name_Char_Latain
SELECT * FROM dbo.test ORDER BY Name_nChar_Latain
SELECT * FROM dbo.test ORDER BY Name_Char_Arabic
SELECT * FROM dbo.test ORDER BY Name_nChar_Arab


And you will get result set as the below:

Now what about the hijri date and UmAlQura date?

By default Sql server support Hijri date format and you can do it by using CONVERT function so let's see these examples:

SELECT CONVERT(char(40),GETDATE(),130)
SELECT CONVERT(char(40),GETDATE(),131)


The result set:


29 ربيع الاول 1431 12:53:11:130AM

29/03/1431 12:53:11:130AM

So Sql Server does a great effort to display the date as Hijri but what about the UmAlQura date.

You need to write custom code using .NET CLR to implement it in SQL Server and this great feature introduced with version of SQL Server 2005 to enable you to write .Net code and by this approach we see new functions and data types like HIERARCHYID.

Let's see how we can Convert Gregorian Date to UmAlQura Date by using .Net UDF:

1. First open VS 2005/2008/2010 and create CLR database project

2. Choose a database connection for your reference

3. Right click on your project and Add New item.. and select UDF

4. Add reference using System.Globalization;

5. The complete code:

using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Globalization;

public partial class UserDefinedFunctions
{
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlString UmAlQuraConverter(DateTime date, string format)
    {
        return new SqlString(getUmAlQuraDate(date, format));
    }

    static string getUmAlQuraDate(DateTime date, string format)
    {
        UmAlQuraCalendar umAlQuraCal = new UmAlQuraCalendar();
        CultureInfo umAlQuraCulture = new CultureInfo("ar-SA");
        umAlQuraCulture.DateTimeFormat.Calendar = umAlQuraCal;
        return date.ToString(format, umAlQuraCulture);
    }
};


Then choose deploy solution

Then run this command to enable using CLR in your SQL server instance

EXEC sp_configure 'clr enabled', 1;
RECONFIGURE;


Then run this command to get UmAlQura Date

SELECT dbo.UmAlQuraConverter(GETDATE(),'dd/MM/yyyy')

And you will get result as UmAlQura Date.


Note:
If you use Sql server database as backend system and .net application as frontend system ,in this case leave your date column as it's (Gregorian date) and just change the formatting at design level or user interface application by using inline function to format the date or use Global file to change it at the application level so by this approach you decrease the operations of convert date at Sql server and you avoid to have multiple columns or create custom data type or use custom functions and this make the integration easy to you because your data stored as standard or default data types.

Wednesday, March 10, 2010

Setting value to a variable by using SELECT or SET statement

You can assign value to a variable by using SET or SELECT statement and almost they work the same except that a SELECT statement has the ability to have the source value come from a column within the SELECT statement.


Let's see these examples to clarify the difference between the two statements:

USE AdventureWorks
GO

DECLARE @Res DateTime
-- Assign value to a variable by using SET statement
SET @Res = GETDATE();
SELECT @Res

SET @Res = (SELECT ModifiedDate FROM Person.Contact WHERE ContactID = 1)
SELECT @Res
-------------------------------------------------------------------------
--Just this case is show the diff between the SET and SELECT
-- It will cause an error
SET @Res = ModifiedDate FROM Person.Contact WHERE ContactID = 1
SELECT @Res
-------------------------------------------------------------------------
-------------------------------------------------------------------------
-- Assign value to a variable by using SELECT statement
SELECT @Res = GETDATE();
SELECT @Res

SELECT @Res = (SELECT ModifiedDate FROM Person.Contact WHERE ContactID = 1)
SELECT @Res
--------------------------------------------------------------------------
-- It will success
SELECT @Res = ModifiedDate FROM Person.Contact WHERE ContactID = 1
SELECT @Res
--------------------------------------------------------------------------

Monday, March 1, 2010

FOR XML modes

Why retrieve data as XML structure?

1. You need to pass the data as XML structure to others like outside applications
2. Sometime if you retrieve the data as XML structure is easier in read or describable more than if you retrieve it as ROWS AND COLUMNS like for example if you retrieve the authors and their books.

To retrieve the result set as XML structure there are four modes:

1. FOR XML RAW
2. FOR XML AUTO
3. FOR XML EXPLICIT
4. FOR XML PATH

So let's start by FOR XML RAW: It returns each row as an XML element and each column as an XML attribute.

Let's see examples:

USE AdventureWorks
GO

SELECT TOP 3 ProductID
            ,Name
            ,ProductNumber
FROM   Production.Product
FOR XML RAW


Result set of the above query:




--You can specify the name of root as well as name of elements
SELECT TOP 3 ProductID
            ,Name
            ,ProductNumber
FROM   Production.Product
FOR XML RAW('Product'),ROOT('Products')


Result set of the above query:


Note:
Simply remove the attribute if the value is NULL.So you can define you result as separate it element to show the empty value or Null.

SELECT TOP 3 ProductID
            ,Name
            ,ProductNumber
FROM   Production.Product
FOR XML RAW('Product'),ROOT('Products'),ELEMENTS


Now let's start by FOR XML AUTO: It returns result as hierarchy structure and only one level supported.
SELECT TOP 3 p.ProductID
            ,p.Name
            ,pm.Name as 'Model'
FROM   Production.Product p
INNER JOIN Production.ProductModel pm
ON p.ProductModelID = pm.ProductModelID
FOR XML AUTO,ROOT('Products')


Now let's start by FOR XML PATH: allows for the easy creation of different XML structures by simply interpreting column names specified using an XPath-like expression.

Let's see this example:

SELECT TOP 4
    ProductID AS 'ProductID'
    ,ProductNumber AS "comment()"
    ,CAST('<Break/>' AS XML) AS "node()"
FROM Production.Product p
FOR XML PATH('Product'), ROOT('Products');


Result set of the above query:

Now let's start by FOR XML EXPLICIT: provides very specific control over your XML structure.
But if you want to use it there are a set of rules to follow them to build your select statement.

Some of these rules:

1. You must include two columns in your select statement:
    a. Tag column with a numerical value for each level of the hierarchy
    b. Parent column with a numerical value based in your Tag column value
2. you must create a SELECT statement for each level of that hierarchy and use UNION ALL clauses to join those statements.
Let's see this example:

USE AdventureWorks
GO

SELECT 1 AS Tag, 
        NULL AS Parent,
        ProductID AS [Product!1!ProductID], 
        NULL AS [ProductName!2!Name!ELEMENT]
FROM Production.Product  
WHERE Name like 'a%'
UNION ALL
SELECT 2 AS Tag, 
        1 AS Parent,
        ProductID , 
        Name
FROM Production.Product  
WHERE Name like 'a%'
FOR XML EXPLICIT;


Result set of the above query:
Note:
Notice an alias to ProductID, Name column. Notice I use a very specific structure to define the alias name

XML Data type (Internally, stored using the varbinary(max))

Xml data type used to store:
1. XML fragments
2. XML documents

 XML fragment lacks the XML declaration () and does not have to have a root element .

The XML data type can be either typed or untyped. Typed simply means that an XML schema collection is assigned to the type to verify its contents.

Let's see an example to clarify the idea:

USE TestDb
GO

CREATE XML SCHEMA COLLECTION EmployeeSchema
AS
'<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="unqualified">
    <xs:element name="employee">
        <xs:complexType>
            <xs:attribute name="FullName" type="xs:string"/>
            <xs:attribute name="IdentityNo" type="xs:integer"/>
        </xs:complexType>
    </xs:element>
</xs:schema>';
GO

CREATE TABLE Employees
(
     EmpID INT IDENTITY PRIMARY KEY
     --this column called as typed because its contents need 
     --to match the schema
    ,FullName XML(DOCUMENT EmployeeSchema)
    --this column called as untyped 
    ,FullName2 XML NULL
)
GO

-- Will succeed:
INSERT Employees VALUES 
('<employee FullName="Fadi Ahmad Abdulwahab" IdentityNo="123234532"/>','<data>fadi</data>');
-- Will not succeed:
INSERT Employees VALUES 
('<employee FullName="Fadi Ahmad Abdulwahab" IdentityNo="fadi"/>','<data>fadi</data>');
GO


Last point need to take care of it:


If you need to change the schema you must first alter all columns that use the schema to instead use untyped XML. Then, you must drop the schema collection, re-create it with the added attributes, and finally alter the columns again to use the schema. And this also harms the performance of Sql server engine because it needs to convert typed data to untyped data.

Note:SQL SERVER 2008 provide you with many of methods that can help you to query or retrieve data as XML.

Saturday, February 27, 2010

Spatial Data types

Sql Server 2008 introduced new types of spatial data including the following types:
1. Geometry data type.
A data type used to store two and three dimensional data coordinates. It is used to store planar (flat-earth) data. It is generally used to store XY coordinates that represent points, lines, and polygons in a two-dimensional space
2. Geography data type.
A data type used to store ellipsoidal data, such as latitude and longitude coordinates. It is used to store ellipsoidal (round-earth) data. It is used to store latitude and longitude coordinates that represent points, lines, and polygons on the earth’s surface

So by using the above types you can store data like latitude,lognlatitude,…which can be used in conjunction with for example Microsoft Virtual Earth to provide a visual representation of your geospatial data.

These types implemented by .Net CLR data types which mean they can support various properties and methods specific to the data.

Let's see examples:

USE TestDb
GO

CREATE TABLE EmployeesAddress
(EmployeeID int IDENTITY PRIMARY KEY,
AddressName NVARCHAR(50),
Location    GEOGRAPHY)
GO

--The Point extended static geography method constructs an
--instance representing a point that includes information 
--on the longitude, latitude, and SRID.
--The Parse extended static geography method returns a geography instance when the input
--is expressed in the OGC WKT representation.
INSERT INTO EmployeesAddress
VALUES
('ALRiyadh',
    GEOGRAPHY::Parse('POINT(-83.0086 39.95954)'));
GO    
    
SELECT * FROM EmployeesAddress
GO

--Other example use GEOMETRY data type
DECLARE @s GEOMETRY
SET @s = 'LINESTRING (  69 26, 69 23, 69 21, 67 20, 65 20, 
          63 18, 58 17, 52 17, 51 17, 49 17, 45 18, 44 20, 
          44 21, 42 26, 42 29, 42 32, 42 35, 43 35, 47 38, 
          50 41, 55 42, 58 42, 65 44, 66 44, 67 44, 68 45, 
          69 47, 70 48, 70 50, 71 51, 70 56, 68 60, 68 63, 
          66 65, 65 66, 63 68, 60 71, 59 71, 57 71, 55 71, 
          51 69, 45 65, 44 63, 42 62, 41 59, 41 57, 41 56, 
          41 54, 42 53 )'
          
SELECT @s



Monday, February 22, 2010

FileStream Data Type

Filestream data type used to store data(unstructured data) as varbinary(MAX) in separate file in the file system that means not with database file. This increase the performance of read and writes to database of this type.

A VARBINARY (MAX) column with FILESTREAM attribute is not restricted to the 2 GB limit SQL Server imposes on Large Value Types. The size of the file is limited by the size of the disk volume only.

Why FileStream data type?

If you think to separate your files storing from its structured storage, This separation can cause data management complexities and if the data is associated with structured storage, the file streaming capabilities and performance can be limited.

Note:Your data must be large than 1 mb in size to involve the filestream data type.

To implement Filestream in your database do the following:
  1. Enblae Filestream in Sql server 2008
  2. Create filegourp that contains the filestream database file
You can enable Filestream in Sql server in three ways:
  1. At the time of installation SQL Server 2008
  2. or by using T-SQL (sp_configure)
  3.  Or by SQL Server Configuration manager
Let's see the easiest way which is by Sql Server Configuration manager:

So first open SQL Server Configuration manager and then right click on your instance name and choose properties and select Filestream tap and enable it.



Let's see an example:

  1. After you enabled the Filestream in your instance
  2.  Create a folder called "TestDb" in C: partition
  3. Then run this command to create a database with three fille (.mdf,.ldf,filestream data container or folder)

CREATE DATABASE TestDb 
ON
PRIMARY ( 

    NAME = TestDB, 
    FILENAME = 'C:\TestDb\TestDb.mdf'

), 
FILEGROUP TestDbFS CONTAINS FILESTREAM( 

     NAME = TestDbFS,
     FILENAME = 'C:\TestDb\TestDbFS')

LOG ON (                         

      NAME = TestDbLog,
      FILENAME = 'C:\TestDb\TestDbLog.ldf')
GO


4. Open c:\TestDb in windows explorer and you see the following:


You will see a folder “$FSLOG” and a file “filestream.hdr”.
The folder “$FSLOG” is the FILESTREAM equivalent of the Database Transaction Log file. “filestream.hdr” contains important metadata information used by SQL Server internally.

5. Create a table has a filestream attribute

USE TestDb
GO

CREATE TABLE [dbo].[Employees](
    -- Table which use filestream must has 
    -- UNIQUEIDENTIFIER ROWGUIDCOL NOT NULL UNIQUE column
   Id        UNIQUEIDENTIFIER ROWGUIDCOL NOT NULL UNIQUE,
   FullName NVARCHAR(50),
   EmpPhoto VARBINARY(MAX) FILESTREAM NULL

)
GO


6. Now let's insert a record to the above table

USE TestDb
GO
--This code to load the image from the disk to
-- @image variable
DECLARE @image AS VARBINARY(MAX)
SELECT @image = CAST(bulkcolumn AS VARBINARY(MAX))

      FROM OPENROWSET(
      BULK
      'C:\TestDb\1.png',
      SINGLE_BLOB ) AS a

-- Then inserted to a table          
INSERT INTO Employees (Id, FullName, EmpPhoto)
SELECT NEWID(), 'Fadi Ahmad',@image
GO


7. And try to run this command

USE TestDb
GO

SELECT *, EmpPhoto.PathName() FROM Employees
GO


You will see that the image data displayed as binary data.

Tuesday, February 16, 2010

New Data types in Sql Server 2008

Sql server 2008 introduces new types that help the developer to have various choices to deal with various data.


Note:

In previous version "The DATETIME data type stores both the date and the time portion together as single column value. This data type supported dates between January 1, 1753 and December 31, 9999, with the time portion being accurate to 3.33 milliseconds and required 8 bytes of storage space. The SMALLDATE data type requires less storage space then a DATETIME data type, only 4 bytes, but supports a smaller date and time range. SMALLDATE only supports dates from January 1, 1900 through June 6, 2079, and the time portion is only accurate down to the minute."

Now let's see the now data types which introduced with sql server 2008.

Date Type (3 byte)
When you don't need the time portion of a DATETIME, you can use it

DECLARE @RES DATE
SET @RES = GETDATE()
SELECT @RES


Time Type (The size can be 3, 4, or 5 bytes, depending on the chosen precision)
When you don't need the date portion of a DATETIME, you can use it

DECLARE @RES TIME
SET @RES = GETDATE()
SELECT @RES

-- The default precision is 7 digits
-- you can display from 1 to 7 digits
DECLARE @RES2 TIME(7)  
SET @RES2 = GETDATE()
SELECT @RES2


DATETIME2 type(6 to 8 bytes)
DATETIME2 shows the greatest range of precision of date and time

DECLARE @RES DATETIME2
SET @RES = GETDATE()
SELECT @RES

--Note number of precision between the two results
DECLARE @RES2 DATETIME
SET @RES2 = GETDATE()
SELECT @RES2


DATETIMEOFFSET type (8 to 10 bytes)
Here provide you with extra time zone

DECLARE @RES DATETIMEOFFSET
SET @RES = GETDATE()
SELECT @RES


HIERARCHYID type (stored as varbinary <= 900 bytes)

Hierarchal data is defined as a set of data items related to one another in a hierarchy, that is, a parent node has a child and so forth
We use this type to store hierarchical data to simplify the retrieve data instead of using recursive operation or other complex way. So if you remember ParentID or ReportToId column now with this type no need for this column as foreign key.
With HierarchyID, the data type stores the whole path to the current record in the “Id” column.

Note:
HIERARCHYID type is a SQL CLR UDT (Common Language Runtime User Defined Type).

Let's see this example:

USE TestDb
GO

--Create Table that contains Id as hierarchyid data type
CREATE TABLE Employees
(
    Id       HIERARCHYID NOT NULL PRIMARY KEY,
    FullName NVARCHAR(256) NOT NULL,
    
)
GO

--Insert some data to this table with its path
INSERT INTO Employees(Id, FullName) VALUES
    (hierarchyid::GetRoot(), 'Fadi Ahmad Abdulwahab'),
    ('/1/', 'Shadi Ahmad Abdulwahab'),                
    ('/1/1/', 'Tareq Ahmad Abdulwahab'),        
    ('/1/1/1/','Rola Ahmad abdulwahab'),
    ('/2/','Rana Ahmad abdulwahab'),
    ('/2/1/','Rasha Ahmad abdulwahab')                
GO

-- Now let's retrieve the data
SELECT 
-- Id stored as binary format
Id, 
-- complete path of current record in the tree
Id.ToString() 'Record Path',
-- level of current record in the tree
Id.GetLevel() 'Record  Level',
-- Get childs of parent start from first root not the root '/'
Id.GetAncestor(1) 'partents of /./..',
-- Get childs of parents start from second root not the first root '/1/'
Id.GetAncestor(2) 'Parents of /././ ...',
FullName
FROM Employees
GO




--Let's do some filtering
--Here i want to retrieve only all records thier parent is /2/
SELECT 
Id, -- stored as binary format
Id.ToString() 'Record Path', -- output path
Id.GetLevel() 'Record  Level', -- output level
FullName
FROM Employees
WHERE Id.IsDescendantOf('/2/') = 1
GO


Others methods supported with HIERARCHYID:

  •  GetAncestor: Returns a HierarchyID that represents the parent of this HierarchyID node.
  •  GetDescendant: Returns a child node of this HierarchyID node.

Sunday, February 14, 2010

When using SQLCLR?

Always make your natural choice for data operations "T-SQL" to get benefits of ease of use and deploy and query plans and caching query plans,..etc unless you face the following issues:
  •  Complex and heavily operations
  • Pattern matching
  • Deals with data out of Sql server
  • Needs to write extended stored procedure by using C++ and COM.
Why Procedural and recursive code works much better in CLR?

CLR code and higher-level programming languages such as C# and VB .NET have offered better support for procedural and recursive code. This is because such languages are designed from the ground up to support per method call-based stack frames and activation records, and they do not suffer from limits such as a maximum call depth of 32 in recursive operations like in T-SQL.

So there are times or reasons to use SQLCLR or T-SQL and basically for example writing outer join using T-SQL in stored procedure easier than CLR stored procedure and better performance and it does not need to host additional resources like SQLCLR engine or more step to get the result.

Shortly

"The use of CLR within SQL Server is an important aspect of making SQL Server more extensible, allowing developers to do more than what is possible with just the T-SQL language and its system functions"

The following types of objects can be created using SQLCLR:
  1. Stored procedures
  2.  Scalar or Table-valued UDFs
  3. Triggers (DML, DDL, and logon triggers)
  4. User-defined aggregates
  5. User-defined types (UDTs)
Note: user-defined aggregates and UDTs, can be created only using SQLCLR; they cannot be created using T-SQL.

Saturday, February 13, 2010

Indexes - part 5

Let's see examples with Filtered and covering indexes:

USE TestDb
GO

--Create demo table called Employees Table
CREATE TABLE Employees
(EmpId INT IDENTITY(1,1)
 ,Name VARCHAR(256)
 ,BirthDate DATETIME
 ,JobDetails VARCHAR(100)
)
GO

--Insert dummy data into Employees Table
DECLARE @loop INT
SET @loop = 1000

WHILE @loop > 0
BEGIN
      INSERT Employees  VALUES ('Fadi Ahmad Abdulwahab',
      '1984-05-26 00:00:00.000','Job details or description...' )
      SET @loop = @loop - 1
END

-- Create Clustered Index 
CREATE UNIQUE CLUSTERED INDEX CI_EmpId
ON Employees (EmpID)
GO

--Update this column for test
UPDATE dbo.Employees SET 
BirthDate = '2009-05-26 00:00:00.000'
WHERE EmpId=700

--Create Covering Index
CREATE NONCLUSTERED INDEX NCI_SmallTable ON Employees(EmpId,Name) INCLUDE (BirthDate)

SELECT EmpId,Name,BirthDate,JobDetails
FROM Employees
WHERE  BirthDate = '2009-05-26 00:00:00.000'






--Create filtered index
CREATE NONCLUSTERED INDEX NCI_BirthDate ON Employees(BirthDate)
WHERE BirthDate >= '2009-05-26 00:00:00.000'

--View Number of Rows in indexes
SELECT Name, i.index_id, [rows] 
FROM sys.partitions p
INNER JOIN sys.indexes i
ON p.[object_id] = i.[object_id]
AND p.index_id = i.index_id
WHERE p.[object_id] = OBJECT_ID('Employees')





SELECT EmpId,Name,BirthDate,JobDetails
FROM Employees
WHERE  BirthDate = '2009-05-26 00:00:00.000'


Wednesday, February 10, 2010

Indexes - part 4

Let's see examples with clustered and nonclustered indexes:

USE TestDb
GO

--Create demo table called Employees Table
CREATE TABLE Employees
(EmpId INT IDENTITY(1,1)
 ,Name VARCHAR(256)
 ,BirthDate DATETIME
 ,JobDetails VARCHAR(100)
)
GO

--Insert dummy data into Employees Table
DECLARE @loop INT
SET @loop = 1000

WHILE @loop > 0
BEGIN
      INSERT Employees  VALUES ('Fadi Ahmad Abdulwahab',
      '1984-05-26 00:00:00.000','Job details or description...' )
      SET @loop = @loop - 1
END

--Run Select statement with Execution Plan
SELECT EmpId,Name,BirthDate,JobDetails
FROM Employees
WHERE EmpId = 700





--View Number of Rows in indexes
SELECT Name, i.index_id, [rows] 
FROM sys.partitions p
INNER JOIN sys.indexes i
ON p.[object_id] = i.[object_id]
AND p.index_id = i.index_id
WHERE p.[object_id] = OBJECT_ID('Employees')
GO

-- Create Clustered Index 
CREATE UNIQUE CLUSTERED INDEX CI_EmpId
ON Employees (EmpID)
GO

--View Number of Rows in indexes
SELECT Name, i.index_id, [rows] 
FROM sys.partitions p
INNER JOIN sys.indexes i
ON p.[object_id] = i.[object_id]
AND p.index_id = i.index_id
WHERE p.[object_id] = OBJECT_ID('Employees')

--Run Select statement with Execution Plan
SELECT EmpId,Name,BirthDate,JobDetails
FROM Employees
WHERE EmpId = 700





--Update this column for test
UPDATE dbo.Employees SET 
BirthDate = '2009-05-26 00:00:00.000'
WHERE EmpId=700

 
--Now let's exam this query before we create 
--NonClustered Index 
SELECT EmpId,Name,BirthDate,JobDetails
FROM Employees
WHERE  BirthDate = '2009-05-26 00:00:00.000'



--Let's Create Nonclustered Index on BirthDate column
CREATE NONCLUSTERED INDEX NCI_BirthDate
ON Employees (BirthDate)

--View Number of Rows in indexes
SELECT Name, i.index_id, [rows] 
FROM sys.partitions p
INNER JOIN sys.indexes i
ON p.[object_id] = i.[object_id]
AND p.index_id = i.index_id
WHERE p.[object_id] = OBJECT_ID('Employees')

--Run Select statement with Execution Plan
SELECT EmpId,Name,BirthDate,JobDetails
FROM Employees
WHERE  BirthDate = '2009-05-26 00:00:00.000'



Indexes - part 3

In part 2 we have seen the clustered index and nonclustered index and let's continue with another new indexes introduced with Sql server 2005/2008


SQL Server 2005 introduces Index covering

By creating a non-clustered index that contains all the columns used in a SQL query called index covering. So the query accesses only the index file; it doesn't touch the table at all. In fact, there is no need to access the table because all the columns necessary to satisfy the query are already stored in the index. This is index covering. The index is much smaller than the table, which is why the query read many fewer pages.

SQL Server 2008 introduces Filtered indexex

Filtered indexes allow you to create a filter on an index. The index B-Tree will only contain the rows of data that meet the filtering criteria. This allows you to reduce the amount of data contained in an index which means that you are also reducing the data affected in an index by data modifications and also decrease usage of disk space. This often involved partitioning the tables in SQL Server 2005 or archiving data in earlier versions. Ideally we can say that Filtered Index is an optimized non clustered index (Filtered Index can only be created as a nonclustered index on a table), which is best suited for those queries that select a very small percentage of rows from a given table.

Points to remember with Filtered Index:

  • They can be used on views only if they are persisted views
  • They cannot be created on full-text indexes.

Monday, February 8, 2010

Indexes - part 2

We learned in part 1 some concept of indexes and let's continue by see different type of indexes.

So what is Clustered Index?
The clustered index stores the real data or rows at the leaf level of index.
Note:

  • Only one clustered index can contain on a table or view.
  •  The indexed values can be sorted in either ascending or descending order.
  •  Data in a table is sorted only if a clustered index has been defined on a table.
Terms: A clustered table can be referred to a table has a clustered index and Heap table for a table does not have a clustered index. When you create PK by default Sql server create a clustered index for your table.

Nonclustered Index
The nonclustered index only contains the pointer to actual data rows that means there is additional step in order to get the actual data.

There are differences of nonclustered index if it's on clustered table or heap table so if it's on a clustered table the pointer points to the clustered index to move to actual data row (the leaf node of the nonclustered index contains the clustered index keys) and if it's on heap table the pointer points to actual data row.

Note:
  •  nonclustered index cannot be sorted
  •  you can create more than one nonclustered indexes for a table or view
  •  SQL Server 2005 supports up to 249 nonclustered indexes, and SQL Server 2008 support up to 999
Index types:

1. Composite index: you can include up to 16 columns in an index as long as the index does not exceed the 900 byte limit.

2. Unique index: to ensures the uniqueness of each value even if your index contains composite index. For example if you create an index on EmpId and DeptId columns so together must be unique. A unique index is defined when you create a primary key for one column or more or add unique constrain.

Term: Column Cardinality
The cardinality of column is referred to the uniqueness of data inside a particular column

How to design indexes
Some considerations before index you table:

1. More indexes means more disk space so you need to implement the necessary indexes
2. When you update data the indexes automatically updated so this can affect performance

You should consider the following guidelines when planning your indexing strategy:

  • For tables that are heavily updated, use as few columns as possible in the index. 
  •  If a table contains a lot of data but data modifications are low, use as many indexes as necessary to improve query performance. 
  •  Use indexes carefully on small tables because the query engine might take longer to navigate the index than to perform a table scan. 
  •  For clustered indexes, try to keep the length of the indexed columns as short as possible. Ideally, try to implement your clustered indexes on unique columns that do not permit null values. This is why the primary key is often used for the table’s clustered index, although query considerations should also be taken into account when determining which columns should participate in the clustered index.
  • The uniqueness of values in a column affects index performance. In general, the more duplicate values you have in a column, the more poorly the index performs. 
  •  For composite indexes, take into consideration the order of the columns in the index definition. Columns that will be used in comparison expressions in the WHERE clause (such as WHERE FirstName = 'Fadi') should be listed first. 
  •  You can also index computed columns if they meet certain requirements. For example, the expression used to generate the values must be deterministic.
  •  Try to insert or modify as many rows as possible in a single statement, rather than using multiple queries. 
  •  Create nonclustered indexes on columns used frequently in your statement’s predicates and join conditions. 
  •  Consider indexing columns used in exact-match queries. 
  •  Index should be created on columns used in WHERE clause, Order By, Group By, Distinct etc.
  •  All columns used in WHERE clause should be included in single index to get best results.
  •  Do not create indexes unless you need them, too many indexes will slow INSERT, UPDATE and DELETE.
Ref : i got the above tips from the following url

When to use it:

Clustered index:

  •  For use on columns that are frequently searched for ranges of data
  • For use on columns with low selectivity
Nonclustered index:

  • For use on columns that are searched for single values
  •  For use on columns with high selectivity