Выбирая несколько строк по идентификатору, существует более быстрый способ, чем WHERE IN

У меня есть таблица SQL, и я бы хотел выбрать несколько строк по ID. Например, я хотел бы получить строку с идентификаторами 1, 5 и 9 из моей таблицы.

Я делал это с инструкцией WHERE IN, как показано ниже:

SELECT [Id]
FROM [MyTable]
WHERE [Id] IN (1,5,9)

Однако это довольно медленно для большого количества элементов в разделе "IN"

Ниже приведены некоторые данные о производительности из выбора строк, в которых используется таблица из 1 000 000 строк

Querying for 1 random keys (where in) took 0ms
Querying for 1000 random keys (where in) took 46ms
Querying for 2000 random keys (where in) took 94ms
Querying for 3000 random keys (where in) took 249ms
Querying for 4000 random keys (where in) took 316ms
Querying for 5000 random keys (where in) took 391ms
Querying for 6000 random keys (where in) took 466ms
Querying for 7000 random keys (where in) took 552ms
Querying for 8000 random keys (where in) took 644ms
Querying for 9000 random keys (where in) took 743ms
Querying for 10000 random keys (where in) took 853ms

Есть ли более быстрый способ, чем использовать WHERE IN для этого.

Мы не можем сделать соединение, поскольку это между отключенными системами.

Я слышал, что в памяти временная таблица, соединенная с данными в MYSQL, может быть быстрее, но из моего исследования MSSQL не имеет опции в таблице памяти и даже если бы он не был подвержен точно такой же сканированию индекса при вставке в таблицу temp, как у WHERE IN?

EDIT:

В этой таблице есть идентификатор как ПК, поэтому есть индекс PK по умолчанию, cf

CREATE TABLE [dbo].[Entities](
    [Id] [int] IDENTITY(1,1) NOT NULL,
 CONSTRAINT [PK_dbo.Entities] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

План выполнения

enter image description here

Вот GIST для консольного приложения, которое производит эти результаты производительности https://gist.github.com/lukemcgregor/5914774

РЕДАКТИРОВАТЬ 2 Я создал функцию, которая создает временную таблицу из разделенной запятой строки, а затем присоединяется к этой таблице. Это быстрее, но я думаю, что в основном из-за проблемы с разбором запроса с помощью

Querying for 1 random keys took 1ms
Querying for 1000 random keys took 34ms
Querying for 2000 random keys took 69ms
Querying for 3000 random keys took 111ms
Querying for 4000 random keys took 143ms
Querying for 5000 random keys took 182ms
Querying for 6000 random keys took 224ms
Querying for 7000 random keys took 271ms
Querying for 8000 random keys took 315ms
Querying for 9000 random keys took 361ms
Querying for 10000 random keys took 411ms

Ответ 1

ОК, поэтому я быстро это сделал, указав тип таблицы, а затем передав этот тип непосредственно в запрос и присоединившись к нему.

в SQL

CREATE TYPE [dbo].[IntTable] AS TABLE(
    [value] [int] NULL
)

в коде

DataTable dataTable = new DataTable("mythang");
dataTable.Columns.Add("value", typeof(Int32));

toSelect.ToList().ForEach(selectItem => dataTable.Rows.Add(selectItem));

using (SqlCommand command = new SqlCommand(
    @"SELECT * 
    FROM [dbo].[Entities] e 
    INNER JOIN @ids on e.id = value", con))
{
    var parameter = command.Parameters.AddWithValue("@ids", dataTable);
    parameter.SqlDbType = System.Data.SqlDbType.Structured;
    parameter.TypeName = "IntTable";

    using (SqlDataReader reader = command.ExecuteReader())
    {
        while (reader.Read())
        {
            results.Add(reader.GetInt32(0));
        }
    }
}

это дает следующие результаты:

Querying for 1 random keys (passed in table value) took 2ms
Querying for 1000 random keys (passed in table value) took 3ms
Querying for 2000 random keys (passed in table value) took 4ms
Querying for 3000 random keys (passed in table value) took 6ms
Querying for 4000 random keys (passed in table value) took 8ms
Querying for 5000 random keys (passed in table value) took 9ms
Querying for 6000 random keys (passed in table value) took 11ms
Querying for 7000 random keys (passed in table value) took 13ms
Querying for 8000 random keys (passed in table value) took 17ms
Querying for 9000 random keys (passed in table value) took 16ms
Querying for 10000 random keys (passed in table value) took 18ms

Ответ 2

Я думаю, если бы вы присоединились к своей таблице с таблицей памяти, индексированной с помощью первичного ключа, например:

declare @tbl table (ids int primary key)

вы можете заполнить эту таблицу идентификатором, который вам нужен, и заготовьте оптимизированное внутреннее соединение.

Проблемой может быть время, необходимое для ее заполнения. Я предполагаю, что у вас может быть связанный сервер для этого или, возможно, использовать служебную программу BCP для заполнения временной таблицы, а затем удалить ее.

Ответ 3

Во-первых, я думаю, что довольно сложно утверждать, что ваши данные наводят на размышления о O(n log(n)). (Замечательно, что вы выполнили тест производительности, кстати.) Вот время за значение:

1000    0.046
2000    0.047
3000    0.083
4000    0.079
5000    0.078
6000    0.078
7000    0.079
8000    0.081
9000    0.083
10000   0.085

Несмотря на небольшое увеличение с течением времени, скачок с 2000-3000 годов намного, гораздо более заметен. Если это воспроизводимо, то для меня возникает вопрос, почему такой разрыв.

Для меня это больше предложений O(n) и O(n log(n)). НО, эмпирические оценки теоретических значений трудно аппроксимировать. Таким образом, точное ограничение не так важно.

Я ожидаю, что производительность будет O(n) (где n - это фактическое значение, а не длина бит, как и в некоторых оценках). Я понимаю, что in ведет себя как гигантский набор or s. Большинство записей не проходят тест, поэтому они должны делать все сравнения. Следовательно, O(n).

Следующий вопрос: если у вас есть индекс в поле id. В этом случае вы можете получить набор идентификаторов соответствия в O(n log(n)) time ( log (n) for traversing the index and n` для выполнения этого для каждого значения). Эти швы хуже, но мы не учитываем коэффициент размера оригинальной таблицы. Это должно быть большой победой.

Как говорит Андре, вы можете загрузить таблицу и присоединиться к временной таблице. Я бы отказался от индекса, потому что вам, вероятно, лучше использовать индекс в большой таблице. Это должно сделать вам O(n log(n)) - без (существенной) зависимости от размера исходной таблицы. Или вы можете оставить индекс и иметь O(n * m), где m - размер исходной таблицы. Я думаю, что любой индекс, построенный на временной таблице, вернет вас к производительности O(n log(n)) (если данные не предварительно настроены).

Размещение всего в запросе имеет аналогичную, неустановленную проблему - разбор запроса. Это занимает больше времени, поскольку строка становится длиннее.

Короче говоря, я рекомендую вам выполнить измерения производительности, но не для того, чтобы прийти к выводам об алгоритмической сложности. Я не думаю, что ваши данные подтверждают ваше заключение. Кроме того, обработка запросов немного сложнее, чем вы предлагаете, и вы оставили размер большой таблицы, которая может иметь доминирующий аффект. И мне очень любопытно, что происходит между 2000 и 3000 рядами.