-1

I have found numerous posts on this but I haven't been able to make any of them work. The code below is the closest I have come to making it work. I read out the values for the ddlSIPA listbox below and the result looks correct but it seems SQL server isn't handling the IN statement for the listbox items.

public void LoadChecklist(Object sender, EventArgs e)
{       
 System.Data.DataTable SearchResultsTable = new System.Data.DataTable();
    SqlCommand cmd = new SqlCommand("sp_get_QUADRA_CHECKLIST", conn);
    cmd.CommandType = CommandType.StoredProcedure;
    //create sql adapter by passing command object
    SqlDataAdapter adapter = new SqlDataAdapter(cmd);

    SearchResultsTable.Clear();

    string strYourIDs = "";
    int[] yourSelectedIndexes = ddlSIPA.GetSelectedIndices();
    for (int i = yourSelectedIndexes.Length - 1; i >= 0; i--)
    {
        strYourIDs += "'" + ddlSIPA.Items[yourSelectedIndexes[i]].Value + "',";
    }
    if (strYourIDs != "")
        strYourIDs = strYourIDs.TrimEnd(",".ToCharArray());
    try
    {
        cmd.Parameters.AddWithValue("@SIPA", strYourIDs);
        Response.Write(strYourIDs);
        cmd.Parameters.AddWithValue("@AP_DEV", CbAPDev.Checked);
        cmd.Parameters.AddWithValue("@PROD_DEV", cbProdDev.Checked);
        cmd.Parameters.AddWithValue("@ROTYPE",                                  ddlROTYPE.SelectedItem.Value);
        adapter.Fill(SearchResultsTable);
        if (SearchResultsTable.Rows.Count > 0)
        {
            //SearchResultsTable.ToString();
            GV1.DataSource = SearchResultsTable;
            GV1.DataBind();
        }
        else if (SearchResultsTable.Rows.Count == 0)
        {
            //Response.Write("No records found!");
            ScriptManager.RegisterStartupScript(this, GetType(), "showalert", "alert('No records found!');", true);
            GV1.DataBind();
        }
    }
    catch (System.Data.SqlClient.SqlException ex)
    {
        Response.Write(ex);
    }
    finally
    {
        conn.Close();
    }
}

SQL Query:

USE [VISIBILITY_BOARD]
GO


SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER OFF
GO



ALTER PROCEDURE [dbo].[sp_get_QUADRA_CHECKLIST] (
    @AP_DEV bit ''
   ,@PROD_DEV bit = ''
   ,@ROTYPE nvarchar(255) = ''
   ,@SIPA nvarchar(255) = '') AS
 --,@RO nvarchar(255) = '') AS


SELECT h.QUES_ANSWER
, h.COMMENTS
, cl.RO_TYPE
, cl.RO
, cl.QUES_ID
, cl.DFQRO AS QDRO
, cl.QUADRA_QUES

FROM Tbl_QUADRA_CL cl
LEFT JOIN TBL_QUADRA_ASSMNT_HIST h
ON cl.QUES_ID = h.QUES_ID 
WHERE (cl.RO_TYPE = @ROTYPE OR @ROTYPE IS NULL)
AND (cl.SIPA IN (@SIPA) OR @SIPA IS NULL)
AND (cl.AP_DEV = @AP_DEV OR @AP_DEV IS NULL)
AND (cl.PROD_DEV = @PROD_DEV or @PROD_DEV IS NULL)
GROUP BY h.QUES_ANSWER
, h.COMMENTS
, cl.RO_TYPE
, cl.RO
, cl.QUES_ID
, cl.DFQRO
, cl.QUADRA_QUES

SET QUOTED_IDENTIFIER On
GO
  • You can't pass a list like that. A table valued parameter is the best approach for this. Otherwise you will be forced to use dynamic sql. Also, you should consider a different prefix for your procedures, or even better, no prefix at all. The sp_ prefix is reserved and can cause issues. http://sqlperformance.com/2012/10/t-sql-queries/sp_prefix – Sean Lange Nov 30 '17 at 15:08
  • Another note of caution, this appears to be a type of catch all query with optional parameters. Your code will not handle these correctly because the only way those parameters can be null is if you pass DBNull from your front end. – Sean Lange Nov 30 '17 at 15:09
  • Possible duplicate of [Passing a varchar full of comma delimited values to a SQL Server IN function](https://stackoverflow.com/questions/878833/passing-a-varchar-full-of-comma-delimited-values-to-a-sql-server-in-function) – Tab Alleman Nov 30 '17 at 15:20
  • improved formatting of question – Matt Spinks Nov 30 '17 at 19:24

1 Answers1

0

Here is your problem: AND (cl.SIPA IN (@SIPA) OR @SIPA IS NULL)

You are making a very common mistake - The IN operator expects a list of values separated by a comma, but you are giving it a single value that happens to contain a comma-separated list.

Since you are using and , I would advise to use a table valued parameter instead.

Please note that there are also some other problems in your code:

  • Using a class level SQLConnection - That's a mistake. A correct use of SQLConnection would be as a local variable inside a using statement.
  • Using instances of classes that implements the IDisposable interface and not disposing them - SQLCommand and SQLDataAdapter in your case.
  • Using AddWithValue - Read Can we stop using AddWithValue() already? for details.

A better c# code would look more like this:

public void LoadChecklist(Object sender, EventArgs e)
{
    var SearchResultsTable = new DataTable();
    using (var con = new SqlConnection("<ConnectionStringGoesHere>"))
    {
        using (var cmd = new SqlCommand("sp_get_QUADRA_CHECKLIST", con))
        {
            cmd.CommandType = CommandType.StoredProcedure;
            using(var adapter = new SqlDataAdapter(cmd))
            {
                using(var dtSIPA = new DataTable())
                {
                    dtSIPA.Columns.Add("Id", typeof(int)); -- assuming you are looking for a list of int values
                    int[] yourSelectedIndexes = ddlSIPA.GetSelectedIndices();
                    for (int i = yourSelectedIndexes.Length - 1; i >= 0; i--)
                    {
                        dtSIPA.Rows.Add(ddlSIPA.Items[yourSelectedIndexes[i]].Value);
                    }

                    cmd.Parameters.Add("@AP_DEV", SqlDbType.Bit).Value = CbAPDev.Checked;
                    cmd.Parameters.Add("@PROD_DEV", SqlDbType.Bit).Value = cbProdDev.Checked;
                    cmd.Parameters.Add("@ROTYPE", SqlDbType.NVarChar, 255).Value = ddlROTYPE.SelectedItem.Value;
                    cmd.Parameters.Add("@SIPA", SqlDbType.Structured).Value = dtSIPA;
                }
                try
                {
                    adapter.Fill(SearchResultsTable);
                }
                catch (System.Data.SqlClient.SqlException ex)
                {
                    Response.Write(ex);
                }                    
            }
        }
    }
 }

As for your stored procedure, you need to create a user defined table type for the @SIPA parameter:

CREATE TYPE SIPA AS TABLE
(
    Id int
)

and change the condition to AND (cl.SIPA IN (SELECT Id FROM @SIPA) OR (SELECT COUNT(*) FROM @SIPA) = 0)

Zohar Peled
  • 79,642
  • 10
  • 69
  • 121
  • So I need to create a table that holds the values for SIPA instead of pulling those from the html listbox? – Gordy McElroy Nov 30 '17 at 17:16
  • THank you Zohar but how do I do this: As for your stored procedure, you need to create a user defined table type for the @SIPA – Gordy McElroy Nov 30 '17 at 17:38
  • Use a create type statement. I've edited my answer to show the basics. – Zohar Peled Nov 30 '17 at 17:42
  • Ok I created the user defined table type the way you mentioned but I'm having trouble implementing the SQL you gave me. I put this in the WHERE clause: AND (cl.SIPA IN (SELECT COUNT(*) from @SIPA) = 0) and I'm getting ticks on "=" and @SIPA saying I must declare the variable which I have already done. – Gordy McElroy Nov 30 '17 at 18:21
  • Thisd is the SQL stack I'm getting: TextSystem.Data.SqlClient.SqlException (0x80131904): Invalid object name 'SIPA'. at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) at – Gordy McElroy Nov 30 '17 at 18:49
  • using (var con = new SqlConnection("SQLDEV1")) is failing with Format of the initialization string does not conform to specification starting at index 0. Please help. – Gordy McElroy Nov 30 '17 at 19:00
  • I got the connection string working. But I'm getting input string not in a correct format: Line 600: for (int i = yourSelectedIndexes.Length - 1; i >= 0; i--) Line 601: { Line 602: dtSIPA.Rows.Add(ddlSIPA.Items[yourSelectedIndexes[i]].Value); Line 603: } – Gordy McElroy Nov 30 '17 at 19:10
  • I don't know how your code look like now, amd I don't know what data type cl.sipa is. I gave a general, simple example and a link to official documentation. That should be enough to get you in the right direction. Also, it's half past midnight here and I'm using my cellphone, kinda hard to understand the code posted in the conments. I'll take another look tomorrow morning. – Zohar Peled Nov 30 '17 at 22:20
  • From my stored procedure I'm getting error message, "Operand type clash: NULL (or NVARCHAR) is incompatible with SIPA_TYPE". Here is the sproc: – Gordy McElroy Dec 04 '17 at 14:50
  • Read the last sentence in my answer. You probably missed that. – Zohar Peled Dec 04 '17 at 14:52
  • SIPA is a list of characters such as A,S,I,SIP,SIPA – Gordy McElroy Dec 04 '17 at 14:56
  • According to the error message, SIPA is a user defined type.... Please edit your question (or ask a new one) to include your current code as well as the full text of the error message and indicate on what line it happens. – Zohar Peled Dec 04 '17 at 14:59
  • Creating the type: CREATE TYPE SIPA_TYPE AS TABLE ( sipaItems NVARCHAR(255)); GO – Gordy McElroy Dec 04 '17 at 15:01
  • Filtering for it now: WHERE (cl.SIPA IN(SELECT sipaItems FROM @SIPA) OR (SELECT COUNT(*) FROM @SIPA) = 0) – Gordy McElroy Dec 04 '17 at 15:03
  • I'll open a new post. – Gordy McElroy Dec 04 '17 at 15:03
  • New Post is here: https://stackoverflow.com/questions/47636658/trying-to-pass-multiple-listbox-values-to-a-stored-procedure-fails-with-operand – Gordy McElroy Dec 04 '17 at 15:43