Parsing Parameters in a Stored Procedure

  • Be careful with the code in the article. Be aware that it doesn't do full validation. It only evaluates what's right and give no indication as to what is wrong. If that's good for you, so be it (although I believe there are better ways). Just make sure it's good for you. ๐Ÿ˜‰

    The following example has been copied from the article code. "XX" has been added to the parameter. See for yourself that "XX" produces no error and no alert because "XX" isn't even considered in the validation.

    -- Imagine this table exists previously in your database ----

    Declare @T_STATES table ( state char(2) )

    Insert @T_STATES Values ( 'AZ' )

    Insert @T_STATES Values ( 'CA' )

    Insert @T_STATES Values ( 'MT' )

    Insert @T_STATES Values ( 'TX' )

    Insert @T_STATES Values ( 'WA' )

    Insert @T_STATES Values ( 'TS' )

    -- Suppose this is the parameter ----

    Declare @sParameter varchar(8000)

    Set @sParameter = 'WA,AZ,CA,TS,XX' --<<< Code doesn't catch the invalid parameter

    Declare @tStates table ( state char(2) )

    Insert @tStates

    Select state

    From @T_STATES

    Where CharIndex( state, @sParameter ) > 0

    Select *

    From @tStates

    Even the final bit of code doesn't catch the fact that an invalid element was passed...

    -- Imagine this table exists previously in your database ----

    Declare @T_STATES table ( state char(2) )

    Insert @T_STATES Values ( 'AZ' )

    Insert @T_STATES Values ( 'CA' )

    Insert @T_STATES Values ( 'MT' )

    Insert @T_STATES Values ( 'TX' )

    Insert @T_STATES Values ( 'WA' )

    Insert @T_STATES Values ( 'TS' )

    -- Suppose this is the parameter ----

    Declare @sParameter varchar(8000)

    Set @sParameter = 'WA,AZ,CA,TS,XX' --<<< Code doesn't catch the invalid parameter

    Declare @tStates table ( state char(2) )

    Insert @tStates

    Select state

    From @T_STATES

    Where CharIndex( state, @sParameter ) > 0

    If @@RowCount < 1

    Begin

    Select 'No matches. Valid values are:' As InfoMessage

    Union All

    Select state

    From @T_STATES

    Return

    End

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • something like:

    set @XMLParameters =

    '<OrderGroup>

    <Item TargetPrinterType="DIAMOND_X" GroupStatus="ACTIVE_cc" ReferenceNo="Joe" />

    </OrderGroup>'

  • With SQL2008 you do have the option of a table variable. I was using XML and found that the table variable provided all the flexibility but was much faster.

  • tom.groszko (8/10/2010)


    With SQL2008 you do have the option of a table variable. I was using XML and found that the table variable provided all the flexibility but was much faster.

    Any chance of you posting some code that others can run to demonstrate that?

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • jcboyer-1091017 (8/10/2010)


    something like:

    set @XMLParameters =

    '<OrderGroup>

    <Item TargetPrinterType="DIAMOND_X" GroupStatus="ACTIVE_cc" ReferenceNo="Joe" />

    </OrderGroup>'

    Thanks, Jerome...

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • TheSQLGuru (8/10/2010)


    Didn't we have a MASSIVE thread here on SSC.com on string parsing, with amazing code samples and wonderful benchmarks??

    We did. I tried to voice an objection about using all the same data for all the rows in any given test table (delimiters ALL in the same position causes lightning speed due to the way things are cached) but no one listened. All is not as it seems on that thread because of the test data that was used. As a result, I'd recommend retesting any code found in that thread before you use it.

    --Jeff Moden


    RBAR is pronounced "ree-bar" and is a "Modenism" for Row-By-Agonizing-Row.
    First step towards the paradigm shift of writing Set Based code:
    ________Stop thinking about what you want to do to a ROW... think, instead, of what you want to do to a COLUMN.

    Change is inevitable... Change for the better is not.


    Helpful Links:
    How to post code problems
    How to Post Performance Problems
    Create a Tally Function (fnTally)

  • I see what you are saying Jeff, but would you want to do validation if the application did not allow the end user to send invalid data? perhaps using a drop down list.

    Kevin G. Boles

    Did you find the link you were thinking about, I would love read the massive discussion .

  • Jeff Moden (8/10/2010)


    ...

    For all of you good folks using XML to split things... I suppose that XML either does something special for you or that your consider its performance to be good enough. Just be aware that there are faster methods than XML... sometimes a lot faster.

    ...

    Using the value() and nodes() method of the XML datatype is on par with other techniques performance wise, assuming you parse it only once, but it probably depends on the complexity of the xml string. Of course, regardless of the method used to pass the data to the stored procedure, the first step should be to get the data into a table variable. Joining an XML variable or UDF in a SQL SELECT, in such a way that it gets called interatively, is where the big performance hit occurs.

    In many cases, the data being passed to the stored procedure parameter orginates as an XML document in the application, so passing the XML as is eliminates the need to parse and transform on the application tier. The comma seperated string method simply doesn't allow for any complexity.

    Another thing to consider about the XML variable method is that it's simple, clean, and standard. You can parse your parameter using a single line of code using SQL Server supplied methods, and the next developer who inherits your code won't have to study or tweak the programming in whatever UDF or CRL function would otherwise have been used to parse comma seperated data.

    declare @customers xml; select @customers = '<id>120</id><id>98</id><id>157</id>';

    select customers.id.value('.','int') as customer_id from @customers.nodes('id') as customers(id);

    -----------

    120

    98

    157

    I've seen some ugly and buggy examples of multiple valued list programming. What's the worst is when a UDF is not even employed but rather a block of LBAL stuff is copied and customized in 20 different stored procedures.

    "Do not seek to follow in the footsteps of the wise. Instead, seek what they sought." - Matsuo Basho

  • I would like to add my $0.02 - XML data type parameter looks even better considering the fact that it could be STRONGLY TYPED (bound to a particular XML schema). Plus in SQL 2008 you could pass a table as parameter!

  • I like you solution ๐Ÿ™‚ simply and easy ๐Ÿ™‚

    But I've a question.

    How can I get those values those are not in State table?

  • I still like the xml version it's so easy even in SSRS

  • I prefer to use XML for this type of problem. However, i would only do this when the number of parameter values is relatively small.

    Think great, be great!

  • I do agree, the XML are simpler, and I use them for my new system.

    but we also maintain old systems and replace XML instead comma delimited parameters means:

    1.- Fix code for every Web Services at .Net

    2.- Recompile

    3.- Fix every java function at Client Side

    4.- Reset the IIS

    5.- Fix Stored Procedures at SQL

    Implementing this kind solution means only to fix code in the Stored Procedures

  • A simple example of using a table valued parameter from C#.

    USE AdventureWorks;

    go

    CREATE SCHEMA Demo;

    GO

    IF EXISTS(SELECT 1 FROM sys.types WHERE name = 'PersonTable' AND is_table_type = 1 AND SCHEMA_ID('Demo') = schema_id)

    DROP TYPE Demo.PersonTable;

    go

    CREATE TYPE Demo.PersonTable AS TABLE

    ( FirstNameVARCHAR(255)

    ,MiddleNameVARCHAR(255)

    ,LastNameVARCHAR(255)

    );

    GO

    IF EXISTS (SELECT 1 FROM sys.objects WHERE OBJECT_ID = OBJECT_ID('Demo.SearchForPeople') AND TYPE = 'P ')

    DROP PROCEDURE Demo.SearchForPeople;

    GO

    CREATE PROCEDURE Demo.SearchForPeople

    ( @Person Demo.PersonTable READONLY

    )

    AS

    SET NOCOUNT ON;

    SELECT ContactID

    ,EmailAddress

    FROM Person.ContactContact

    JOIN @PersonPersonON Contact.FirstName =Person.FirstName

    AND Contact.MiddleName= Person.MiddleName

    AND Contact.LastName= Person.LastName;

    RETURN 0;

    GO

    {try

    {System.Data.SqlClient.SqlConnectionConnection=null;

    System.Data.SqlClient.SqlConnectionStringBuilderConnectionStringBuilder=null;

    System.Data.SqlClient.SqlCommandSQLCommand=null;

    System.Data.SqlClient.SqlDataReaderSQLReader=null;

    DataTablePersonTable=null;

    ConnectionStringBuilder = new SqlConnectionStringBuilder();

    ConnectionStringBuilder.DataSource= @"TOMGROSZKO-PC\SQL2008R2";

    ConnectionStringBuilder.InitialCatalog= "AdventureWorks";

    ConnectionStringBuilder.IntegratedSecurity=true;

    Connection = new SqlConnection(ConnectionStringBuilder.ConnectionString);

    PersonTable = new DataTable("PersonTable");

    PersonTable.Columns.AddRange(new System.Data.DataColumn[]

    { new System.Data.DataColumn("FirstName",typeof(String))

    ,new System.Data.DataColumn("MiddleName",typeof(String))

    ,new System.Data.DataColumn("LastName",typeof(String))

    }

    );

    PersonTable.Rows.Add(new object[] {"Catherine", "R.", "Abel"});

    PersonTable.Rows.Add(new object[] {"Frances", "B.", "Adams"});

    PersonTable.Rows.Add(new object[] {"Gregory", "F.", "Alderson"});

    PersonTable.Rows.Add(new object[] {"Ramona", "J.", "Antrim"});

    PersonTable.Rows.Add(new object[] {"Robert", "A.", "Avalos"});

    Connection.Open();

    SQLCommand = Connection.CreateCommand();

    SQLCommand.CommandText = "Demo.SearchForPeople";

    SQLCommand.CommandType = CommandType.StoredProcedure;

    SQLCommand.Parameters.Add ("@Person", System.Data.SqlDbType.Structured);

    intPARMPerson= SQLCommand.Parameters.Count -1;

    SQLCommand.Parameters[PARMPerson].Direction= System.Data.ParameterDirection.Input;

    SQLCommand.Parameters[PARMPerson].IsNullable= false;

    SQLCommand.Parameters[PARMPerson].Value= PersonTable;

    intCOLContactID =System.Int32.MinValue;

    intCOLEmailAddress =System.Int32.MinValue;

    SQLReader = SQLCommand.ExecuteReader();

    boolReaderState = SQLReader.Read();

    while(ReaderState == true)

    {if(COLContactID==System.Int32.MinValue)

    {COLContactID =SQLReader.GetOrdinal("ContactID");

    COLEmailAddress =SQLReader.GetOrdinal("EmailAddress");

    }

    Int32?ContactID =(Int32?) SQLReader.GetSqlInt32 (COLContactID);

    StringEmailAddress=(String) SQLReader.GetSqlString (COLEmailAddress);

    Console.WriteLine (ContactID.ToString() + " " + EmailAddress);

    ReaderState = SQLReader.Read();

    }

    Connection.Close();

    }

    catch (Exception OOPS)

    {Console.WriteLine(OOPS.Message);

    throw;

    }

    finally

    {

    }

    }

  • Appreciate the post - but solution is much too limiting (and not worth implementing).

    Table valued parameters are a good alternative.

    XML is a good alternative

Viewing 15 posts - 16 through 30 (of 124 total)

You must be logged in to reply to this topic. Login to reply