Wednesday, May 25, 2011

New DATETIME Data Types in SQL Server 2008

Introduction

From the beginning, Microsoft SQL Server DBAs have longed for two different data types, where you could store time and date separately. The long wait is over. In all of the older versions, we had only one data type that could store Datetime data type, and it stored both the value of the date and the value of the time.
Finally, in Microsoft SQL Server 2008, Microsoft is introducing a set of new data types for storing date, time and both date and time together. The new data types store more data as well, which means you can store dates anywhere from 01-01-01 to 9999-12-31 and also store time up to the fraction of 9999999.
There are four DATETIME data types newly introduced in SQL Server 2008. They are
DATE, TIME, DATETIMEOFFSET and DATETIME2.


1) DATE Data Type

The DATE data type stores only the date value.
The DATE data type returns data in the form of year, Month and Day. However, the format in which the date is returned is determined by the current language setting in SQL Server.
The range for the DATE data type is from 0001-01-01 through 9999-12-31.

When you specify a datetime value, SQL Server automatically converts that value into the DATE data type, which means that only the date portion is stored.

Example:

DECLARE @Date_Type DATE
SET @Date_Type = '2011-05-17 05:29:15.1234567'

SELECT @Date_Type

Result:
  2011-05-17

2) TIME Data type

The TIME data type stores only the time value. 
The TIME data type returns data in the form of hour, minute, second and fractional second.

The fractional part of the seconds can support up to 7 decimal places.
The precision of the TIME data type supports a range of 00:00:00.0000000 through 23:59:59.9999999.

When you specify the TIME datatype in a Transact-SQL statement, you can specify the precision of the stored values by including the appropriate number within parentheses.

For example, to specify a precision of 7, you would specify TIME(7).
For a precision of 5, you would specify TIME(5), and so on.

If you do not specify the precision, 7 are assumed.

Example:

DECLARE @Time_Type TIME
SET @Time_Type = '2011-05-17 05:29:15.1234567'

SELECT @Time_Type

Result:
  05:29:15.1234567

You can also specify a different precision when converting the data

DECLARE @DateTimeOffset_Type TIME
DECLARE @Time_Type TIME

SET @DateTimeOffset_Type = '2011-05-17 05:29:15.1234567'
SET @Time_Type = CAST(@DateTimeOffset_Type AS TIME(5))

SELECT @Time_Type

Result:
  05:29:15.1234600

Notice that the fractional seconds have been rounded up.
The original fractional seconds were .1234567. However, if you specify a precision of 5, SQL Server will round up the "67" fractional part, returning .1234600 in the results, rather than .1234500.

When inserting data into a TIME column, you can specify a time value or a datetime value as shown in the following INSERT statements:

INSERT INTO TableName (ID, Time_DataType)
VALUES ('1004, 05:23:10.1234567');    --Only Time
INSERT INTO TableName (ID, Time_DataType)
VALUES (1005, '2011-05-17 05:23:10.1234567');  --Date & Time

In both cases, only the time is inserted into the Time_DataType column.


3) DATETIME2 Data Type

The DATETIME2 data type stores the both date and time value.
The data type DATETIME2 is the combination of the data type DATE and TIME.
DATETIME2 is used to store both a date value ranging from 01-01-01 to 9999-12-31 as well as a time value 00:00:00.0000000 to 23:59:59.9999999.

Example:

DECLARE @DateTime2_Type DATETIME2
SET @DateTime2_Type = CONVERT(DATETIME2,'01/01/0001 16:14:00.1234567')

SELECT @DateTime2_Type

Result:
  0001-01-01 16:14:00.1234567 


We could limit the precision like (Precision value range 0-7),
DECLARE @DateTime2_Type DATETIME2(4)
SET @DateTime2_Type = CONVERT(DATETIME2,'01/01/0001 16:14:00.1234567')

SELECT @DateTime2_Type

Result:
  0001-01-01 16:14:00.1235

Notice that the fractional seconds have been rounded up.

4) DATETIMEOFFSET Data type

The DATETIMEOFFSET data type stores the date, time and time-zone offset value.
The DATETIMEOFFSET data type is nearly identical to the DATETIME2 data type, except that a DATETIMEOFFSET value includes one important addition that is a time-zone offset value.
The offset value represents the number of hours and minutes before or after Coordinated Universal Time (UTC).
A positive number indicates the amount of time to add to the UTC to determine the local time. (+05:30).
A negative number indicates the amount of time to subtract from the UTC to determine the local time. (-05:30)

The Date range is between 0001-01-01 and 9999-12-31.
The Time Range is between 00:00:00 and 23:59:59.9999999.
The Offset range is between -14:00 through +14:00.

Example:

DECLARE @DateTimeOffset_Type DATETIMEOFFSET
SET @DateTimeOffset_Type = CONVERT(DATETIMEOFFSET,'01/01/0001 16:14:00.1234567 +5:30')

SELECT @DateTimeOffset_Type

Result:
  0001-01-01 16:14:00.1234567 +05:30

You can also set the precision of the data type, like

DECLARE @DateTimeOffset_Type DATETIMEOFFSET(4)
SET @DateTimeOffset_Type = CONVERT(DATETIMEOFFSET,'01/01/0001 16:14:00.1234567 +5:30')

SELECT @DateTimeOffset_Type

Result:
  0001-01-01 16:14:00.1235 +05:30



Summary:

Data type
Format
Range
Accuracy
Storage size (bytes)
User-defined fractional second precision
Time zone offset
TIME
hh:mm:ss[.nnnnnnn]
00:00:00.0000000  to 23:59:59.9999999
100 nanoseconds
3 to 5
Yes
No
DATE
YYYY-MM-DD
00001-01-01 to 9999-12-31
1 day
3
No
No
SMALLDATETIME
YYYY-MM-DD hh:mm:ss
1900-01-01 to 2079-06-06
1 minute
4
No
No
DATETIME
YYYY-MM-DD hh:mm:ss[.nnn]
1753-01-01 to 9999-12-31
0.333 second
8
No
No
DATETIME2
YYYY-MM-DD hh:mm:ss[.nnnnnnn]
0001-01-01 00:00:00.0000000 to 9999-12-31 23:59:59.9999999
100 nanoseconds
6 to 8
Yes
No
DATETIMEOFFSET
YYYY-MM-DD hh:mm:ss[.nnnnnnn] [+|-]hh:mm
00001-01-01 00:00:00.0000000 to 9999-12-31 23:59:59.9999999 (in UTC)
100 nanoseconds
8 to 10
Yes
Yes

Thursday, May 19, 2011

Yield Keyword in C#


Introduction:

The yield keyword was new feature in C# 2.0 and is used to simplify the implementation of enumeration in custom classes.

Following are two forms of the yield statement.
1.       yield return <expression>;
2.       yield break;

·         In a yield return statement, expression is evaluated and returned as a value to the enumerator object; expression has to be implicitly convertible to the yield type of the iterator.

·         In a yield break statement, control is unconditionally returned to the caller of the iterator, 

Problem:

The yield keyword simplifies the implementation of iterable collections, but it also allows us to move beyond collections and into result sets. Using the yield keyword, we can convert calculated sequences into collections.

Let me give an example. Let’s say that I am calculating the sequence of square roots for all numbers.
Assuming for the moment that we do create an infinite array, let’s look at how those numbers would be generated without using the yield keyword.

There would be a piece of code that would call the algorithm to generate the sequence of numbers. The sequence of numbers would be added to an array, which is returned to the calling code when the algorithm has completed. Yet we are calculating an infinite sequence of numbers, meaning that the algorithm will never end and the array will never be complete.

Of course, in reality, algorithms do end, and arrays do become complete. But the example illustrates that if you were to generate a collection that could be iterated, you must first generate the collection and then iterate the collection. This would mean you first allocate the space for an array and then fill the array, resulting in a not-as-efficient solution. The yield keyword is more efficient, because it allows a calculation to generate numbers on the fly.


Implementation:

yield return statement:

In the following example, the yield statement is used inside an iterator block, which is the method ComputePower(int number, int power). When the Power method is invoked, it returns an enumerable object that contains the powers of a number. 

using System;
using System.Collections.Generic;

public class Program
{
  static void Main()
  {
    // Compute two with the exponent of 30.
    foreach (int value in ComputePower(2, 30))
    {
      Console.Write(value);
      Console.Write(" ");
    }
    Console.WriteLine();
  }

  public static IEnumerable<int> ComputePower(int number, int exponent)
  {
    int exponentNum = 0;
    int numberResult = 1;

    // Continue loop until the exponent count is reached.
    while (exponentNum < exponent)
    {
      // Multiply the result.
      numberResult *= number;
      exponentNum++;

      // Return the result with yield.
      yield return numberResult;
    }
  }
}
Output:

2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 32768 65536 131072 262144 524288 1048576 2097152 4194304 8388608 16777216 33554432 67108864 134217728 268435456 536870912 107374182


yield break statement:

 If inside this loop I want to abort the iteration and return, I do this through the yield break. The yield break will do more than a normal break, as it will return from the method, and not only from the for-loop.
E.g.: If I have to consecutive for-loops in the same method, both using yield, and I do yield break in the first, the second for-loop will never be executed:

  public static IEnumerable<string> GetMoreResults()
  {
    for (int i = 0; i < 20; i++)
    {
      yield return "Value " + i;
      yield break;
    }

    //Do something else

    for (int i = 0; i < 20; i++)
    {
      yield return "Another value " + i;
    }
  }

Here I will only get one value returned in my IEnumerable, as the yield break will end the method execution.

Limitation:

·         Unsafe blocks are not allowed.
·         Parameters to the method, operator, or accessor cannot be ref or out.
·         A yield return statement cannot be located anywhere inside a try-catch block. It can be located in a try block if the try block is followed by a finally block.
·         A yield break statement may be located in a try block or a catch block but not a finally block.
·         A yield statement cannot appear in an anonymous method. 

Merge Statement


In versions of SQL Server prior to SQL Server 2008, if you had a set of data rows in a source table that you wanted to synchronize with a target table, you had to perform at least three operations:
     I.    One scan of the source table to find matching rows to update in the target table,
   II.    Another scan of the source table to find non matching rows to insert into the target table,
  III.    And a third scan to find rows in the target table not contained in the source table that needed to be deleted.

SQL Server 2008, however, introduces the MERGE statement. With the MERGE statement, you can synchronize two tables by inserting, updating, or deleting rows in one table based on differences found in the other table, all in just a single statement, minimizing the number of times that rows in the source and target tables need to be processed.

The MERGE statement can also be used for performing conditional inserts or updates of rows in a target table from a source table.

The basic syntax of the MERGE statement is as follows:

[ WITH common_table_expression [,...n] ]
MERGE
[ TOP ( N ) [ PERCENT ] ]
[ INTO ] target_table [ [ AS ] table_alias ]
USING table_or_view_name [ [ AS ] table_alias ]
ON merge_search_condition
[ WHEN MATCHED [ AND search_condition ]
THEN { UPDATE SET set_clause | DELETE } ] [ ...n ]
[ WHEN NOT MATCHED [ BY TARGET ] [ AND search_condition ]
THEN { INSERT [ ( column_list ) ] { VALUES ( values_list ) | DEFAULT VALUES}} ]
[ WHEN NOT MATCHED BY SOURCE [ AND search_condition ]
THEN { UPDATE SET set_clause | DELETE } ] [ ...n ]
[ OUTPUT column_name | scalar_expression
INTO { @table_variable | output_table } [ (column_list) ] ]
[ OUTPUT column_name | scalar_expression [ [AS] column_alias_identifier ] [,...n ] ] ;

The MERGE syntax consists of the following primary clauses:

1.    The MERGE clause specifies the table or view that is the target of the insert, update, or delete operations.
2.    The USING clause specifies the data source being joined with the target. The ON clause specifies the join conditions that determine how the target and source match.
3.    The WHEN MATCHED clause specifies either the update or delete operation to perform when rows of target table match rows in the source table and any additional search conditions.
4.    WHEN NOT MATCHED BY TARGET specifies the insert operation when a row in the source table does not have a match in the target table.
5.    WHEN NOT MATCHED BY SOURCE specifies the update or delete operation to perform when rows of the target table do not have matches in the source table.
6.    The OUTPUT clause returns a row for each row in the target that is inserted, updated or deleted.

The WHEN clauses specify the actions to take on the rows identified by the conditions specified in the ON clause. The conditions specified in the ON clause determine the full result set that will be operated on. Additional filtering to restrict the affected rows can be specified in the WHEN clauses. Multiple WHEN clauses with different search conditions can be specified. However, if there is a MATCH clause that includes a search condition, it must be specified before all other WHEN MATCH clauses.

Example:

SELECT * FROM Test01
SELECT * FROM Test02

MERGE
  dbo.Test01
AS TargetLV
USING (
        SELECT
          *
        FROM
          dbo.Test02
      ) AS SourceLV
    ON TargetLV.Id = SourceLV.Id
  WHEN MATCHED THEN
    UPDATE
    SET
      TargetLV.Name = SourceLV.Name
  WHEN NOT MATCHED BY SOURCE THEN
    DELETE
  WHEN NOT MATCHED BY TARGET THEN
    INSERT
    (
      ID,
      Name
    )
    VALUES
    (
      SourceLV.ID,
      SourceLV.Name
    )
  OUTPUT $ACTION, Inserted.Id, Inserted.Name,Deleted.Id, Deleted.Name;

SELECT * FROM Test01
SELECT * FROM Test02

Conclusion
Generally, we can use the MERGE statement while implementing the Master-Child relationship. What I have tried to explain here is just an overview of this statement. For further details, you can refer –