Sunday 31 July 2011

Using MERGE statement in SQL Server 2008

‘MERGE’ statement is a new feature in SQL Server 2008. It can be used to perform insert, update and delete operation on a destination table simultaneously based on the results of a join with a source table. Well, it sounds like a bit confusing, but let's see an example on how it can help us.

Assume we have following two tables.

  • STUDENT_A
  • STUDENT_B

Both table are identical in structure (Structure does not need to be identical).

STUDENT_A

img_scr_001

 

STUDENT_B

img_scr_002

 

And we have to update the ‘STUDENT_A’ with the details at ‘STUDENT_B’. We need to compare and if student ID’s are matched, ‘A’ table should be updated with the ‘B’ table. And if the ID’s in ‘B’ Table are new then we have to insert those to the ‘A’ table.

img_scr_003

 

So using the ‘MERGE’ statement we can achieve this in one execution.

Syntax:

MERGE  <Target> [AS T]
USING <Source> [AS S]
ON <Condition>
[WHEN MATCHED THEN <Execution>]
[WHEN NOT MATCHED BY TARGET <Execution>]
[WHEN NOT MATCHED BY SOURCE <Execution>]


And to do the above operation use the following code:


MERGE STUDENT_A AS T
USING STUDENT_B AS S
ON T.ID = S.ID
WHEN MATCHED THEN UPDATE SET T.AGE = S.AGE
WHEN NOT MATCHED THEN INSERT (ID, FNAME, LNAME, AGE) VALUES(S.ID,S.FNAME,S.LNAME,S.AGE);



**Please note that semicolon ‘;’ is mandatory.


So after executing the above code, and if you inspect the Table ‘A’, you can see that it’s updated the way we wanted.


img_scr_005



 


And also you can use additional rules other than your condition. To illustrate that, first we insert a record to both the tables.


insert into STUDENT_A 
select 10, 'John','Doe',30

insert into STUDENT_B
select 10, 'John','Doe',30



And using the following code you can remove the record with matches the condition and have the value 10.


MERGE STUDENT_A AS T
USING STUDENT_B AS S
ON T.ID = S.ID
WHEN MATCHED and S.ID < 5 THEN UPDATE SET T.AGE = S.AGE
WHEN MATCHED and S.ID = 10 THEN DELETE
WHEN NOT MATCHED BY TARGET THEN INSERT (ID, FNAME, LNAME, AGE) VALUES(S.ID,S.FNAME,S.LNAME,S.AGE);



And if you inspect the table A, you can see that it has the same following results:


img_scr_005

Thursday 28 July 2011

How to Use Update Cursors in SQL Server

There can be a situation where you have to use a cursor, even though the experts say not to use cursors or to avoid them as much as possible. But if you look closely, most of the time we use cursors to iterate through a row collection and update the same table.

In these type of situations it is ideal to use a Update Cursor, than using the default read only one.

Consider the following table :

CREATE TABLE [dbo].[SAMPLE_EMPLOYEE](
[EMP_ID] [int] NOT NULL,
[RANDOM_GEN_NO] [VARCHAR](50) NULL
) ON [PRIMARY]


Insert few records to the above table using the following script :


SET NOCOUNT ON
DECLARE @REC_ID AS INT

SET @REC_ID = 1

WHILE (@REC_ID <= 1000)
BEGIN
INSERT INTO SAMPLE_EMPLOYEE
SELECT @REC_ID,NULL

IF(@REC_ID <= 1000)
BEGIN
SET @REC_ID = @REC_ID + 1
CONTINUE
END

ELSE
BEGIN
BREAK
END
END
SET NOCOUNT OFF


Next we will add a Primary Key using the below script (Or you can use the table designer) :


ALTER TABLE [dbo].[SAMPLE_EMPLOYEE] ADD  CONSTRAINT [PK_SAMPLE_EMPLOYEE] PRIMARY KEY CLUSTERED 
(
[EMP_ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]


** Please note: A primary key should be there if we are to use an update cursor. Otherwise the cursor will be read only.


Here is how you use the Update Cursor. I have highlighted the areas which is differ from compared with a normal cursor. You have to mention which column you are going to update (or all columns in your selection will be updatable) and you have to use ‘where current of <cursor>’ in your update statement.


img_scr_001_a


SET NOCOUNT ON
DECLARE
@EMP_ID AS INT,
@RANDOM_GEN_NO AS VARCHAR(50),
@TEMP AS VARCHAR(50)



DECLARE EMP_CURSOR CURSOR FOR
SELECT EMP_ID, RANDOM_GEN_NO FROM SAMPLE_EMPLOYEE FOR UPDATE OF RANDOM_GEN_NO
OPEN EMP_CURSOR
FETCH NEXT FROM EMP_CURSOR
INTO @EMP_ID, @RANDOM_GEN_NO

WHILE (@@FETCH_STATUS = 0)
BEGIN
SELECT @TEMP = FLOOR(RAND()*10000000000000)
UPDATE SAMPLE_EMPLOYEE SET RANDOM_GEN_NO = @TEMP WHERE CURRENT OF EMP_CURSOR

FETCH NEXT FROM EMP_CURSOR
INTO @EMP_ID, @RANDOM_GEN_NO
END

CLOSE EMP_CURSOR
DEALLOCATE EMP_CURSOR

SET NOCOUNT OFF

Tuesday 26 July 2011

Fixed ~ Error installing SQL 2008 Business Intelligence Studio and Related Features

 

 

If you try to install/add feature SQL 2008 Business Intelligence Studio, to a machine with VS2008 having installed to a different location than the default, you will most probably get the following error :

img_screen_01

In order to install BI Studio 2008 properly, please do the following :

Find the folder which contains 'devenv.exe' (I have installed Visual Studio in 'D:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE')

Copy and paste the 'devenv.exe.config' file to the following location:

64-Bit
C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE

32-Bit
C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE

And now you proceed with the installation without any issue.

After installation, copy everything from 'C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE' (where you copied the 'devenv.exe.config') and paste it to the directory where you have VS2008 (In my machine it's  'D:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE')

And if you run Visual Studio 2008 you can see that the Business Intelligence Projects are now available.

img_screen_02_a

Sometimes you will be prompted to locate the 'devenv.exe' when opening the Business Intelligence Development Studio. Please adjust the shortcut or you can use the 'Visual Studio 2008' IDE.

img_screen_03

Thursday 21 July 2011

[Resolved] - AdventureWorks2008 sample database not available after installation

 

Many of you who have tried installing AdventureWorks2008 sample database, might have come across with an issue, which after installing the database package (using the executable), 'AdventureWorks2008' is missing from the database list. This is not a bug in SQL server or any error in the installer.
 
img_screen_04_a
 
In order to get the 'AdventureWorks2008' installed following should be fulfilled:
 
1.          Full-text search should be installed and running
 
img_screen_01_a
 
2.          FILESTREAM should be enabled and running
 
img_screen_03_a
 
Enabling FILESTREAM:

  1. Go to SQL Server Configuration Manager
  2. Click on SQL Server Services (left pane)
  3. Right click on the correct SQL Server Service (if you have more than one instance)
  4. Select properties from the menu
  5. Go to FILESTREAM tab and check the ‘Enable FILESTREAM for Transact-SQL access’ check box.
  6. Apply changes.

And if you still don’t see it in your database list, please reinstall the package.

img_screen_05_a

Thursday 14 July 2011

Extension Methods in C#

                                            
As name implies extension methods allow existing classes to be extended without using inheritance or changing class’s source code.

Some important points when using extension methods:

•    You cannot use extension methods to override existing methods
•    An extension method with the same name and signature as an instance method must not be called
•    The concept must not be applied to fields, properties or events
•    Do not overuse

As an example, we’ll create an extension method to existing ‘String’ class. We’ll implement the functionality of converting a string to a proper case. (Any given string will be converted to lower case, having the first letter of each word in upper case. E.g.: ‘this is sample text’ will be converted to ‘This Is Sample Text’)

Add a class and add the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace ExtensionMethods {
public static class SampleExtensionMethods {
public static string ToProper(this string zSource) {
StringBuilder sb = new StringBuilder();

string zTemp = zSource.ToLower();
List<string> data = zTemp.Split(new char[] { ' ' }).ToList<string>();
if (data.Count > 0) {
foreach (string element in data) {
if (element.Trim().Length > 0) {
sb.Append(element.Trim().Substring(0, 1).ToUpper() + element.Trim().Substring(1, element.Trim().Length - 1) + " ");
}
}
}

return sb.ToString().Trim();
}
}
}



please note that the class that the class should be static and the method should be public (Unless you are writing it within the same calss which you intend to use it). And also in input the parameter I have mentioned 'this'. This will tell the compiler to add the following method to the 'String' class.


Before using include the namspace. (I have used 'ExtensionMethods' as the namespace)


using ExtensionMethods;


And you can use the method we created using this syntax :


SomeStringVar.ToProper()


E.g:


using System;
using System.Text;
using ExtensionMethods;

namespace SampleConsoleApplicationA {
class SampleExtMethod {
static void Main(string[] args) {
string zSample = "this is sample text";
Console.WriteLine("Input : " + zSample);
Console.WriteLine("Output : " + zSample.ToProper());
Console.ReadLine();


}
}
}



And if you run the application you can get the below output (I have used a console application to illustrate this)


Extension Method

Delegates in C#

                                            
What's a delegate

A delegate is a type that safely wraps/encapsulate a  method. It is similar to 'function pointers' in C/C++, but it's type safe.

 

When to use a delegate

Use a delegate when :

  • An eventing design pattern is used.
  • It is desirable to encapsulate a static method.
  • The caller has no need access other properties, methods, or interfaces on the object implementing the method.
  • Easy composition is desired.
  • A class may need more than one implementation of the method.

There was a good example given on a book that I recently referenced.
'Consider your will—your last will and testament. It’s a set of instructions—“pay the bills, make a donation to charity, leave the rest of my estate to the cat,” for instance. You write it before your death, and leave it in an appropriately safe place. After your death, your attorney will (you hope!) act on those instructions.'

In order to a delegate to proceed, following four things need to happen:
* The delegate type needs to be declared.
* There must be a method containing the code to execute.
* A delegate instance must be created.
* The delegate instance must be invoked.

Declaring delegate type
Declaration of a delegate type specifies what kind of action can be represented by instances of the type.

E.g :

delegate void MySampleDelegate(string zParameter)



If we are creating an instance of the above delegate, we should have a method with one string parameter and void return type.



Method containing the code to execute
There should be a method which matches the signature of the delegate. Method can be either static or non static. I will take two examples to illustrate a static method and a non static method. I will use the following two methods in the 'DelegateClass' class.


E.g :


public class DelegateClass {
public static void MySampleDelegateMethodStatic(string zParameter) {
Console.WriteLine("From static method. Parameter : {0}", zParameter);
}

void MySampleDelegateMethodNonStatic(string zParameter) {
Console.WriteLine("From non static method. Parameter : {0}", zParameter);
}
}



Creating a delegate instance
When creating delegate instances static methods can  be passed directly. But for non static methods an instance should be created and passed that to the delegate.


E.g :    [Static Method]


MySampleDelegate delMethodA = new MySampleDelegate(DelegateClass.MySampleDelegateMethodStatic);


E.g :    [Non Static Method]


DelegateClass deligateClass = new DelegateClass();
MySampleDelegate delMethodB = new MySampleDelegate(deligateClass.MySampleDelegateMethodNonStatic);


Or


MySampleDelegate delMethodB = new MySampleDelegate(new DelegateClass().MySampleDelegateMethodNonStatic);


Invoking delegate method
It's a matter of calling the Invoke method on the delegate instance.


E.g :


delMethodA.Invoke("Parameter A");
delMethodB.Invoke("Parameter B");



Here is the complete source code for the above example. I have used a console application to illustrate this :


using System;
using System.Text;


namespace SampleConsoleApplicationA {

delegate void MySampleDelegate(string zParameter);

class Program {
static void Main(string[] args) {

MySampleDelegate delMethodA = new MySampleDelegate(DelegateClass.MySampleDelegateMethodStatic);
MySampleDelegate delMethodB = new MySampleDelegate(new DelegateClass().MySampleDelegateMethodNonStatic);

delMethodA.Invoke("Parameter A");
delMethodB.Invoke("Parameter B");

Console.ReadLine();
}
}

public class DelegateClass {
public static void MySampleDelegateMethodStatic(string zParameter) {
Console.WriteLine("From static method. Parameter : {0}", zParameter);
}

public void MySampleDelegateMethodNonStatic(string zParameter) {
Console.WriteLine("From non static method. Parameter : {0}", zParameter);
}
}
}






And when you run your application you will get the following output:


Delegate Sample


Instead of using separate delegate instances, you can combine and execute delegates. We have to use the 'Combine' method in Delegate class. To illustrate this I will alter my 'DelegateClass' as shown below :


public class DelegateClass {

public void MySampleMethodA(string zParameter) {
Console.WriteLine("MySampleMethodA. Parameter : {0}", zParameter);
}

public void MySampleMethodB(string zParameter) {
Console.WriteLine("MySampleMethodB. Parameter : {0}", zParameter);
}

public void MySampleMethodC(string zParameter) {
Console.WriteLine("MySampleMethodC. Parameter : {0}", zParameter);
}

}



To create instances and invoke :



MySampleDelegate[] mySampleDelegates = new MySampleDelegate[]{
new MySampleDelegate(new DelegateClass().MySampleMethodA),
new MySampleDelegate(new DelegateClass().MySampleMethodB),
new MySampleDelegate(new DelegateClass().MySampleMethodC)
};

MySampleDelegate sample = (MySampleDelegate)Delegate.Combine(mySampleDelegates);
sample.Invoke("Parameter X");



Or

MySampleDelegate D, deligateA, deligateB, deligateC;

deligateA = new DelegateClass().MySampleMethodA;
deligateB = new DelegateClass().MySampleMethodB;
deligateC = new DelegateClass().MySampleMethodC;

D = deligateA + deligateB + deligateC;

D("Parameter X");



 


The complete source code :


using System;
using System.Text;


namespace SampleConsoleApplicationA {

delegate void MySampleDelegate(string zParameter);

class Program {
static void Main(string[] args) {

MySampleDelegate[] mySampleDelegates = new MySampleDelegate[]{
new MySampleDelegate(new DelegateClass().MySampleMethodA),
new MySampleDelegate(new DelegateClass().MySampleMethodB),
new MySampleDelegate(new DelegateClass().MySampleMethodC)
};

MySampleDelegate sample = (MySampleDelegate)Delegate.Combine(mySampleDelegates);
sample.Invoke("Parameter X");


/*
MySampleDelegate D, deligateA, deligateB, deligateC;

deligateA = new DelegateClass().MySampleMethodA;
deligateB = new DelegateClass().MySampleMethodB;
deligateC = new DelegateClass().MySampleMethodC;

D = deligateA + deligateB + deligateC;

D("Parameter X");

*/

Console.ReadLine();
}
}

public class DelegateClass {

public void MySampleMethodA(string zParameter) {
Console.WriteLine("MySampleMethodA. Parameter : {0}", zParameter);
}

public void MySampleMethodB(string zParameter) {
Console.WriteLine("MySampleMethodB. Parameter : {0}", zParameter);
}

public void MySampleMethodC(string zParameter) {
Console.WriteLine("MySampleMethodC. Parameter : {0}", zParameter);
}

}
}



 


An when you run the application, you will get the following output:


Delegate Sample B

Wednesday 4 May 2011

Fixed: SharePoint Designer Error - Unexpected error on server associating the workflow

 

When publishing a SPD workflow which contains more than 6 Approval process steps, it gives the following error :

screen_01

How ever if you try 5-6 times, you might end up publishing it. But sometimes it won’t work.

Even though Microsoft had mentioned that their 'February 2011 CU for the SharePoint 2010 Product Family' would fix most of the errors, this was not included among them.

But there’s a work around to fix the above issue.

Open the ‘web.config’ which associate with the site collection that you are publishing the workflow. (E.g: ‘C:\inetpub\wwwroot\wss\VirtualDirectories\<Port>’)

Find the following entry :

<httpRuntime maxRequestLength="51200" />


And change it to :


<httpRuntime maxRequestLength="51200" executionTimeout="300" />



Save the ‘web.config’ file and restart the web site from IIS.


Now you can publish the workflow without any issue.

Tuesday 19 April 2011

How to display distinct data from a duplicated data source in an InfoPath form

Sometimes we have to get data to our InfoPath forms from various sources. (From SQL Table, using a Web Service, from a SharePoint list etc..)

But most of the it’s really annoying, if you do not have the option of filtering them directly from the backend (so all the duplicated data will be displayed).

For this example I will be using the following SQL table with duplicated data.

img_scr_001

So when I connect and retrieve data to an InfoPath form. I will get the following result. (I am using an InfoPath list box to display the data). And if you note, you can see various duplicated values are there in the list.

img_scr_003

And I have used the following connection to retrieve data.

img_scr_002

We can eliminate the duplicated values by applying the following filter to the list box. To do that follow these steps:

  • Right click on the list box and select ‘List Box Properties’

img_scr_004

Click on the ‘Select XPath’ button in front of Entries field.

img_scr_005

Click on the filter button on the next screen.

img_scr_006

And add the following filter:

not(. = ../preceding::<XPath>)


‘<XPath>’ is the data source entry of your list box control. To find the XPath, right click that data field and select ‘Copy XPath’.


In my example the XPath is:


/dfs:myFields/dfs:dataFields/d:temp_table/@Name


But since I am referring to it from ‘/dfs:myFields/dfs:dataFields/’ branch, it should be omitted and only ‘d:temp_table/@Name’ is required.


So the filter should be :


not(. = ../preceding::d:temp_table/@Name)

img_scr_007

Click ‘OK’ on all dialogs and close them. And when you preview your form, you can see only the distinct values.


img_scr_008









Hope this will help you.. Smile

Thursday 27 January 2011

How to filter data correctly on a repeating table in an InfoPath form using a date range

If you have used InfoPath in your developments, you might have come across with the issue of filtering data using a date range, after the data is received to the form. To illustrate the issue I will use a sample InfoPath form.

Add two date pickers and name them ‘fldFrom’ and ‘fldTo’

screen_01

Add your data source to the form. To this sample I will use a SharePoint list. which contains ID, Created Date and the Version. Drag the data source to the InfoPath form and you will be prompt with three options

screen_02

Select the ‘Repeating Table’ option. And if you preview the form you can see a similar view shown below.

screen_03

Now we will add a rule to the repeating table so that the data will be filtered according to a given date range.

Select the data source and click on the ‘Manage Rules’ on the home tab.

screen_06

Add a formatting rule.

screen_07

And enter following to the condition.

screen_08

Click ok and select the ‘Hide this control’

screen_10

And if you preview your form and give the desired date range for the From and To dates. You can easily notice that the filtering of data is not what you have expected. In this example I want to list data, which the created date is between ‘01-Jan-2011’ and ‘14-Jan-2011’. But even I assign those values to the date pickers, I will not get data which was created on ‘14-Jan-2011’.

**Please note : In the InfoPath form that I have used, the date format is ‘M/D/YYYY’

screen_11

So prevent this issue, in the Rule we created earlier, we will use an expression. The logic of the expression is to convert the dates into a number, and compare them.

E.g.: We convert the date to a number format similar to ‘YYYYMMDD’ if the date is ‘01/01/2011’ after the conversion it will be ‘20110101

First change the dates formats to the following format [‘English (South Africa) and ‘YYYY/MM/DD’]

screen_12

And change the Condition we added earlier to the following expressions:

screen_13

The expressions should be :

number(concat(substring(d:Created, 1, 4), substring(d:Created, 6, 2), substring(d:Created, 9, 2))) < number(concat(substring(xdXDocument:get-DOM()/my:myFields/my:fldFrom, 1, 4), substring(xdXDocument:get-DOM()/my:myFields/my:fldFrom, 6, 2), substring(xdXDocument:get-DOM()/my:myFields/my:fldFrom, 9, 2)))


number(concat(substring(d:Created, 1, 4), substring(d:Created, 6, 2), substring(d:Created, 9, 2))) > number(concat(substring(xdXDocument:get-DOM()/my:myFields/my:fldTo, 1, 4), substring(xdXDocument:get-DOM()/my:myFields/my:fldTo, 6, 2), substring(xdXDocument:get-DOM()/my:myFields/my:fldTo, 9, 2)))


And when you preview the form, initially all the records will be displayed, since there are no default values set on two date pickers. But when I change the dates the range I set earlier, I will get the desired  result shown below.


screen_14


Hope this might be useful to you.. Smile

Tuesday 18 January 2011

Repeating a SQL row based on a value in a different column

There are times that we get requirements such as populating and duplicate SQL rows, based on a value, on another column. E.g.: In an inventory system when items are received those details will be saved in the following format (ItemDetails) :

screen_01

And we are asked to create a GUI for end user to enter ‘Serial Numbers’ for each item. And we have to repeat the above mentioned item codes number of times which equals to the ‘ItemQty’. Of course we can achieve that using a SQL cursor or iterate using C# coding. But following example I will show how to do it using SQL.

The task would have been very simple if we would have a another table with a structure similar to this: (TempTable)

screen_02

So when the two table are joined ‘ItemDetails’ will repeat according to the row count of the ‘TempTable’. But it is not very practical, and it will result in duplicating data, which will grow your database un-necessary when time goes.

But instead we can use on single table which contains a series of numbers. These numbers will start from ‘1’. And the end should be the maximum quantity which an Item can have. For this example I will take ‘10’ as the maximum value. And that table should have the following structure.

screen_03

Use the following T-SQL statement to create the table:

CREATE TABLE [IntermediateTable](
[MaxQty] [int] NULL
) ON [PRIMARY]


For this example I have inserted up to 20. But in a real world scenario it may be required to enter values (More than 1000). In a similar situation you can use the following T-SQL statement to insert values.


insert into IntermediateTable
select
thousand.number*1000 +
hundred.number*100 +
ten.number*10 +
one.number
from(
select 1 as number union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 0) one
cross join (select 1 as number union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 0) ten
cross join (select 1 as number union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 0) hundred
cross join (select 1 as number union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 0) thousand
where (thousand.number*1000 + hundred.number*100 + ten.number*10 + one.number) > 0 and (thousand.number*1000 + hundred.number*100 + ten.number*10 + one.number) <= 2000
order by (thousand.number*1000 + hundred.number*100 + ten.number*10 + one.number)



** Please Note : Above statement will insert values from ‘1’ to ‘2000’. But removing the where condition will insert values from ‘0’ to ‘10000’.


And using the following T-SQL statement we can join the table and produce the required result.


select A.*
from ItemDetails as A
join IntermediateTable as B on B.MaxQty <= A.ItemQty
where A.BatchNo = 'B1'


screen_04