Tuesday, January 31, 2012

Schedule SSIS in SQL 2005

In the SQL Server 2005, after you create a SSIS package ( DTS), you want to create a job and schedule to run it. You will get the error to prevent you to run the job. What is the problem?

Here is why: SQL Server 2005 is quite different from SQL Server 2000. In SQL Server 2000, you can create the job and run it without problem. In SQL Server 2005, you need to go through the security layer in order to run the job.

The logic is like this:
Ø      The job executor account needs the roles of sysadmin,  SQLAgentUserRole, SQLAgentReaderRole,  SQLAgentOperatorRole
Ø      The job needs to be run under Proxy account
Ø      The job executor account is better to be used to create SSIS package and so you are sure the job executor account has the right to run this SSIS package.

The following steps can be followed to get the job done.
The work environment is MS SQL Server Management Studio and you log in as sa.

I. Create job executor account
Highlight Security->New Login, say to make login as devlogin, type your password, default database can be your target database.
Server roles: check “sysadmin
User mapping: your target database
Msdb database: you make sure to include SQLAgentUserRole, SQLAgentReaderRole,  SQLAgentOperatorRole
Then click OK

II. Create SQL proxy account and associate proxy account with job executor account
Here is the code and run it the query window.

Use master
CREATE CREDENTIAL [MyCredential] WITH IDENTITY = 'yourdomain\myWindowAccount', secret = 'WindowLoginPassword'
Use msdb
Sp_add_proxy @proxy_name='MyProxy', @credential_name='MyCredential'
Sp_grant_login_to_proxy @login_name=' devlogin', @proxy_name='MyProxy'
Sp_grant_proxy_to_subsystem @proxy_name='MyProxy', @subsystem_name='SSIS'

III. Create SSIS package
In MS SQL Server Business Intelligence Development Studio, you use job executor account devlogin to create the SSIS package (DTS) and make sure you can execute this package in SQL Server Business Intelligence Development Studio. Compile/build this package.

IV. Create the job, schedule the job and run the job
In SQL Server Management Studio, highlight SQL Server Agent -> Start. Highlight Job ->New Job…, name it , myJob.
Under Steps, New Step, name it, Step1,
Type: SQL Server Integration Service Package
Run as: myProxy
Package source: File System
Browse to select your package file xxx.dtsx
Click Ok
Schedule your job and enable it

Now you can run your job.

Batch Files

Creating a batch file

MS-DOS users
Microsoft Windows and other users

MS-DOS users

To create a basic batch file in MS-DOS, follow the below steps that give you an example of how to create a basic batch file.

    Open an MS-DOS command window or get to MS-DOS.
    At the MS-DOS prompt, type: edit test.bat and press enter.
    If typed properly, you should now be in a blue screen. Within the screen, type:

    pause
    dir c:\windows
    dir c:\windows\system

    Once the above three lines have been typed in, click File and choose exit; when prompted to save, click "Yes." Users who do not have a mouse cursor can accomplish this same task by pressing ALT+F to access the file menu, then pressing "X" to exit, and pressing enter to save changes.
    Once you are back at the MS-DOS prompt, type: test and press enter. This will execute the test.bat file and begin running the file. Because the first line is pause, you will first be prompted to press a key. Once you press a key the batch file will run line-by-line; in this case, listing the files in the windows and windows\system directories.

If you wish to add more lines to this batch file you would type "edit test.bat" to edit the file again.

Additional information about the MS-DOS edit command can be found on our edit command page. Some versions of MS-DOS and bootable diskettes may not have the edit command; if this is the case, you would either need to obtain the edit.com file to access this file or use the copy con command.

Microsoft Windows and other users

A Windows user can still use the above MS-DOS steps if they wish to create a batch file. If, however, you're more comfortable using Microsoft Windows or your operating system, you can use any text editor, such as Notepad or Wordpad, to create your batch files, as long as the file extension ends with .bat. In the below example we use the Windows notepad to create a batch file.

    Click Start
    Click Run
    Type: notepad and press enter.
    Once notepad is open, type the below lines in the file or copy and paste the below lines into notepad.

    @echo off
    echo Hello this is a test batch file
    pause
    dir c:\windows

    Click File and click Save; browse to where you want to save the file. For the file name, type "test.bat", and if your version of Windows has a "Save as type" option, choose "All files", otherwise it will save as a text file. Once all of this has been done click the Save button and exit notepad.
    Now, to run the batch file, double-click or run the file like any other program. Once the batch file has completed running it will close the window automatically.

Batch commands

Just like all MS-DOS commands, all batch file commands are not case sensitive. However, in the below listing we have listed all commands in all caps to help you identify what is a command and what is not.
@    Does not echo back the text after the at symbol. This is most commonly used as @ECHO OFF to prevent any of the commands in the batch file from being displayed, just the information needed.
%1    The percent followed by a numeric value, beginning with one, allows users to add variables within a batch file. The below line is an example of what can be used in a batch file.

ECHO Hello %1

When the above one-line batch file is created, add your name after the batch file. For example, typing myname (being the name of the bat file) and then your name:

myname bob

would output:

Hello bob

Note: This can be extended to %2, %3, and so on.
::    One of two ways of adding remarks into the batch file without displaying or executing that line when the batch file is run. Unlike REM this line will not show regardless if ECHO off is in the batch file.
:LABEL    By adding a colon in front of a word, such as LABEL, you create a category, more commonly known as a label. This allows you to skip to certain sections of a batch file such as the end of the batch file. Also see GOTO.
CALL    This is used to run another batch file within a batch file. When the batch file that is called is completed, the remainder of the original batch file is completed. Note if the batch file does not exist it will give an error message.
CHOICE    See running different programs for an example of how to use this command.

    Additional information and the syntax of this command in each version of Windows and MS-DOS can be found on our CHOICE command page.

CLS    Just like the DOS command would clear your screen.

    Additional information and the syntax of this command in each version of Windows and MS-DOS can be found on our CLS command page.

ECHO    Will echo a message in the batch file. Such as ECHO Hello World will print Hello World on the screen when executed. However, without @ECHO OFF at the beginning of the batch file you'll also get "ECHO Hello World" and "Hello World." Finally, if you'd just like to create a blank line, type ECHO. adding the period at the end creates an empty line.
EXIT    Exits out of the DOS window if the batch file is running from Windows.

    Additional information and the syntax of this command in each version of Windows and MS-DOS can be found on our EXIT command page.

GOTO LABEL    Used to go to a certain label, such as LABEL. An example of GOTO would be to GOTO END. For an example of this see running different programs.

    Additional information and the syntax of this command in each version of Windows and MS-DOS can be found on our GOTO command page.

IF    Used to check for a certain condition if the condition exists. If that condition exists it will perform that function. To see an example of this see running different programs.

    Additional information and the syntax of this command in each version of Windows and MS-DOS can be found on our IF command page.

PAUSE    Prompt the user to press any key to continue.
REM    One of two ways of adding remarks into the batch file without displaying or executing that line when the batch file is run.
SHIFT    Changes the position of replaceable parameters in a batch program.

    Shift command help and syntax information.

START    Used for Windows 95, Windows 98, and Windows NT 4.0 and above to start a windows application; such as START C:\WINDOW\CALC would run the Windows Calculator. Users running Windows 3.x need to utilize the WIN command.  For example, WIN C:\Windows\CALC.EXE would run Windows and then Calculator after Windows has finished loading.

    Start command help and syntax information.

adding logging to SSIS - step by step

  1. On the SSIS menu, click Logging.
  2. In the Configure SSIS Logs dialog box, in the Containers pane, make sure that the topmost object, which represents the Lesson 4 package, is selected.
  3. On the Providers and Logs tab, in the Provider type box, select SSIS log provider for Text files, and then click Add.
    Integration Services adds a new Text File log provider to the package with the default name SSIS log provider for text files. You can now configure the new log provider.
  4. In the Name column, type Lesson 4 Log File.
  5. Optionally, modify the Description.
  6. In the Configuration column, click <New Connection> to specify the destination to which the log information is written.
    In the File Connection Manager Editor dialog box, for Usage type, select Create file, and then click Browse. By default, the Select File dialog box opens the project folder, but you can save log information to any location.
  7. In the Select File dialog box, in the File name box type TutorialLog.log, and click Open.
  8. Click OK to close the File Connection Manager Editor dialog box.
  9. In the Containers pane, expand all nodes of the package container hierarchy, and then clear all check boxes, including the Extract Sample Currency Data check box. Now select the check box for Extract Sample Currency Data to get only the events for this node.
    Important noteImportant
    If the state of the Extract Sample Currency Data check box is dimmed instead of selected, the task uses the log settings of the parent container and you cannot enable the log events that are specific to the task.
  10. On the Details tab, in the Events column, select the PipelineExecutionPlan and PipelineExecutionTrees events.
  11. Click Advanced to review the details that the log provider will write to the log for each event. By default, all information categories are automatically selected for the events you specify.
  12. Click Basic to hide the information categories.
  13. On the Provider and Logs tab, in the Name column, select Lesson 4 Log File. Once you have created a log provider for your package, you can optionally deselect it to temporarily turn off logging, without having to delete and re-create a log provider.
  14. Click OK.

Sunday, January 29, 2012

SQL Server> Concatenate rows

http://www.simple-talk.com/sql/t-sql-programming/concatenating-row-values-in-transact-sql/

Many a time, SQL programmers are faced with a requirement to generate report-like resultsets directly from a Transact SQL query. In most cases, the requirement arises from the fact that there neither sufficient tools nor in-house expertise to develop tools that can extract the data as a resultset, and then massage the data in the desired display format. Quite often folks are confused about the potential of breaking relational fundamentals such as the First Normal Form or the scalar nature of typed values. (Talking about 1NF violations in a language like SQL which lacks sufficient domain support, allows NULLs and supports duplicates is somewhat ironic to begin with, but that is a topic which requires detailed explanations.)
By 'Concatenating row values' we mean this:
You have a table, view or result that looks like this...
...and you wish to have a resultset like the one below:
In this example we are accessing the sample NorthWind database and using the following SQL

SELECT CategoryId, ProductName
      FROM Northwind..Products
The objective is to return a resultset with two columns, one with the Category Identifier, and the other with a concatenated list of all the Product Names separated by a delimiting character: such as a comma.
Concatenating column values or expressions from multiple rows are usually best done in a client side application language, since the string manipulation capabilities of Transact SQL and SQL based DBMSs are somewhat limited. However, you can do these using different approaches in Transact SQL, but it is best to avoid such methods in long-term solutions

A core issue

Even though SQL, in general, deviates considerably from the relational model, its reliance on certain core aspects of relational foundations makes SQL functional and powerful. One such core aspect is the set based nature of SQL expressions (well, multi-sets to be exact, but for the given context let us ignore the issue of duplication). The primary idea is that tables are unordered and therefore the resultsets of any query that does not have an explicit ORDER BY clause is unordered as well. In other words, the rows in a resultset of a query do not have a prescribed position, unless it is explicitly specified in the query expression.
On the other hand, a concatenated list is an ordered structure. Each element in the list has a specific position. In fact, concatenation itself is an order-utilizing operation in the sense that values can be prefixed or post fixed to an existing list. So approaches that are loosely called “concatenating row values”, “aggregate concatenation” etc. would have to make sure that some kind of an order, either explicit or implicit, should be specified prior to concatenating the row values. If such an ordering criteria is not provided, the concatenated string would be arbitrary in nature.

Considerations

Generally, requests for row value concatenations often comes in two basic flavors, when the number of rows is known and small (typically less than 10) and when the number of rows is unknown and potentially large. It may be better to look at each of them separately.
In some cases, all the programmer wants is just the list of values from a set of rows. There is no grouping or logical partitioning of values such as  the list of email addresses separated by a semicolon or some such. In such situations, the approaches can be the same except that the join conditions may vary. Minor variations of the examples list on this page illustrate such solutions as well.
For the purpose of this article the Products table from Northwind database is used to illustrate column value concatenations with a grouping column. Northwind is a sample database in SQL Server 2000 default installations. You can download a copy from from the Microsoft Downloads

Concatenating values when the number of items is small and known beforehand


When the number of rows is small and almost known beforehand, it is easier to generate the code. One common approach where there is a small set of finite rows is the pivoting method. Here is an example where only the first four alphabetically-sorted product names per categoryid is retrieved:
   SELECT CategoryId,
           MAX( CASE seq WHEN 1 THEN ProductName ELSE '' END ) + ', ' +
           MAX( CASE seq WHEN 2 THEN ProductName ELSE '' END ) + ', ' +
           MAX( CASE seq WHEN 3 THEN ProductName ELSE '' END ) + ', ' +
           MAX( CASE seq WHEN 4 THEN ProductName ELSE '' END )
      FROM ( SELECT p1.CategoryId, p1.ProductName,
                    ( SELECT COUNT(*)
                        FROM Northwind.dbo.Products p2
                        WHERE p2.CategoryId = p1.CategoryId
                        AND p2.ProductName <= p1.ProductName )
             FROM Northwind.dbo.Products p1 ) D ( CategoryId, ProductName, seq )
     GROUP BY CategoryId ;
 The idea here is to create a expression inside the correlated subquery that produces a rank (seq) based on the product names and then use it in the outer query. Using common table expressions and the ROW_NUMBER() function, you can re-write this as:
; WITH CTE ( CategoryId, ProductName, seq )
     AS ( SELECT p1.CategoryId, p1.ProductName,
            ROW_NUMBER() OVER ( PARTITION BY CategoryId ORDER BY ProductName )
           FROM Northwind.dbo.Products p1 )
SELECT CategoryId,
           MAX( CASE seq WHEN 1 THEN ProductName ELSE '' END ) + ', ' +
           MAX( CASE seq WHEN 2 THEN ProductName ELSE '' END ) + ', ' +
           MAX( CASE seq WHEN 3 THEN ProductName ELSE '' END ) + ', ' +
           MAX( CASE seq WHEN 4 THEN ProductName ELSE '' END )
     FROM CTE
     GROUP BY CategoryId ;
 Note that ROW_NUMBER() is a newly-introduced feature in SQL 2005. If you are using any previous version, you will have to use the subquery approach (You can also use a self-join, to write it a bit differently). Using the recently introduced PIVOT operator, you can write this as follows:
SELECT CategoryId,
           "1" + ', ' + "2" + ', ' + "3" + ', ' + "4" AS Product_List
      FROM ( SELECT CategoryId, ProductName,
                    ROW_NUMBER() OVER (PARTITION BY CategoryId
             ORDER
BY ProductName)
               FROM Northwind.dbo.Products ) P ( CategoryId, ProductName, seq )
     PIVOT ( MAX( ProductName ) FOR seq IN ( "1", "2", "3", "4" ) ) AS P_ ;
Not only does the syntax appear a bit confusing, but also it does not seem to offer any more functionality than the previous CASE approach. However, in rare situations, it could come in handy.

Concatenating values when the number of items is not known

When you do not know the number of items that are to be concatenated beforehand, the code can become rather  more demanding. The new features in SQL 2005 make some of the approaches easier. For instance, the recursive common table expressions (CTEs) and the FOR XML PATH('') syntax makes the server do the hard work behind the concatenation, leaving the programmer to deal with the presentation issues. The examples below make this point obvious.

Recursive CTE methods

The idea behind this method is from a newsgroup posting by Vadim Tropashko. It is similar to the ideas behind generating a materialized path for hierarchies.
WITH CTE ( CategoryId, product_list, product_name, length )
          AS ( SELECT CategoryId, CAST( '' AS VARCHAR(8000) ), CAST( '' AS VARCHAR(8000) ), 0
                 FROM Northwind..Products
                GROUP BY CategoryId
                UNION ALL
               SELECT p.CategoryId, CAST( product_list +
                      CASE WHEN length = 0 THEN '' ELSE ', ' END + ProductName AS VARCHAR(8000) ),
                      CAST( ProductName AS VARCHAR(8000)), length + 1
                 FROM CTE c
                INNER JOIN Northwind..Products p
                   ON c.CategoryId = p.CategoryId
                WHERE p.ProductName > c.product_name )
SELECT CategoryId, product_list
      FROM ( SELECT CategoryId, product_list,
                    RANK() OVER ( PARTITION BY CategoryId ORDER BY length DESC )
               FROM CTE ) D ( CategoryId, product_list, rank )
     WHERE rank = 1 ;
 The CASE in the recursive part of the CTE is used to eliminate the initial comma, but you can use RIGHT or the SUBSTRING functions instead. This may not be the best performing option, but certain additional tuning could be done to make them suitable for medium sized datasets.
Another approach using recursive common table expressions was sent in by Anub Philip, an Engineer from Sathyam Computers that uses separate common table expressions for the anchor and recursive parts.
WITH Ranked ( CategoryId, rnk, ProductName ) 
             AS ( SELECT CategoryId,
                         ROW_NUMBER() OVER( PARTITION BY CategoryId ORDER BY CategoryId ),
                         CAST( ProductName AS VARCHAR(8000) )
                    FROM Northwind..Products),
   AnchorRanked ( CategoryId, rnk, ProductName ) 
             AS ( SELECT CategoryId, rnk, ProductName
                    FROM Ranked
                   WHERE rnk = 1 ),
RecurRanked ( CategoryId, rnk, ProductName )
             AS ( SELECT CategoryId, rnk, ProductName
                    FROM AnchorRanked
                   UNION ALL
                  SELECT Ranked.CategoryId, Ranked.rnk,
                         RecurRanked.ProductName + ', ' + Ranked.ProductName
                    FROM Ranked
                   INNER JOIN RecurRanked
                      ON Ranked.CategoryId = RecurRanked.CategoryId
                     AND Ranked.rnk = RecurRanked.rnk + 1 )
SELECT CategoryId, MAX( ProductName )
      FROM RecurRanked
  GROUP BY CategoryId;
On first glance, this query may seem a bit expensive in comparison, but the reader is encouraged to check the execution plans and make any additional tweaks as needed.

The blackbox XML methods

Here is a technique for string concatenation that uses the FOR XML clause with PATH mode. It was initially posted by Eugene Kogan, and later became common in public newsgroups.
SELECT p1.CategoryId,
       ( SELECT ProductName + ','
           FROM Northwind.dbo.Products p2
          WHERE p2.CategoryId = p1.CategoryId
          ORDER BY ProductName
            FOR XML PATH('') ) AS Products
      FROM Northwind.dbo.Products p1
      GROUP BY CategoryId ;
There is a similar approach that was originally found in the beta newsgroups, using the CROSS APPLY operator.
SELECT DISTINCT CategoryId, ProductNames
    FROM Northwind.dbo.Products p1
   CROSS APPLY ( SELECT ProductName + ','
                     FROM Northwind.dbo.Products p2
                     WHERE p2.CategoryId = p1.CategoryId
                     ORDER BY ProductName
                     FOR XML PATH('') )  D ( ProductNames )
You may notice a comma at the end of the concatenated string, which you can remove using a STUFF, SUBSTRING or LEFT function. While the above methods are deemed reliable by many at the time of writing, there is no guarantee that it will stay that way, given that the internal workings and evaluation rules of FOR XML PATH() expression in correlated subqueries are not well documented.

Using Common Language Runtime

Though this article is about approaches using Transact SQL, this section is included due to the popularity of CLR aggregates in SQL 2005. It not only empowers the CLR programmer with new options for database development, but also, in some cases, they work at least as well as native Transact SQL approaches.
If you are familiar with .NET languages, SQL 2005 offers a convenient way to create user defined aggregate functions using C#, VB.NET or similar languages that are supported by the Common Language Runtime (CLR). Here is an example of a string concatenate aggregate function written using C#.
using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.IO;
using Microsoft.SqlServer.Server;

[Serializable]
[SqlUserDefinedAggregate(Format.UserDefined,  MaxByteSize=8000)]
public struct strconcat : IBinarySerialize{
        private List values;

        public void Init()    {
            this.values = new List();
        }

        public void Accumulate(SqlString value)    {
            this.values.Add(value.Value);
        }

        public void Merge(strconcat value)    {
            this.values.AddRange(value.values.ToArray());
        }

        public SqlString Terminate()    {
            return new SqlString(string.Join(", ", this.values.ToArray()));
        }

        public void Read(BinaryReader r)    {
            int itemCount = r.ReadInt32();
            this.values = new List(itemCount);
            for (int i = 0; i <= itemCount - 1; i++)    {
                this.values.Add(r.ReadString());
            }
        }

        public void Write(BinaryWriter w)    {
            w.Write(this.values.Count);
            foreach (string s in this.values)      {
                w.Write(s);
            }
        }
}
Once you build and deploy this assembly on the server, you should be able to execute your concatenation query as:
SELECT CategoryId,
           dbo.strconcat(ProductName)
      FROM Products
     GROUP BY CategoryId ;

 If you are a total newbie on CLR languages, and would like to learn more about developing database solutions using CLR languages, consider starting at Introduction to Common Language Runtime (CLR) Integration

Scalar UDF with recursion

Recursive functions in t-SQL have a drawback that the maximum nesting level is 32. So this approach is applicable only for smaller datasets, especially when the number of items within a group, that needs to be concatenated, is less than 32.

CREATE FUNCTION udf_recursive ( @cid INT, @i INT )
RETURNS VARCHAR(8000) AS BEGIN
        DECLARE @r VARCHAR(8000), @l VARCHAR(8000)
        SELECT @i = @i - 1,  @r = ProductName + ', '
          FROM Northwind..Products p1
         WHERE CategoryId = @cid
           AND @i = ( SELECT COUNT(*) FROM Northwind..Products p2
                       WHERE p2.CategoryId = p1.CategoryId
                         AND p2.ProductName <= p1.ProductName ) ;
        IF @i > 0 BEGIN
              EXEC @l = dbo.udf_recursive @cid, @i ;
              SET @r =  @l + @r ;
END
RETURN @r ;
END
This function can be invoked as follows:
SELECT CategoryId,
           dbo.udf_recursive( CategoryId, COUNT(ProductName) )
      FROM Northwind..Products
     GROUP BY CategoryId ;

Table valued UDF with a WHILE loop

This approach is based on the idea by Linda Wierzbecki where a table variable with three columns is used within a table-valued UDF. The first column represents the group, second represents the currently processing value within a group and the third represents the concatenated list of values.

CREATE FUNCTION udf_tbl_Concat() RETURNS @t TABLE(
            CategoryId INT,
            Product VARCHAR(40),
            list VARCHAR(8000) )
BEGIN
     INSERT @t (CategoryId, Product, list)
     SELECT CategoryId, MIN(ProductName),  MIN(ProductName)
       FROM Products
      GROUP BY CategoryId
WHILE ( SELECT COUNT(Product) FROM @t ) > 0 BEGIN
        UPDATE t
           SET list = list + COALESCE(
                         ( SELECT ', ' + MIN( ProductName )
                             FROM Northwind..Products
                            WHERE Products.CategoryId = t.CategoryId
                              AND Products.ProductName > t.Product), ''),
               Product = ( SELECT MIN(ProductName)
                             FROM Northwind..Products
                            WHERE Products.CategoryId = t.CategoryId
                              AND Products.ProductName > t.Product )
          FROM @t t END
RETURN
END
 The usage of the above function can be like:
SELECT CategoryId, list AS Products
  FROM udf_tbl_Concat() ;

 Dynamic SQL

This approach is a variation of the kludge often known using the nickname of  ‘dynamic cross tabulation’. There is enough literature out there which demonstrates the drawbacks and implications of using Dynamic SQL. A popular one, at least from Transact SQL programmer’s perspective, is Erland's Curse and Blessings of Dynamic SQL. The Dynamic SQL approaches can be developed based on creating a Transact SQL query string based on the number of groups and then use a series of CASE expressions or ROW_NUMBER() function to pivot the data for concatenation.
DECLARE @r VARCHAR(MAX), @n INT, @i INT
SELECT @i = 1,
       @r = 'SELECT CategoryId, ' + CHAR(13),
       @n = (SELECT TOP 1 COUNT( ProductName )
                   FROM Northwind..Products
                  GROUP BY CategoryId
                  ORDER BY COUNT( ProductName ) DESC ) ;     
WHILE @i <= @n BEGIN
           SET @r = @r +
           CASE WHEN @i =
                THEN 'MAX( CASE Seq WHEN ' + CAST( @i AS VARCHAR ) + '
                                 THEN ProductName
                                            ELSE SPACE(0) END ) + ' + CHAR(13)
           WHEN @i = @n
             THEN 'MAX( CASE Seq WHEN ' + CAST( @i AS VARCHAR ) + '
                                 THEN '', '' + ProductName
                                 ELSE SPACE(0) END ) ' + CHAR(13)
             ELSE 'MAX( CASE Seq WHEN ' + CAST( @i AS VARCHAR ) + '
                                 THEN '', '' + ProductName
                                 ELSE SPACE(0) END ) + ' + CHAR(13) 
           END ;
           SET @i = @i + 1 ;
END
SET @r = @r + '
    FROM ( SELECT CategoryId, ProductName,
                  ROW_NUMBER() OVER ( PARTITION BY CategoryId ORDER BY ProductName )
             FROM Northwind..Products p ) D ( CategoryId, ProductName, Seq )
           GROUP BY CategoryId;'
EXEC( @r ) ;

 The Cursor approach

The drawbacks of rampant usage of cursors are well-known among the Transact SQL community. Because they are generally resource intensive, procedural and inefficient, one should strive to avoid cursors or loop based solutions in general Transact SQL programming.
DECLARE @tbl TABLE (id INT PRIMARY KEY, list VARCHAR(8000))
SET NOCOUNT ON
DECLARE @c INT, @p VARCHAR(8000), @cNext INT, @pNext VARCHAR(40)
DECLARE c CURSOR FOR
        SELECT CategoryId, ProductName
          FROM Northwind..Products
         ORDER BY CategoryId, ProductName ;
        OPEN c ;
        FETCH NEXT FROM c INTO @cNext, @pNext ;
        SET @c = @cNext ;
        WHILE @@FETCH_STATUS = 0 BEGIN
             IF @cNext > @c BEGIN
                  INSERT @tbl SELECT @c, @p ;
                  SELECT @p = @PNext, @c = @cNext ;
             END ELSE
                  SET @p = COALESCE(@p + ',', SPACE(0)) + @pNext ;
             FETCH NEXT FROM c INTO @cNext, @pNext
        END
        INSERT @tbl SELECT @c, @p ;
        CLOSE c ;
DEALLOCATE c ;
SELECT * FROM @tbl ;

 Unreliable approaches

This section details a couple of notorious methods often publicized by some in public forums. The problem with these methods is that they rely on the physical implementation model; changes in indexes, statistics etc or even a change of a simple expression in the SELECT list or ORDER BY clause can change the output. Also these are undocumented, unsupported and unreliable to the point where one can consistently demonstrate failures. Therefore these methods are not recommended at all for production mode systems.

Scalar UDF with t-SQL update extension

It is rare for the usage of an expression that involves a column, a variable and an expression in the SET clause in an UPDATE statement to appear intuitive. However, in general, the optimizer often seems to process these values in the order of materialization, either in the internal work tables or any other storage structures.
CREATE FUNCTION udf_update_concat (@CategoryId INT)
        RETURNS VARCHAR(MAX) AS
BEGIN
DECLARE @t TABLE(p VARCHAR(40));
DECLARE @r VARCHAR(MAX) ;
        SET @r = SPACE(0) ;
        INSERT @t ( p ) SELECT ProductName FROM Northwind..Products
                         WHERE CategoryId = @CategoryId ;
        IF @@ROWCOUNT > 0
            UPDATE @t
               SET @r = @r + p + ',' ;
        RETURN(@r)
END
Here is how to use this function:
SELECT CategoryId, dbo.udf_update_concat(CategoryId)
      FROM Northwind..Products
     GROUP BY CategoryId ;

Again, it is important to consider that lack of physical independence that is being exploited here before using or recommending this as a usable and meaningful solution.

Scalar UDF with variable concatenation in SELECT

This is an approach purely dependent on the physical implementation and internal access paths. Before using this approach, make sure to refer to the relevant knowledgebase article.
CREATE FUNCTION dbo.udf_select_concat ( @c INT )
RETURNS VARCHAR(MAX) AS BEGIN
DECLARE @p VARCHAR(MAX) ;
           SET @p = '' ;
        SELECT @p = @p + ProductName + ','
          FROM Northwind..Products
         WHERE CategoryId = @c ;
RETURN @p
END
And, as for its usage:
SELECT CategoryId, dbo.udf_select_concat( CategoryId )
      FROM Northwind..Products
     GROUP BY CategoryId ;

Conclusion

Regardless of how it is used, "aggregate concatenation" of row values in Transact SQL, especially when there is a grouping, is not a simple routine. You need to consider carefully the circumstances  before you choose one method over another.  The most logical choice would to have a built-in operator with optional configurable parameters that can do the concatenation of the values depending on the type. Till then, reporting requirements and external data export routines will have to rely on such Transact SQL programming hacks.

Tuesday, January 24, 2012

Java Decompliler for Eclipse

http://java.decompiler.free.fr/?q=jdeclipse


JD-Eclipse

JD-Eclipse is a plug-in for the Eclipse platform. It allows you to display all the Java sources during your debugging process, even if you do not have them all.
JD-Eclipse is free for non-commercial use. This means that JD-Eclipse shall not be included or embedded into commercial software products. Nevertheless, this project may be freely used for personal needs in a commercial or non-commercial environments.

Supported Platforms

  • Windows 32/64-bit
  • Linux 32/64-bit
  • Mac OSX 32/64-bit on x86 hardware

Installation 

Windows Platform Prerequisites

The native library, included into JD-Eclipse for Windows, has been built with Microsoft Visual C++ 2008 Express Edition. Some runtime components of Visual C++ Libraries are required to run the decompiler. You can download and install them from the Microsoft Web site :
Microsoft Visual C++ 2008 SP1 Redistributable Package (x86)
Microsoft Visual C++ 2008 SP1 Redistributable Package (x64)

Eclipse 3.6 Instructions

  1. From the Help menu in Eclipse, select Install New Software... to open an Install dialog window (shown below).


  2. Add a new repository to add JD-Eclipse plug-in:
    1. Click on the Add... button to open the New Repository dialog window.
    2. Type JD-Eclipse Update Site in the site Name text box.
    3. In the URL text box, type the URL for the JD-Eclipse update site: http://java.decompiler.free.fr/jd-eclipse/update (shown below) and click OK.


  3. Check boxes "Java Decompiler Eclipse Plug-in" and "JD-Eclipse Plug-in", and click on Next buttons.
  4. Choose the option to accept the terms of the license agreement (shown below).


  5. Click on the Finish button.
  6. On the Selection Needed dialog window, click on the Select All and Ok buttons.
  7. Restart Eclipse.

Eclipse 3.2, 3.3, 3.5 Instructions

  1. From the Help menu in Eclipse, select Software Updates > Find and Install... to open the Install/Update dialog window (shown below).


  2. On the Install/Update pop-up window, choose the Search for new features to install option, and click on the Next button.
  3. Add a new remote site to add JD-Eclipse plug-in:
    1. Click the New Remote Site... button to open a New Update Site dialog window.
    2. On the New Update Site pop-up window, type JD-Eclipse Update Site in the site Name text box.
    3. In the URL text box, type the URL for the JD-Eclipse update site: http://java.decompiler.free.fr/jd-eclipse/update (shown below) and click OK.


    4. Click on the Finish button to switch to the Updates window.
  4. On the Updates window, check the JD-Eclipse Update Site box (shown below), and click the Finish button.


  5. On the next screen, check the JD-Eclipse Update Site box, and click the Next button.
  6. Choose the option to accept the terms of the license agreement, and click on the Next button.
  7. Click on the Finish button.
  8. Click on the Install All button.
Eclipse installs the JD-Eclipse plug-in. To finish the installation process, follow the prompts to shut down and re-start Eclipse.

Eclipse 3.4 Instructions

Installation of Equinox/p2 plug-in

  1. From the Help menu in Eclipse, select Software Updates... to open an Software Updates and Add-ons pop-up window (shown below).


  2. Select the Available Software tab.
  3. Expand Ganymede tree node.
  4. Expand Uncategorized tree node.
  5. Check Equinox p2 Provisioning tree node (shown below), and click the Install... button.


  6. Click the Finish button.

Installation of JD-Eclipse plug-in

  1. From the Help menu in Eclipse, select Software Updates... to open an Software Updates and Add-ons pop-up window.
  2. Select the Available Software tab.
  3. Add a new remote site to add JD-Eclipse plug-in:
    1. Click the Add Site... button to open a Add Site pop-up window.
    2. In the Location text box, type the URL for the JD-Eclipse update site: http://java.decompiler.free.fr/jd-eclipse/update (shown below) and click OK.


  4. On the Software Updates and Add-ons window, check the JD-Eclipse Plug-in box (shown below), and click the Install... button.


  5. On the next screen, click the Finish button.
  6. On the next screen, check the Java Decompiler Eclipse Plug-in certificate box and click the OK button.
Eclipse installs the JD-Eclipse plug-in. To finish the installation process, follow the prompts to shut down and re-start Eclipse