2 votes

Écrire une redoutable requête de recherche SQL (2e phase)

Je travaille sur une requête de recherche (avec un frontal asp.net 3.5) qui semble assez simple, mais qui est assez complexe. La requête complète est la suivante

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go

ALTER PROCEDURE [dbo].[usp_Item_Search]
    @Item_Num varchar(30) = NULL
    ,@Search_Type int = NULL
    ,@Vendor_Num varchar(10) = NULL
    ,@Search_User_ID int = 0
    ,@StartDate smalldatetime = NULL
    ,@EndDate smalldatetime = NULL
AS
DECLARE @SQLstr as nvarchar(4000)

Set @SQLstr = 'SELECT RecID, Vendor_Num, Vendor_Name, InvoiceNum, Item_Num, 
(SELECT CONVERT(VARCHAR(11), RecDate, 106) AS [DD MON YYYY]) As RecDate, NeedsUpdate, RecAddUserID FROM [tbl_ItemLog] where 1=1 '

IF (@Item_Num IS NOT NULL and LTRIM(@Item_Num) <> '')
    Begin
        If @Search_Type = 0
            BEGIN
                Set @SQLstr = @SQLstr +  'AND Item_Num LIKE ''' + @Item_Num + '%'''
            END
        If @Search_Type = 1
            BEGIN
                Set @SQLstr = @SQLstr + 'AND Item_Num LIKE ''%' + @Item_Num + '%'''
            END
        If @Search_Type = 2
            BEGIN
                Set @SQLstr = @SQLstr + 'AND Item_Num LIKE ''%' + @Item_Num + ''''
            END
    End

IF (@Vendor_Num IS NOT NULL and LTRIM(@Vendor_Num) <> '')
    Begin
        Set @SQLstr = @SQLstr + ' AND Vendor_Num = ''' + @Vendor_Num + ''''
    End

IF (@Search_User_ID IS NOT NULL and @Search_User_ID > 0)
    Begin
        Set @SQLstr = @SQLstr + ' AND RecAddUserID = ' + convert(nvarchar(20),@Search_User_ID)
    End

Set @SQLstr = @SQLstr + ' AND (RecDate BETWEEN ''' + convert(nvarchar(10),@StartDate,106) + ''' AND ''' + convert(nvarchar(10),@EndDate,106) + ''')'

PRINT (@SQLstr)
--Execute (@SQLstr)

Lorsque je passe toutes les valeurs de paramètres vides, j'obtiens une erreur :

"Échec de la conversion de la valeur du paramètre d'un String à un Int32".

Le code asp.net qui appelle la procédure stockée est le suivant :

        //Display search results in GridView;
        SqlConnection con = new SqlConnection(strConn);
        //string sqlItemSearch = "usp_Item_Search";
        SqlCommand cmdItemSearch = new SqlCommand(sqlItemSearch, con);
        cmdItemSearch.CommandType = CommandType.StoredProcedure;

        cmdItemSearch.Parameters.Add(new SqlParameter("@Item_Num", SqlDbType.VarChar, 30));
        cmdItemSearch.Parameters["@Item_Num"].Value = txtItemNumber.Text.Trim();

        cmdItemSearch.Parameters.Add(new SqlParameter("@Search_Type", SqlDbType.Int));
        cmdItemSearch.Parameters["@Search_Type"].Value = ddlSearchType.SelectedItem.Value;

        cmdItemSearch.Parameters.Add(new SqlParameter("@Vendor_Num", SqlDbType.VarChar, 10));
        cmdItemSearch.Parameters["@Vendor_Num"].Value = txtVendorNumber.Text.Trim();

        cmdItemSearch.Parameters.Add(new SqlParameter("@Search_User_ID", SqlDbType.Int));
        cmdItemSearch.Parameters["@Search_User_ID"].Value = ddlSeachUser.SelectedItem.Value;

        if (!string.IsNullOrEmpty(txtStartDate.Text))
        {
            cmdItemSearch.Parameters.Add(new SqlParameter("@StartDate", SqlDbType.DateTime));
            cmdItemSearch.Parameters["@StartDate"].Value = Convert.ToDateTime(txtStartDate.Text.Trim());
        }
        else
        {
            cmdItemSearch.Parameters.Add(new SqlParameter("@StartDate", SqlDbType.DateTime));
            cmdItemSearch.Parameters["@StartDate"].Value = Convert.ToDateTime("01/01/1996");
        }

        if (!string.IsNullOrEmpty(txtEndDate.Text))
        {
            cmdItemSearch.Parameters.Add(new SqlParameter("@EndDate", SqlDbType.DateTime));
            cmdItemSearch.Parameters["@EndDate"].Value = Convert.ToDateTime(txtEndDate.Text.Trim());
        }
        else
        {
            cmdItemSearch.Parameters.Add(new SqlParameter("@EndDate", SqlDbType.DateTime));
            cmdItemSearch.Parameters["@EndDate"].Value = Convert.ToDateTime(DateTime.Now);
        }
        con.Open();

        SqlDataAdapter ada = new SqlDataAdapter(cmdItemSearch);
        DataSet ds = new DataSet();
        ada.Fill(ds);

            gvSearchDetailResults.DataSource = ds;
            gvSearchDetailResults.DataBind();
            pnlSearchResults.Visible = true;

Comment puis-je résoudre ce problème ?

3voto

Tom H. Points 23783

Vous ne construisez pas la corde correctement, d'après ce que je vois. Si aucun @Item_Num n'est transmis, vous n'aurez pas de mot clé WHERE... vous aurez juste "FROM [tblItem_Log] AND...".

Je ferais en sorte que toutes les appositions de critères soient "AND ..." et comme votre déclaration initiale, j'utiliserais :

FROM [tbl_Item_Log] WHERE (1=1)

Puisque vous avez un code pour renvoyer la chaîne générée, pourquoi ne pas le mettre dans SSMS et essayer de l'exécuter ?

Je viens également de remarquer que si vous ne transmettez pas de valeurs de date, vous finirez par exécuter une chaîne NULL, car votre concaténation finale provoquera un NULL. C'est le genre de choses auxquelles il faut faire très attention si l'on veut utiliser le SQL dynamique pour construire des requêtes.

Une fois que j'ai corrigé cela, j'ai pu exécuter la procédure stockée sans aucune erreur (au moins pour générer ce qui ressemble à une instruction SQL valide). Cela m'amène à penser qu'il s'agit peut-être d'un problème de types de données dans la table sous-jacente. Pouvez-vous en fournir la définition ?

Une dernière remarque : Personnellement, j'utiliserais

CONVERT(VARCHAR(11), RecDate, 106) AS RecDate

au lieu de la sous-requête apparemment inutile que vous avez.

Encore un autre montage : Vous pouvez supprimer le code qui vérifie que LTRIM(@Search_User_ID) <> ''. C'est un bout de code inutile et peut-être qu'un paramètre particulier à votre serveur/connexion le fait échouer à cause de l'incompatibilité de type.

1voto

HLGEM Points 54641
IF (Search_User_ID IS NOT NULL) 

nécessite un symbole @ devant la variable

Vous dites que vous passez une chaîne vide pour toutes les variables, mais l'une d'entre elles est un int, il ne peut pas prendre une chaîne vide qui n'est pas une donnée int. Je n'arrive pas à croire que je n'ai pas remarqué ça la première fois.

0voto

STO Points 4597

Pourquoi n'utilisez-vous pas une requête à paramètre unique comme ceci :

select 
   recdid,
   Vendor_Num,
   Vendor_Name,
   InvoiceNum,
   Item_Num, 
   CONVERT(VARCHAR(11), RecDate, 106) as RecDate,
   NeedsUpdate, 
   RecAddUserID 
FROM 
  [tbl_ItemLog] as t
where
   (((Item_num like @Item_Num + '%' and @Search_Type = 0) OR
    (Item_num like '%' + @Item_Num + '%' and @Search_Type =  1) OR
    (Item_num like '%' + @Item_Num + '%' and @Search_Type = 2))  
        OR
    @Item_Num IS NULL) AND
   (Vendor_Num = @Vendor_Num OR @Vendor_Num IS NULL) AND
   (RecAddUserId = @Search_User_Id OR @Search_User_Id IS NULL) AND
   (RecDate BETWEEN @StartDate AND @EndDate)

0voto

le dorfier Points 27267

Vous avez vraiment plusieurs procédures stockées différentes ici. Pourquoi ne pas les écrire séparément ? Tout ce qui est contrôlé par les instructions switch pourrait être facilement dans du code procédural. Même chose pour les appels LTRIM.

Vous pourriez les appeler tous à partir d'une seule SP avec des instructions switch ; mais je pense qu'il est généralement préférable de ne pas les fusionner en premier lieu. Les requêtes SP s'optimiseront plus facilement, et le code sera simplifié. Il n'y a pas grand-chose à gagner à les consolider.

Je ne suis pas sûr des règles de votre entreprise, mais vous pourriez simplifier cela en dehors de SQL avec

switch(search_type) {    
case 1:  
    do_query_type_1(args);  
    break;  
case 2:  
    do_query_type_2(args);  
    break;  
case 3:  
    do_query_type_3(args);  
    break;  
default:  
    whatever ...  
}

Il semble également que vous ayez une logique distincte pour les cas où le numéro d'article est fourni ou non. Idem pour les autres champs. Chacun de vos cas d'utilisation semble se résoudre à une requête assez simple.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X