Инструкция SQL SELECT внутри WHERE IN ()

#sql #select #where-clause #where-in

Вопрос:

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

 DECLARE @ids TABLE (id UNIQUEIDENTIFIER PRIMARY KEY)  INSERT INTO @ids SELECT TOP 1 ... -- returns D2820EDA-5F00-4EE8-A4E0-992E74D25C09 -- 0 seconds execution  SELECT field1, field2, field3 FROM list WHERE id IN (SELECT id FROM @ids)  

Моя проблема в том, что этот запрос занимает слишком много времени (более минуты), что в настоящее время неприемлемо.

Один и тот же запрос Select с фиксированным значением занимает около 3 секунд.

 SELECT field1, field2, field3 FROM list WHERE id IN ('D2820EDA-5F00-4EE8-A4E0-992E74D25C09')  

У меня одинаковое время выполнения для 100 значений фильтра.

Что я могу сделать, чтобы отфильтровать свои значения с той же скоростью, с какой я бы установил фиксированные строковые значения внутри оператора IN?

ИЗМЕНИТЬ: столбец «идентификатор» внутри «списка» не является уникальным и имеет более одного результата.

EDIT2: Я попробовал это с помощью оператора JOIN. (время выполнения даже больше, чем 2 минуты):

 SELECT field1, field2, field3 FROM list AS ud JOIN @ids AS ids ON ud.id= ids.id  

и со строковым результатом (время выполнения 1 минута):

 DECLARE @result NVARCHAR(MAX);  SELECT @result = STUFF( (SELECT TOP 1 CONVERT(VARCHAR(38), filterId)   ',' FROM Filters WHERE ... FOR xml path(''))   'empty' ,1,1,'');  SELECT field1, field2, field3 FROM list AS ud WHERE ','   @result LIKE '%,'   CONVERT(VARCHAR(38), ud.id)   ',%'  

Комментарии:

1. Воспользуйся exists . Ваш запрос является неполным в вашем вопросе, поэтому я не могу предложить ничего большего.

2. 3 секунды для одного значения-это тоже много. Есть ли у этого id столбца «списка» индекс?

3. Я пробовал это с exists, тот же результат… в списке около 4,5 млн записей, так что 3 секунды вполне приемлемы.

4. Поскольку @ids .id является ПЕРВИЧНЫМ КЛЮЧОМ, вы также можете попробовать присоединиться.

5. @Serg это решение также работает медленно.

Ответ №1:

Есть много способов сделать то, что вы пытаетесь сделать. т. е.:

 DECLARE @ids TABLE (id UNIQUEIDENTIFIER PRIMARY KEY)  INSERT INTO @ids (id) values (...);  

а затем запросите:

 SELECT field1, field2, field3 FROM list WHERE id IN (Select id from @ids);  

или:

 SELECT field1, field2, field3  FROM list  WHERE exists (select * from @ids i where i.id = list.id);  

или:

 SELECT t1.field1, t1.field2, t1.field3  FROM list t1  inner join @ids i on i.id = t1.id;  

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

Скорее всего, ваш кэшированный план запроса плох. Вы можете попробовать добавить опцию (Перекомпилировать). ie:

 SELECT field1, field2, field3 FROM list WHERE id IN (Select id from @ids) Option (Recompile);  

И если у вас много значений in (нужно передать тысячи идентификаторов Guid), я предлагаю использовать анализатор TVF на основе CLR, который получает их в виде значений, разделенных запятыми, и анализирует в таблице на стороне сервера.

ПРАВКА: Я не знаю, что заставило меня подумать, что это SQL server. Может быть, я видел метку другого вопроса. В любом случае, я сохраню это. Теперь я понимаю, почему я подумал о SQL server, из-за синтаксиса в ваших образцах. Весьма вероятно, что это SQL server, даже если вы не пометили свой сервер.

РЕДАКТИРОВАТЬ: Я только что протестировал это с помощью 10 ключей в таблице из 5 миллионов деталей (PartID, номер партнера, доступное количество, цена).:

 DECLARE @ids TABLE(id UNIQUEIDENTIFIER); INSERT INTO @ids(id) VALUES('D5DCE10E-513C-449D-8E34-8FE771FA464A')  , ('EAC65501-21F5-F831-FB07-DCFEAD50D1D9')  , ('30F20823-885A-332F-86EF-B08B142F744C')  , ('49849363-2487-4351-0761-57F581B71E46')  , ('32502023-4B75-D650-7525-3868B79E7C31')  , ('D69804D7-534E-3C43-DBAE-9F37E5E516D6')  , ('8413D734-24E4-53AA-38FF-7BA6B8CF19BA')  , ('55036E2E-A022-67B9-307A-8A53AB77D5CF')  , ('0EC2F7C3-43B6-7002-4B85-A95549D2382F')  , ('2E017358-1FE7-316B-A1ED-38ED11E6AD18');  SELECT * FROM dbo.parts AS p WHERE p.partID IN( SELECT id FROM @ids);  

Прошло 18 миллисекунд.

 SELECT * FROM dbo.parts AS p WHERE p.partID IN ( 'C727963F-C1B5-FE0C-1141-6B919346DF69'  , 'CDBAB6F8-AB42-0DDE-51CD-AEAECAE914D7'  , 'D162D88D-ABA9-FCE8-F651-B45601340B46'  , '26AD28EF-846B-C4FF-281C-06F4AA958157'  , '5000857A-8A0D-F500-E2C3-087443B5C60D'  , '59C386F4-0412-347B-3F03-D7AB0428572D'  , '0524D4A8-34A4-EA75-392A-1716DBF8A6A1'  , 'F350E8A4-2F42-E21D-C308-04912065EFF8'  , 'AA31FB13-FF0C-4986-9E1E-1F081028806F'  , '4890072E-0B3B-E5FF-B788-FA2E2E6668AC'  );  

Прошло 11 миллисекунд.

Я думаю, что разница незначительна и может отличаться при повторных тестах. Попробуйте добавить ОПЦИЮ (ПЕРЕКОМПИЛЯЦИЯ).

Комментарии:

1. Да, это SQL-сервер, извините… Моя самая большая проблема заключается в том, почему он извлекает во много раз быстрее с фиксированными строковыми значениями GUID, чем с извлеченной таблицей GUID.

2. @Senad, это быстро с фиксированным или нет. С фиксированным может быть немного быстрее из-за сгенерированного плана выполнения. Вы пробовали перекомпилировать — я только что протестировал и добавил свои результаты.