У меня есть массив с огромным количеством идентификаторов, которые я бы хотел выбрать из БД.
Обычный подход - сделать select blabla from xxx where yyy IN (ids) OPTION (RECOMPILE)
.(Параметр перекомпилировать необходим, поскольку сервер SQL недостаточно интеллектуален, чтобы видеть, что помещение этого запроса в кэш запросов - это огромная трата памяти)
Однако SQL Server ужасенЭтот тип запроса, когда количество идентификаторов велико, анализатор, который он использует, просто слишком медленно.Позвольте мне привести пример:
SELECT * FROM table WHERE id IN (288525, 288528, 288529,<about 5000 ids>, 403043, 403044) OPTION (RECOMPILE)
Время выполнения: ~ 1100 мсек (в моем примере это возвращает приблизительно 200 строк)
Versus:
SELECT * FROM table WHERE id BETWEEN 288525 AND 403044 OPTION (RECOMPILE)
Время выполнения: ~ 80 мсек (в моем примере это возвращает приблизительно 50000 строк)
Так что, хотя я получаю в 250 раз больше данных, он выполняется в 14 раз быстрее...
Итак, я построил эту функцию, чтобы взять мой список идентификаторов и создать что-то, что будет возвращать разумный компромисс между ними (то, что не возвращает в 250 раз больше данных, но все же дает преимуществоболее быстрого анализа запроса)
private const int MAX_NUMBER_OF_EXTRA_OBJECTS_TO_FETCH = 5;
public static string MassIdSelectionStringBuilder(
List<int> keys, ref int startindex, string colname)
{
const int maxlength = 63000;
if (keys.Count - startindex == 1)
{
string idstring = String.Format("{0} = {1}", colname, keys[startindex]);
startindex++;
return idstring;
}
StringBuilder sb = new StringBuilder(maxlength + 1000);
List<int> individualkeys = new List<int>(256);
int min = keys[startindex++];
int max = min;
sb.Append("(");
const string betweenAnd = "{0} BETWEEN {1} AND {2}\n";
for (; startindex < keys.Count && sb.Length + individualkeys.Count * 8 < maxlength; startindex++)
{
int key = keys[startindex];
if (key > max+MAX_NUMBER_OF_EXTRA_OBJECTS_TO_FETCH)
{
if (min == max)
individualkeys.Add(min);
else
{
if(sb.Length > 2)
sb.Append(" OR ");
sb.AppendFormat(betweenAnd, colname, min, max);
}
min = max = key;
}
else
{
max = key;
}
}
if (min == max)
individualkeys.Add(min);
else
{
if (sb.Length > 2)
sb.Append(" OR ");
sb.AppendFormat(betweenAnd, colname, min, max);
}
if (individualkeys.Count > 0)
{
if (sb.Length > 2)
sb.Append(" OR ");
string[] individualkeysstr = new string[individualkeys.Count];
for (int i = 0; i < individualkeys.Count; i++)
individualkeysstr[i] = individualkeys[i].ToString();
sb.AppendFormat("{0} IN ({1})", colname, String.Join(",",individualkeysstr));
}
sb.Append(")");
return sb.ToString();
}
Затем он используется следующим образом:
List<int> keys; //Sort and make unique
...
for (int i = 0; i < keys.Count;)
{
string idstring = MassIdSelectionStringBuilder(keys, ref i, "id");
string sqlstring = string.Format("SELECT * FROM table WHERE {0} OPTION (RECOMPILE)", idstring);
Однако мой вопрос ... Кто-нибудь знает, что лучше / быстрее / умнеесделать это?