Thursday, July 4, 2013

How to fetch last row from a table Or How to fetch Second Last row from a SQL table

This is similar to my blog-post: How to fetch second row from a table in SQL Server
To fetch rows from last, first you have to query number of rows in your table then use this number in OFFSET FETCH clause
Consider I have a table with following Data
SELECT [ID]
      ,[Name]
      ,[Post]
  FROM [JPDB].[dbo].[Employee]
Result:

Below is the query to fetch Last row in a result set
DECLARE @Total [int]

SET @Total= (SELECT COUNT(*) FROM [JPDB].[dbo].[Employee])
SELECT [ID]
      ,[Name]
      ,[Post]
  FROM [JPDB].[dbo].[Employee]
  ORDER BY [ID] ASC OFFSET (@Total-1) ROWS FETCH NEXT 1 ROWS ONLY
Result:

I am taking one more example, suppose I want last three rows
Below is the query
DECLARE @Total [int]

SET @Total= (SELECT COUNT(*) FROM [JPDB].[dbo].[Employee])
SELECT [ID]
      ,[Name]
      ,[Post]
  FROM [JPDB].[dbo].[Employee]
  ORDER BY [ID] ASC OFFSET (@Total-3) ROWS FETCH NEXT 3 ROWS ONLY
Result:

One more trick to get last row from a table using OFFSET FETCH Clause
Use reverse order DESC in place of ASC
Below is the query
SELECT [ID]
      ,[Name]
      ,[Post]
  FROM [JPDB].[dbo].[Employee]
  ORDER BY [ID] DESC  OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY
Result

How to fetch second row from a table in SQL Server, Or How to retrieve specific row from a ordered result of SQL Query

SQL Server 2012 has many enhanced features, one is OFFSET FETCH clause. To fetch second row or any specific row from an ordered result of SQL query we can use this feature of SQL Server. We can fetch specific row from ordered result set using only single statement as I explained below.

OFFSET FETCH Clause

The OFFSET-FETCH clause provides us option to fetch only a window or page of results from the result set. The only limitation is, you can use this with ORDER BY clause only. That make sense actually, if we want second row of a result set it means some order should present from which we want second row
I have one table which has employee information as shown below
SELECT [ID]
      ,[Name]
      ,[Post]
  FROM [JPDB].[dbo].[Employee]
Result of above query is:

Below is the SQL script using OFFSET FETCH to get 2nd row of a result
 SELECT [ID]
      ,[Name]
      ,[Post]
  FROM [JPDB].[dbo].[Employee]
  ORDER BY [ID] ASC OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY
The number I have written after OFFSET skips rows, and the number written after NEXT get total number of rows
Result of above query:

Suppose I want 2nd and 3rd row then below is the query:
SELECT [ID]
      ,[Name]
      ,[Post]
  FROM [JPDB].[dbo].[Employee]
  ORDER BY [ID] ASC OFFSET 1 ROWS FETCH NEXT 2 ROWS ONLY
Result:

Wednesday, July 3, 2013

XML Parser in SQL Server (T-SQL), How to parse XML in SQL

There are many cases when we want to read XML file and store data in SQL Server table

Suppose we have XML file as show below:

  
   
   Inside First Node  
   
   
   Inside Second Node 1  
   inside Second Node 2  
   
   
   Item 1  
   Item 2  
   Item 3  
   
 

There are two options to parse XML in T-SQL

1- nodes method msdn link

2- OPENXML msdn link

nodes method in T-SQL

nodes method is useful when you want to read a node data from a XML, Lets take an example for above xml

If I want to read data from a specific node 'insidesecondnode1' from above xml file

DECLARE @x xml 
SET @x=
'    
     
   Inside First Node    
     
     
   Inside Second Node 1    
   inside Second Node 2    
     
     
   Item 1    
   Item 2    
   Item 3    
     
'
SELECT T.c.query('text()') AS result
FROM   @x.nodes('/root/secondnode/insidesecondnode1') T(c)
GO

Result for above query is as shown below

In the line 18, what ever path you will give this query will result the data inside the node

If a node is present multiple time in xml for example 'item' node inside 'thirdnode' in above xml, then this query will fetch data from all the nodes

DECLARE @x xml 
SET @x=
'    
     
   Inside First Node    
     
     
   Inside Second Node 1    
   inside Second Node 2    
     
     
   Item 1    
   Item 2    
   Item 3    
     
'
SELECT T.c.query('text()') AS result
FROM   @x.nodes('/root/thirdnode/item') T(c)
GO

Result of above query:

If you want to fetch particular node in above case and id value also, then use below query:

      
   Inside First Node    
     
     
   Inside Second Node 1    
   inside Second Node 2    
     
     
   Item 1    
   Item 2    
   Item 3    
     
'
SELECT T.c.value('@id','int') ID
       ,T.c.query('text()') AS result
FROM   @x.nodes('/root/thirdnode/item[2]') T(c)
GO

In above query line 18, by appending with [number] will read only that specific node, in this case second node

Result of above query:

OPENXML in T-SQL

In nodes method there is one restriction, you can not pass SQL parameter in nodes method

Suppose you have many keys stored in one table as shown below

We can iterate over all the keys using this table to fetch corresponding data from xml file and store to another table as simple text

We can do this by applying a loop and passing all the keys stores in table

SQL Query to implement this approach is as shown below:

DECLARE @hdoc [int]
DECLARE @Key [varchar](1000)
DECLARE @XMLDoc [XML]
DECLARE @NumberOfKeys [int]
DECLARE @Counter [int] = 0;
DECLARE @Value [varchar](2000);

IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[TempTable]') AND TYPE IN (N'U'))
  BEGIN
   DROP TABLE [dbo].[TempTable]
  END
  -- Creating temp table to store values parsed from XML
  CREATE TABLE [dbo].[TempTable](
      [ID] [int],
   [Key] [varchar](2000),
   [Value] [varchar](2000)
  ) 
SET @XMLDoc=
'    
     
   Inside First Node    
     
     
   Inside Second Node 1    
   inside Second Node 2    
     
     
   Item 1    
   Item 2    
   Item 3    
     
'
SET @NumberOfKeys= (SELECT COUNT([Key]) FROM [JPDB].[dbo].[XMLKeysTable]);

EXEC sp_xml_preparedocument @hdoc OUTPUT, @XMLDoc

 WHILE(@Counter < @NumberOfKeys)
 BEGIN
   SELECT @Key = [Key] FROM [JPDB].[dbo].[XMLKeysTable] ORDER BY [Key] OFFSET @Counter ROWS FETCH NEXT 1 ROWS ONLY
   SELECT @Value = Value FROM OPENXML (@hdoc, @Key,1) WITH (Value [varchar](1000) 'text()[1]')
   INSERT INTO [dbo].[TempTable] 
   VALUES(@Counter, @Key, @Value)
   SET @Counter += 1;
  END

--Removes the internal representation of the XML document specified by the document handle and invalidates the document handle
EXEC sp_xml_removedocument @hdoc

SELECT * FROM [dbo].[TempTable]

Result of above query:

You can also refer my blog: Pass XML file in stored procedure as a input parameter from C#