Published on

November 30, 2020

Как реализовать циклы в SQL Server

Проблема: Мне нужно использовать цикл в Transact (T-SQL), но похоже, что в Microsoft SQL Server нет конструкции цикла FOR. Как мне написать цикл в моих SQL-скриптах? Можете ли вы предоставить пример цикла с оператором SELECT, включающим начало, счетчик цикла и условную логику окончания, которую можно использовать в хранимых процедурах?

Решение: В программировании часто требуется выполнить итерацию над некоторым набором, массивом или списком. Например, для каждой строки заказа продажи в счете вы хотите рассчитать возможную скидку. Здесь ключевые слова – “для каждого”. Для каждого элемента набора вы хотите выполнить некоторое действие. Другими словами, вы хотите “пройтись по” набору. Во многих языках программирования есть оператор цикла FOR. Однако в SQL-коде нет оператора цикла FOR. Но есть альтернативные SQL-команды, которые можно использовать для реализации функциональности цикла.

Оператор цикла WHILE

Самая простая альтернатива циклу FOR в SQL Server – это цикл WHILE. Цикл WHILE выполняется до тех пор, пока логическое условие истинно. Это означает, что цикл WHILE может выполняться бесконечно долго, если в логическом условии допущена ошибка. Однако с помощью цикла WHILE можно имитировать цикл FOR. Вот пример:

DECLARE @i TINYINT = 0;
DECLARE @length TINYINT = 10;

WHILE @i <= @length
BEGIN
    PRINT @i;
    SET @i += 1;
END

В этом примере скрипт начинается с @i = 0, выводит значение переменной @i, увеличивает эту переменную на 1 и продолжает делать это, пока @i не станет равным 10. Содержимое цикла WHILE заключено между ключевыми словами BEGIN и END. Оператор += является сокращением для оператора SET @i = @i + 1;.

Вы можете управлять операторами внутри цикла с помощью ключевых слов BREAK и CONTINUE. При выполнении CONTINUE цикл WHILE переходит к следующей итерации, игнорируя любые операторы в цикле после CONTINUE. BREAK, с другой стороны, полностью останавливает цикл WHILE, даже если логическое условие все еще истинно.

Курсоры

С помощью курсора вы можете прокручивать набор данных построчно. Курсоры имеют плохую репутацию в SQL Server, потому что они обрабатывают данные построчно, что может быть медленнее по сравнению с операциями, выполняемыми над наборами. Однако есть случаи, когда вам нужно реализовать сложную бизнес-логику, которую можно обрабатывать только построчно, или вам нужно пройтись по небольшому набору, где нет значительного влияния на производительность.

Вот пример использования курсора для реализации логики цикла:

DECLARE @i TINYINT;
DECLARE cursor_numbers CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY
FOR
    SELECT Number = 1
    UNION ALL
    SELECT 2
    UNION ALL
    SELECT 3
    UNION ALL
    SELECT 4
    UNION ALL
    SELECT 5
    UNION ALL
    SELECT 6
    UNION ALL
    SELECT 7
    UNION ALL
    SELECT 8
    UNION ALL
    SELECT 9
    UNION ALL
    SELECT 10;

OPEN [cursor_numbers];
FETCH NEXT FROM [cursor_numbers] INTO @i;

WHILE @@FETCH_STATUS = 0
BEGIN
    PRINT @i;
    FETCH NEXT FROM [cursor_numbers] INTO @i;
END

CLOSE [cursor_numbers];
DEALLOCATE [cursor_numbers];

В этом примере объявляется и открывается курсор с именем “cursor_numbers”. Оператор FETCH NEXT извлекает следующую строку из курсора и присваивает значение переменной @i. Цикл WHILE продолжается до тех пор, пока есть еще строки для извлечения. Оператор PRINT выводит значение @i. Наконец, курсор закрывается и удаляется.

Циклический обход объектов базы данных

Иногда вам нужно пройтись по объектам базы данных или сервера вместо фактических данных. Это можно сделать с помощью курсора или цикла WHILE в сочетании с системными представлениями. Например, вы можете захотеть переорганизовать все индексы в базе данных, создать резервную копию всех пользовательских баз данных или очистить часть таблиц в базе данных.

Вот пример использования курсора для очистки всех таблиц фактов в образцовой базе данных хранилища данных Adventure Works:

DECLARE @stmt VARCHAR(500);
DECLARE @i TINYINT = 1;
DECLARE @n TINYINT;

SELECT @n = MAX(ID)
FROM [#statements];

WHILE @i <= @n
BEGIN
    SELECT @stmt = [SQLStatement]
    FROM [#statements]
    WHERE ID = @i;

    PRINT @stmt;
    --EXEC sp_executesql @stmt; -- фактическое очищение таблицы
    SET @i += 1;
END

В этом примере используется курсор для обхода временной таблицы с именем “#statements”, которая содержит операторы TRUNCATE TABLE для таблиц фактов. Оператор PRINT выводит каждый оператор, а фактическое выполнение динамического SQL закомментировано для тестирования.

Таблица Tally или Numbers Table

Таблица Tally, также известная как таблица “numbers”, представляет собой таблицу с одним столбцом, содержащим последовательные числа. Ее можно использовать для реализации цикла FOR, рассматривая каждую строку таблицы чисел как итерацию цикла. Преимущество таблицы чисел заключается в том, что она позволяет выполнять операции над наборами, которые обычно выполняются быстрее в SQL Server.

Вот пример использования таблицы чисел для разделения строки на отдельные символы:

DECLARE @mystring VARCHAR(100) = 'Hello MSSQLTips!';

WITH E00(N) AS (SELECT 1 UNION ALL SELECT 1),
    E02(N) AS (SELECT 1 FROM E00 a, E00 b),
    E04(N) AS (SELECT 1 FROM E02
Click to rate this post!
[Total: 0 Average: 0]

Let's work together

Send us a message or book free introductory meeting with us using button below.