2012-02-21 8 views
16

Per qualche motivo, l'Sqlparameter per la mia clausola IN() non funziona. Il codice compila bene, e la query funziona se sostituire il parametro con i valori effettiviCome passare sqlparameter a IN()?

StringBuilder sb = new StringBuilder(); 
      foreach (User user in UserList) 
      { 
       sb.Append(user.UserId + ","); 
      } 

      string userIds = sb.ToString(); 
      userIds = userIds.TrimEnd(new char[] { ',' }); 


SELECT userId, username 
FROM Users 
WHERE userId IN (@UserIds) 
+0

Le virgole devono essere tra le stringhe, non all'interno di una stringa. –

+0

Le virgole separano ciascun ID utente – chobo

+0

Quale versione di SQL-Server? –

risposta

33

È necessario creare un parametro per ogni valore desiderato nella clausola IN.

Lo SQL ha bisogno di guardare in questo modo:

SELECT userId, username 
FROM Users 
WHERE userId IN (@UserId1, @UserId2, @UserId3, ...) 

Quindi, è necessario creare i parametri e la clausola IN nel ciclo foreach.
Qualcosa di simile (fuori di testa, non testata):

StringBuilder sb = new StringBuilder(); 
int i = 1; 

foreach (User user in UserList) 
{ 
    // IN clause 
    sb.Append("@UserId" + i.ToString() + ","); 

    // parameter 
    YourCommand.Parameters.AddWithValue("@UserId" + i.ToString(), user.UserId); 

    i++; 
} 
+5

Ricorda di rimuovere l'ultima virgola alla fine, perché con l'esempio sopra sarà in (@userId,) –

+0

Its, post molto vecchio, ma solo un altro aggiornamento per rispondere. per aiutare un altro googler,;) Puoi usare xml come parametro per ottenere un effetto simile di CSV e nella clausola Query - SELECT userId, nome utente Dagli utenti u1 JOIN INTERNO @ UsersID.nodes ('/ ID') T (col) ON u1.userId = t.col.value ('.', 'int') – 0cool

3

SQL Server vede la tua clausola IN come:

IN ('a,b,c') 

Cosa bisogna guardare come è:

IN ('a','b','c') 

C'è un modo migliore per fare ciò che stai cercando di fare.

  • Se l'utente id sono nel DB, quindi la clausola IN dovrebbe essere cambiata a una sottoquery, in questo modo:

    IN (SELECT UserID FROM someTable WHERE someConditions)

  • Si tratta di un hack - non è così lavorare bene con gli indici, e bisogna stare attenti funziona a destra con i vostri dati, ma ho usato con successo in passato:

    @UserIDs LIKE '%,' + UserID + ',%' -- also requires @UserID to begin and end with a comma

+0

+1 per il tuo hack. Anche se probabilmente impone una scansione completa e impedisce all'ottimizzatore di eseguire il proprio lavoro, è un trucco intelligente, utilizzabile anche con Access. –

+0

@John: Ho provato questo: 'IN (@param)' e poi 'command.Parameters.AddWithValue (" @ param "," 'a', 'b', 'c' ");' ma questo non ha successo . Potete per favore consigli su questo. – Praveen

+0

@ user1671639 Se si dispone sempre di 3 parametri, è possibile utilizzare 'IN (@ param1, @ param2, @ param3)' e quindi 'command.Parameters.AddWithValue (" @ param1 "," a "); command.Parameters.AddWithValue ("@ param2", "b"); command.Parameters.AddWithValue ("@ param3", "c"); '. Se non hai sempre 3 valori, forse dovresti fare una nuova domanda su stackoverflow.com, fornire dettagli sufficienti e indirizzarmi alla nuova domanda. Scommetto che diverse persone proveranno a rispondere subito. –

7

Se si utilizza SQL 2008, è possibile creare una stored procedure che accetta un parametro Valued Table (TVP) e utilizzare per eseguire ADO.net la stored procedure e passare un DataTable ad esso:

in primo luogo, è necessario creare il tipo in SQL Server:

CREATE TYPE [dbo].[udt_UserId] AS TABLE(
    [UserId] [int] NULL 
) 

Quindi, è necessario scrivere una stored procedure che accetta questo tipo come parametro:

CREATE PROCEDURE [dbo].[usp_DoSomethingWithTableTypedParameter] 
(
    @UserIdList udt_UserId READONLY 
) 
AS 
BEGIN 

     SELECT userId, username 
     FROM Users 
     WHERE userId IN (SELECT UserId FROM @UserIDList) 

END 

Ora da .net, non è possibile utilizzare LINQ poiché non supporta ancora i parametri di tabella; quindi devi scrivere una funzione che faccia semplicemente il vecchio ADO.net, prende un DataTable e lo passa alla stored procedure: ho scritto una funzione generica che uso che può fare questo per qualsiasi stored procedure, purché richieda solo un parametro tipizzato da tabella, indipendentemente da cosa sia;

public static int ExecStoredProcWithTVP(DbConnection connection, string storedProcedureName, string tableName, string tableTypeName, DataTable dt) 
    { 
     using (SqlConnection conn = new SqlConnection(connection.ConnectionString)) 
     { 
      SqlCommand cmd = new SqlCommand(storedProcedureName, conn); 
      cmd.CommandType = CommandType.StoredProcedure; 

      SqlParameter p = cmd.Parameters.AddWithValue(tableName, dt); 
      p.SqlDbType = SqlDbType.Structured; 
      p.TypeName = tableTypeName; 

      conn.Open(); 
      int rowsAffected = cmd.ExecuteNonQuery(); // or could execute reader and pass a Func<T> to perform action on the datareader; 
      conn.Close(); 

      return rowsAffected; 
     } 
    } 

Quindi è possibile scrivere le funzioni DAL che utilizzano questa funzione di utilità con i nomi effettivi delle stored procedure; per costruire l'esempio nella sua interrogazione, qui è ciò che il codice sarebbe simile:

public int usp_DoSomethingWithTableTypedParameter(List<UserID> userIdList) 
    { 
     DataTable dt = new DataTable(); 
     dt.Columns.Add("UserId", typeof(int)); 

     foreach (var userId in updateList) 
     { 
      dt.Rows.Add(new object[] { userId }); 
     } 

     int rowsAffected = ExecStoredProcWithTVP(Connection, "usp_DoSomethingWithTableTypedParameter", "@UserIdList", "udt_UserId", dt); 
     return rowsAffected; 
    } 

Nota il parametro "Connessione" di cui sopra - Io in realtà uso questo tipo di funzione in una classe DataContext parziale di estendere LINQ DataContext con la mia funzionalità TVP, e uso ancora la sintassi (usando il contesto var = new MyDataContext()) con questi metodi.

Questo funziona solo se si utilizza SQL Server 2008 - si spera che lo sia e in caso contrario, questa potrebbe essere una buona ragione per eseguire l'aggiornamento! Naturalmente nella maggior parte dei casi e in ambienti di produzione di grandi dimensioni non è così facile, ma FWIW penso che questo sia il modo migliore per farlo se hai la tecnologia disponibile.

+0

Se la maggior parte delle tue tabelle usa lo stesso tipo per il loro PK, potresti non creare un UDT ID riutilizzabile generalizzato? Come: CREATE TYPE [dbo]. [Udt_IntId] AS TABLE ([Id] [int] NULL) da riutilizzare in * qualsiasi * caso in cui è necessario eseguire una clausola sql nell'ID chiave primaria int? – Pxtl

4

possibile "pulito" versione:

StringBuilder B = new StringBuilder(); 
for (int i = 0; i < UserList.Count; i++) 
    YourCommand.Parameters.AddWithValue("@UserId" + i.ToString(), UserList[i].UserId); 
B.Append(String.Join(",", YourCommand.Parameters.Select(x => x.Name))); 
Problemi correlati