Время прочтения: 3 мин.

В одном из фильмов с Уиллом Смитом главный герой в процессе обзвона клиентов заметил, что если он не кладет трубку, а сразу набирает следующий номер, то на один звонок он тратит меньше времени и в день может совершить больше звонков. Таким образом, он сократил время на мелких рутинных делах. Подобное возможно и в других сферах деятельности. Например, если работа связана с выгрузкой или загрузкой данных на SQL Server.

При написании больших запросов зачастую требуется записать в «физическую» таблицу результаты соединения нескольких таблиц. Конечно, сделать это можно (или даже нужно) традиционным способом с применением инструкции CREATE TABLE, но для этого необходимо заранее знать все типы данных в столбцах, прописывать их наименования, что занимает определённое время. Более того, в силу человеческого фактора не исключён риск совершения ошибок, которые приведут к лишним трудозатратам на пересоздание таблицы и потере времени при выполнении запроса.

Кто-то скажет: «А как же инструкция SELECT INTO?». Да, эта инструкция позволяет сразу из запроса создать новую таблицу и записать туда результаты запроса выборки, но есть один большой очень критичный недостаток. На больших объёмах данных запросы, содержащие данную инструкцию занимают значительное количество ресурсов на сервере и блокируют работу других пользователей сервера. Обычно такие запросы отключаются администраторами серверов, а в некоторых случаях ещё и блокируются учётные записи пользователей, злоупотребляющих такими выгрузками.

В качестве решения вопроса можно применить следующий вариант. Учитывая, что инструкция SELECT INTO копирует структуру результирующей таблицы, можно применить её к запросу, но не нагружая сервер, выбирать 0 строк (Select top 0 * into и т.д.). После этого, выполнить обычный запрос INSERT INTO во вновь созданную таблицу. Для того чтобы не делать это каждый раз (ведь главная цель – экономия времени), оформляем всё это в хранимую процедуру с использованием инструкции IF EXIST и данных из системных таблиц.

Таким образом, получаем следующий код:

create procedure [dbo].[sp_ctwsi] (@tablename nvarchar(max), @sql nvarchar(max))
as
begin
	if not exists
(	
select 
			s.name shm,
			o.name tbl	 
		from sys.objects o
		inner join sys.schemas s on o.schema_id = s.schema_id
		where type = 'u' 
and o.name =  substring(@tablename, CHARINDEX('.', @tablename)+1, len(@tablename) - CHARINDEX('.', '')+1) 
and s.name = substring(@tablename, 1, CHARINDEX('.', @tablename)-1)
)
	begin 	
		exec ('select top 0 * into '+@tablename+' from (' + @sql +') t' )
	end
	else
	begin
		exec ('insert into '+@tablename+' select * from (' + @sql +') t' )
	end
end

Как видно из кода, на входе в процедуру требуется передать два параметра – наименование таблицы со схемой через точку (например, “dbo.MyTable”) и SQL-запрос, результаты которого требуется записать в таблицу.

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