English 中文(简体)
Convert an array of integers for use in a SQL "IN" clause
原标题:
  • 时间:2009-11-13 15:54:17
  •  标签:
  • c#
  • sql
  • arrays

Surely there is a framework method that given an array of integers, strings etc converts them into a list that can be used in a SQL "IN" clause?

e.g.

int[] values = {1,2,3};

would go to

"(1,2,3)"
最佳回答
var inClause = "("
     + String.Join(",", values.Select(x => x.ToString()).ToArray()) 
     + ")";

Note: You no longer need to call .ToArray() in .NET Framework 4. A new String.Join<T>(string separator, IEnumerable<string> values) method is added.

问题回答

You can use the String.Join method, like this:

var str = "(" + string.Join(", ", Array.ConvertAll(values, v => v.ToString(CultureInfo.InvariantCulture)));

Assuming that values is an array, Array.ConvertAll should be more efficient than LINQ with ToArray.

If you don t have access to the .NET 3.5 extension methods, you can do this:

StringBuilder sb = new StringBuilder();
sb.Append( ( );

foreach (int i in values) {
    sb.Append(i).Append( , );
}

// remove final ,
sb.Length -= 1;
sb.Append( ) );

string inValue = sb.ToString();

Which ll work on .NET 2

This could be done in one line too

public string ToInStatement(this int[] values) {
    string[] stringValues = 
       Array.ConvertAll<int, string>(values, Convert.ToString);
    string result = "(" + String.Join(",", stringValues) + ")";
    return result;
 }

If your list of integers is large, you may end up generating a string that is too long for your database to accept. E.g. I think the maximum length of a VARCHAR in SQL2000 is around 8K.

So I have a set of helper method something like the sample below, which return an enumeration of strings, which can then be used as follows:

List<int> idList = ...;
using(SqlCommand command = ...)
{
    ...
    foreach(string idString in ConcatenateValues(ids,",", maxLength, false))
    {
       command.Parameters[...] = idString;
       // or command.CommandText = "SELECT ... IN (" + idString + ")...";
       ... execute command ...
    }
}

The concatenate method might look something like the following:

public static IEnumerable<string> ConcatenateValues(IEnumerable<int> values, string separator, int maxLength, bool skipDuplicates)
{
    IDictionary<int, string> valueDictionary = null;
    StringBuilder sb = new StringBuilder();
    if (skipDuplicates)
    {
        valueDictionary = new Dictionary<int, string>();
    }
    foreach (int value in values)
    {
        if (skipDuplicates)
        {
            if (valueDictionary.ContainsKey(value)) continue;
            valueDictionary.Add(value, "");
        }
        string s = value.ToString(CultureInfo.InvariantCulture);
        if ((sb.Length + separator.Length + s.Length) > maxLength)
        {
            // Max length reached, yield the result and start again
            if (sb.Length > 0) yield return sb.ToString();
            sb.Length = 0;
        }
        if (sb.Length > 0) sb.Append(separator);
        sb.Append(s);
    }
    // Yield whatever s left over
    if (sb.Length > 0) yield return sb.ToString();
}

You can do this more efficiently using the following extension method:

    ///<summary>Appends a list of strings to a StringBuilder, separated by a separator string.</summary>
    ///<param name="builder">The StringBuilder to append to.</param>
    ///<param name="strings">The strings to append.</param>
    ///<param name="separator">A string to append between the strings.</param>
    public static StringBuilder AppendJoin(this StringBuilder builder, IEnumerable<string> strings, string separator) {
        if (builder == null) throw new ArgumentNullException("builder");
        if (strings == null) throw new ArgumentNullException("strings");
        if (separator == null) throw new ArgumentNullException("separator");

        bool first = true;

        foreach (var str in strings) {
            if (first)
                first = false;
            else
                builder.Append(separator);

            builder.Append(str);
        }

        return builder;
    }

    ///<summary>Combines a collection of strings into a single string.</summary>
    public static string Join<T>(this IEnumerable<T> strings, string separator, Func<T, string> selector) { return strings.Select(selector).Join(separator); }
    ///<summary>Combines a collection of strings into a single string.</summary>
    public static string Join(this IEnumerable<string> strings, string separator) { return new StringBuilder().AppendJoin(strings, separator).ToString(); }

Hey, great suggestions, just a slight modification below

public static class IEnumerableExtensions
{
    // reasonable to assume you will use this everywhere, not just
    // Sql statements, but log statements, anywhere you need to 
    // dump a list into a readable format!
    // 
    // HINT: extra credit: you can generalize this, and provide
    // specialized short hands that invoke the general method
    public static string ToCommaSeparatedString<T>(this IEnumerable<T> values)
    {
         // SIGH: so apparently this does not generate minimal
         // assembler on every machine, please note the following
         // is written for clarity, please feel free to substitute
         // your own favourite ultra-performance high-octance
         // string appender algorithm
         StringBuilder commaSeparated = new StringBuilder ();
         foreach (T value in values)
         {
             // PERF: store format string as const
             commaSeparated.AppendFormat ("{0}, ", value);
         }
         // PERF: store trim chars as static readonly array
         return commaSeparated.Trim (", ".ToCharArray ());
    }
}

...
// elsewhere in code
List<int> myIdentifiers = new List<int> { 1, 2, 3, 4, 5, };
string mySqlIdentifierList = myIdentifiers.ToCommaSeparatedList ();
string mySqlStatementFormat = "SELECT * FROM [SomeTable] WHERE [Id] IN ({0})";
string mySqlStatement = 
    string.format (mySqlStatementFormat, mySqlIdentifierList);
...
/// <summary>
/// Converts an array of integers into a string that may be used in a SQL IN expression.
/// </summary>
/// <param name="values">The array to convert.</param>
/// <returns>A string representing the array as a parenthetical comma-delemited list. If the array
/// is empty or missing, then "(null)" is returned.</returns>
public static string ToSqlInList(int[] values)
{
    if (values == null || values.Length == 0)
        return "(null)";  // In SQL the expression "IN (NULL)" is always false.

    return string.Concat("(", string.Join(",", Array.ConvertAll<int, string>(values,x=>x.ToString())), ")");
}




相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签