<?xml version="1.0" encoding="utf-8"?> 
<rss version="2.0"
  xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
  xmlns:atom="http://www.w3.org/2005/Atom">

<channel>

<title>Богдан Стефанюк: дописи з тегом sql</title>
<link>https://www.stefaniuk.website/tags/sql/</link>
<description>Всім привіт! Я — Богдан, фулстек розробник з Києва. Пишу в основному про програмування та штуки які вивчаю. Час від часу публікую підбірки фоток, зроблених на плівку, розказую про подорожі та цікаві речі, що оточують мене.</description>
<author></author>
<language>uk</language>
<generator>Aegea 11.2 (v4116)</generator>

<itunes:subtitle>Всім привіт! Я — Богдан, фулстек розробник з Києва. Пишу в основному про програмування та штуки які вивчаю. Час від часу публікую підбірки фоток, зроблених на плівку, розказую про подорожі та цікаві речі, що оточують мене.</itunes:subtitle>
<itunes:image href="" />
<itunes:explicit></itunes:explicit>

<item>
<title>Удаления дубликатов строк</title>
<guid isPermaLink="false">167</guid>
<link>https://www.stefaniuk.website/all/delete-duplicates-in-table/</link>
<pubDate>Wed, 13 Nov 2019 17:29:28 -0400</pubDate>
<author></author>
<comments>https://www.stefaniuk.website/all/delete-duplicates-in-table/</comments>
<description>
&lt;p&gt;Недавно мне попалась интересная задачка по SQL.&lt;br /&gt;
У вас есть таблица с 2 колонками: Id, Name. Но на колонках нет никаких ограничений. И получилось так что там если дубли и нам надо их удалить.&lt;/p&gt;
&lt;p&gt;Пример таблички&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://www.stefaniuk.website/pictures/TableWithDuplicates.png" width="168" height="136" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;В случае когда у нас нет ни одного уникального значения, мы можем использовать номер строки для идентификации записи для удаления.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;; WITH DuplicatesForDelete(Row) AS (
SELECT ROW_NUMBER() OVER(PARTITION BY t1.Id, t1.Name ORDER BY Id, Name) AS Row 
FROM TableWithDuplicates t1
)
DELETE FROM DuplicatesForDelete WHERE Row &amp;gt; 1&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;a href="https://www.fastreport.ru/ru/blog/251/show/"&gt;Статья о том что такое оконные функции (OVER) &lt;/a&gt;&lt;/p&gt;
</description>
</item>

<item>
<title>Полезные SQL скрипты: запросы, которые страдают от блокировок</title>
<guid isPermaLink="false">121</guid>
<link>https://www.stefaniuk.website/all/get-blocked-queries-on-sql-server/</link>
<pubDate>Tue, 05 Nov 2019 17:04:49 -0400</pubDate>
<author></author>
<comments>https://www.stefaniuk.website/all/get-blocked-queries-on-sql-server/</comments>
<description>
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;SELECT TOP 10
    [Average Time Blocked] = (total_elapsed_time - total_worker_time) / qs.execution_count,
    [Total Time Blocked] = total_elapsed_time - total_worker_time,
    [Execution count] = qs.execution_count,
    [Individual Query] = SUBSTRING (qt.text,qs.statement_start_offset/2,
        (CASE
        WHEN qs.statement_end_offset = -1 THEN LEN(CONVERT(NVARCHAR(MAX), qt.text)) * 2
        ELSE qs.statement_end_offset
        END - qs.statement_start_offset)/2),
    [Parent Query] = qt.text,
    [DatabaseName] = DB_NAME(qt.dbid)
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) as qt
ORDER BY [Average Time Blocked] DESC;&lt;/code&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>Получить информацию по индексу в таблице</title>
<guid isPermaLink="false">131</guid>
<link>https://www.stefaniuk.website/all/get-information-about-table-index-in-mssql/</link>
<pubDate>Fri, 25 Oct 2019 06:27:24 -0400</pubDate>
<author></author>
<comments>https://www.stefaniuk.website/all/get-information-about-table-index-in-mssql/</comments>
<description>
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;DECLARE @TableName varchar(255) = 'LeadInFolder'
DECLARE @TableIndexName varchar(255) = 'Icy1fjQeGXuR5svQcM20sAenE8U'

SELECT * FROM sys.dm_db_index_physical_stats(DB_ID(), OBJECT_ID(@TableName), NULL, NULL, NULL) ips
INNER JOIN sys.indexes i ON (ips.object_id = i.object_id) AND (ips.index_id = i.index_id)
WHERE i.name = @TableIndexName&lt;/code&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>Полезные SQL скрипты: неиспользуемые индексы</title>
<guid isPermaLink="false">120</guid>
<link>https://www.stefaniuk.website/all/get-not-used-indexes-in-mssql/</link>
<pubDate>Mon, 21 Oct 2019 04:57:03 -0400</pubDate>
<author></author>
<comments>https://www.stefaniuk.website/all/get-not-used-indexes-in-mssql/</comments>
<description>
&lt;p&gt;Скрипт показывает индексы, которы не участвуют в построении планов, но постоянно обноляються при изменении данных.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;SELECT DatabaseName = DB_NAME(),
    TableName = OBJECT_NAME(s.[object_id]),
    IndexName = i.name,
    user_updates,
    system_updates,
    'alter index [' +OBJECT_SCHEMA_NAME(i.object_id, DB_ID())+ '].['+i.name+'] ON ['+OBJECT_NAME(s.[object_id])+'] DISABLE' as [Disable],
    'exec sp_rename ''['+OBJECT_SCHEMA_NAME(i.object_id, DB_ID())+'].['+OBJECT_NAME(s.[object_id])+'].['+i.name+']'',''disable_'+i.name+''',''INDEX''' as [Rename]
FROM sys.dm_db_index_usage_stats s
INNER JOIN sys.indexes i ON s.object_id = i.object_id and
                            s.index_id  = i.index_id
WHERE s.database_id = DB_ID() and
    OBJECTPROPERTY(s.[object_id], 'IsMsShipped') = 0 and
    s.user_seeks   = 0 and
    s.user_scans   = 0 and
    s.user_lookups = 0 and
    i.is_disabled  = 0 and
    i.is_unique = 0 and
    i.is_primary_key = 0 and
    i.type_desc &amp;lt;&amp;gt; 'HEAP'
order by user_updates + system_updates desc&lt;/code&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>Полезные SQL скрипты: поиск foreign key по БД</title>
<guid isPermaLink="false">122</guid>
<link>https://www.stefaniuk.website/all/search-foreign-key-by-name/</link>
<pubDate>Sat, 28 Sep 2019 06:19:02 -0400</pubDate>
<author></author>
<comments>https://www.stefaniuk.website/all/search-foreign-key-by-name/</comments>
<description>
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;SELECT 
    f.name AS ForeignKey, 
    OBJECT_NAME(f.parent_object_id) AS TableName, 
    COL_NAME(fc.parent_object_id, fc.parent_column_id) AS ColumnName, 
    OBJECT_NAME (f.referenced_object_id) AS ReferenceTableName, 
    COL_NAME(fc.referenced_object_id, fc.referenced_column_id) AS ReferenceColumnName 
FROM 
    sys.foreign_keys AS f 
INNER JOIN 
    sys.foreign_key_columns AS fc 
ON 
    f.OBJECT_ID = fc.constraint_object_id 
WHERE 
    f.name = 'FKnokgcq1rxh5Y7DLhgCqIZJ4KY'&lt;/code&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>Полезные SQL скрипты: текущая ситуация на сервере</title>
<guid isPermaLink="false">119</guid>
<link>https://www.stefaniuk.website/all/poleznye-sql-skripty-tekuschaya-situaciya-na-servere/</link>
<pubDate>Fri, 27 Sep 2019 06:21:00 -0400</pubDate>
<author></author>
<comments>https://www.stefaniuk.website/all/poleznye-sql-skripty-tekuschaya-situaciya-na-servere/</comments>
<description>
&lt;p&gt;С помощью этих скрипов можно посмотреть какие запросы в данных момент выполняются на сервере.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;select *
from sys.sysprocesses 
where 
spid &amp;gt; 50 and 
spid &amp;lt;&amp;gt; @@spid and 
status &amp;lt;&amp;gt; 'sleeping'
order by spid, ecid&lt;/code&gt;&lt;/pre&gt;&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;select 
session_id, 
status, 
wait_type, 
command, 
last_wait_type, 
percent_complete, 
qt.text, 
total_elapsed_time/1000 as [total_elapsed_time, сек],
wait_time/1000 as [wait_time, сек],
(total_elapsed_time - wait_time)/1000 as [work_time, сек]
from sys.dm_exec_requests as qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) as qt
where session_id &amp;gt;= 50 and session_id &amp;lt;&amp;gt; @@spid&lt;/code&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>Небольшая заметка об индексах в ms sql</title>
<guid isPermaLink="false">140</guid>
<link>https://www.stefaniuk.website/all/indexes-in-ms-sql/</link>
<pubDate>Sat, 21 Sep 2019 06:20:03 -0400</pubDate>
<author></author>
<comments>https://www.stefaniuk.website/all/indexes-in-ms-sql/</comments>
<description>
&lt;p&gt;Индексы это важная часть большинства серверов баз данных. Они позволяют ускорить доступ к данным. Суть индексов хорошо описывает следующий пример: оглавление книги, оно позволяет найти нужную нам страницу вместо перебора всех страниц. Но у индексов есть недостатки: они заполняют дополнительное место (бывают случаи что индексы занимают в 3 раза больше места чем сами данные), также замедляются операции изменения данных, так как теперь нам надо менять еще и индексы.&lt;/p&gt;
&lt;h2&gt;Что из себя представляет индекс?&lt;/h2&gt;
&lt;p&gt;Внутри сервера они представлены в виде B-tree, где B означает сбалансированное, а не бинарное дерево.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://www.stefaniuk.website/pictures/b-tree-index-view.png" width="572" height="437" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;Например мы хотим найти запись с Id = 2581.&lt;br /&gt;
Начиная с корня, выполняется поиск наименьшего значения ключа, большего или равного требуемому. Так мы найдем узел 18316, потом спустимся в узел 9031 и там мы увидим что есть прямая ссылка на лежащие данные по ключу 2581, после чего осуществляем вычитку данных.&lt;/p&gt;
&lt;h2&gt;Кластерный индекс&lt;/h2&gt;
&lt;p&gt;Кластерный индекс позволяет определить порядок данных в таблице. Таким образом индекс содержит не только указатели на строки но и сами данных. Из этого выходит что у таблицы может быть только один кластерный индекс, так как нельзя физически упорядочить элементы более чем одним способом. Индекс создается автоматически для каждой таблицы, в которой определен первичный ключ.&lt;/p&gt;
&lt;h2&gt;Не кластерный индекс&lt;/h2&gt;
&lt;p&gt;Структура такая же как и кластерного индекса, но с двумя отличиями:&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Не изменяет физическое упорядочивание данных.&lt;/li&gt;
&lt;li&gt;Страницы индекса состоят из ключа индекса и ссылки на строку.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;SQL Server использует индекс для нахождения записей, совпадающих с условиями запроса.&lt;/p&gt;
&lt;h2&gt;Составной ключ в индексе&lt;/h2&gt;
&lt;p&gt;SQL Server позволяет создавать индексы по нескольким колонкам. Но в таком случае у нас появляется ограничение. Длинна составного ключа не должна быть больше 900 байт. Но бывают исключения, например у нас есть две колонки, каждая из которых длинной в 500 байт. Сервер создаст индекс, в случае если нет данных, которые будут превышать длину в 900 байт.&lt;/p&gt;
&lt;p&gt;Также стоит помнить что индексы типа (Col1, Col2) и (Col2, Col1) разные.&lt;/p&gt;
&lt;h2&gt;Уникальные индексы&lt;/h2&gt;
&lt;p&gt;Такие индексы создаются для реализации целостности данных. Таким образом сервер гарантирует уникальные значение для указанной колонки или составного ключа.&lt;/p&gt;
&lt;h2&gt;Статьи, которые советую почитать чтобы глубже разобраться в теме&lt;/h2&gt;
&lt;ol start="1"&gt;
&lt;li&gt;&lt;a href="https://professorweb.ru/my/sql-server/2012/level3/3_5.php"&gt;Очень хорошая статья о всех типах индексов, когда их стоит создавать и как использовать&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.sql.ru/articles/mssql/03013101indexes.shtml"&gt;Индексы. Теоретические основы.&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</description>
</item>

<item>
<title>Изучение SQL: рекурсивные запросы</title>
<guid isPermaLink="false">137</guid>
<link>https://www.stefaniuk.website/all/recursion-in-sql/</link>
<pubDate>Mon, 16 Sep 2019 16:00:05 -0400</pubDate>
<author></author>
<comments>https://www.stefaniuk.website/all/recursion-in-sql/</comments>
<description>
&lt;p&gt;Основой любого рекурсивного запроса является &lt;a href="https://metanit.com/sql/sqlserver/10.4.php"&gt;производная таблица&lt;/a&gt;.  С ее помощью мы можем сделать запрос. который будет выполняться до тех пор, пока не выполниться условие.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Общий вид рекурсивного запроса&lt;/b&gt;&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;WITH &amp;lt;имя&amp;gt; (&amp;lt;список столбцов&amp;gt;)
AS (
SELECT    -- анкорная часть
UNION ALL -- рекурсивная часть
        SELECT FROM &amp;lt;имя&amp;gt;
        WHERE &amp;lt;условие продолжения интерации&amp;gt;
)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Для того чтобы происходила рекурсия мы используем в рекурсивной части ссылку на самого себя.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Пример 1&lt;/b&gt;&lt;br /&gt;
У нас есть табличка, в которой лежат сотрудники, у каждого сотрудника есть руководитель, который указывается в колонку ParentId. Нам надо найти менеджера, в непрямом подчинении которого есть указанный нами человек.&lt;/p&gt;
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://www.stefaniuk.website/pictures/Screenshot_11.png" width="296" height="104" alt="" /&gt;
&lt;div class="e2-text-caption"&gt;Наполнение таблицы.&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Код запроса:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;;WITH OrgStructure AS 
(
SELECT Id, ParentId, EmployeeType, EmployeeName
FROM Employees
WHERE EmployeeName = 'Jim' -- отправная точка, нам надо найти менеджера сотрудника Jim

UNION ALL

SELECT e.Id, e.ParentId, e.EmployeeType, e.EmployeeName
FROM Employees as e
JOIN OrgStructure as os
ON e.Id = os.ParentId
)

SELECT * FROM OrgStructure
WHERE EmployeeType = 'manager' -- указываем что мы ищем менеджера.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;b&gt;Пример 2&lt;/b&gt;&lt;br /&gt;
Найдем первые 10 чисел Фибоначчи:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;;WITH FIBONACHI AS 
(
SELECT
1 Iteration,
1 SecondValue,
2 CurrentValue
UNION ALL

SELECT
Iteration + 1,
SecondValue = CurrentValue,
CurrentValue = SecondValue + CurrentValue
FROM FIBONACHI
WHERE Iteration &amp;lt; 10
)
SELECT CurrentValue FROM FIBONACHI&lt;/code&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>Шпаргалка по джоинам в SQL</title>
<guid isPermaLink="false">116</guid>
<link>https://www.stefaniuk.website/all/sql-join-cheetsheet/</link>
<pubDate>Tue, 20 Aug 2019 05:39:15 -0400</pubDate>
<author></author>
<comments>https://www.stefaniuk.website/all/sql-join-cheetsheet/</comments>
<description>
&lt;div class="e2-text-picture"&gt;
&lt;img src="https://www.stefaniuk.website/pictures/sql-join-cheat-sheet.png" width="600" height="472" alt="" /&gt;
&lt;/div&gt;
&lt;p&gt;А также &lt;a href="http://www.skillz.ru/dev/php/article-Obyasnenie_SQL_obedinenii_JOIN_INNER_OUTER.html"&gt;отличная статья&lt;/a&gt; которая раскрывает тему объединения данных.&lt;/p&gt;
</description>
</item>

<item>
<title>Изучение SQL: ключевое слово GO</title>
<guid isPermaLink="false">99</guid>
<link>https://www.stefaniuk.website/all/sql-go-statement/</link>
<pubDate>Fri, 02 Aug 2019 04:34:05 -0400</pubDate>
<author></author>
<comments>https://www.stefaniuk.website/all/sql-go-statement/</comments>
<description>
&lt;p&gt;SQL позволяет объединять несколько команд в один пакет, который будет выполняться вместе и представлять одно целое. В качестве сигнала завершения пакета и его выполнения служит команда GO. Смысл в том, что одни команды должны выполниться после выполнения предыдущих.  Тем самым мы можем строить последовательность команд.&lt;/p&gt;
&lt;p&gt;Например, создание БД и добавления в нее таблиц:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;CREATE DATABASE TestDB
GO

USE TestDB

CREATE TABLE Users
(
Id uniqueidintefier PRIMARY KEY IDENTITY,
FirstName varchar(50) NOT NULL
)&lt;/code&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>Изучение SQL: EXCEPT и INTERSECT</title>
<guid isPermaLink="false">50</guid>
<link>https://www.stefaniuk.website/all/learn-sql-except-i-intersect/</link>
<pubDate>Sun, 28 Apr 2019 11:28:28 -0400</pubDate>
<author></author>
<comments>https://www.stefaniuk.website/all/learn-sql-except-i-intersect/</comments>
<description>
&lt;h2&gt;EXCEPT&lt;/h2&gt;
&lt;p&gt;С помощью данной команды можно найти разницу двух выборок.&lt;br /&gt;
Результатом будут строки первой таблицы, которых нету во второй.&lt;/p&gt;
&lt;p&gt;Например нам нужно вывести только тех пользователей, которые не являются сотрудниками:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;SELECT Name, SecondName
FROM Contact
EXCEPT
SELECT Name, SecondName
FROM Employee&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;INTERSECT&lt;/h2&gt;
&lt;p&gt;Работает похожим образом как EXCEPT, только наоборот.&lt;br /&gt;
Позволяет найти общие записи для двух выборок.&lt;/p&gt;
&lt;p&gt;Выведем контакты, которые являются нашими сотрудниками.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;SELECT Name, SecondName
FROM Contact
INTERSECT
SELECT Name, SecondName
FROM Employee&lt;/code&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>Изучение SQL: UNION</title>
<guid isPermaLink="false">49</guid>
<link>https://www.stefaniuk.website/all/learn-sql-union/</link>
<pubDate>Sat, 27 Apr 2019 14:19:22 -0400</pubDate>
<author></author>
<comments>https://www.stefaniuk.website/all/learn-sql-union/</comments>
<description>
&lt;p&gt;Оператор UNION позволяет соединить две таблицы. Но в отличие от inner/outer join объединения соединяют не столбцы разных таблиц, а два однотипных набора в один.&lt;/p&gt;
&lt;p&gt;Также должно совпадать количество колонок и их тип, в противном случае будет ошибка.&lt;/p&gt;
&lt;p&gt;Пример:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;SELECT Name, Age 
FROM Contact
UNION 
SELECT Name, Age 
FROM Employees&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Также мы можем отсортировать выборку. Для этого нужно ориентироваться на имена колонок из первой выборки.&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;SELECT Name, Age as FirstAge
FROM Contact
UNION 
SELECT Name, Age as SecondAge 
FROM Employees
ORDER BY FirstAge&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Если при объединении буду дубли данных, они не будут отображаться, для того чтобы их отобразить, нужно использовать UNION ALL.&lt;/p&gt;
</description>
</item>

<item>
<title>Изучение SQL: Операторы GROUP BY и HAVING</title>
<guid isPermaLink="false">48</guid>
<link>https://www.stefaniuk.website/all/izuchenie-sql-operatory-group-by-i-having/</link>
<pubDate>Tue, 23 Apr 2019 10:40:43 -0400</pubDate>
<author></author>
<comments>https://www.stefaniuk.website/all/izuchenie-sql-operatory-group-by-i-having/</comments>
<description>
&lt;p&gt;Для группировки данных существуют 2 ключевых слова: GROUP BY и HAVING.&lt;/p&gt;
&lt;h2&gt;GROUP BY&lt;/h2&gt;
&lt;p&gt;Оператор GROUP BY определяет, как строки будут группироваться.&lt;/p&gt;
&lt;p&gt;Например сгруппировать контакты по стране:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;SELECT country, Count(*) AS UserByCountryCount
FROM Contacts
GROUP BY country&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;HAVING&lt;/h2&gt;
&lt;p&gt;Используется для фильтрации групп.&lt;/p&gt;
&lt;p&gt;Например, найдем страны в которых больше 100 пользователей:&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;SELECT country, COUNT(*) AS UserByCountryCount
FROM Contacts
GROUP BY country
HAVING COUNT(*) &amp;gt; 100&lt;/code&gt;&lt;/pre&gt;</description>
</item>

<item>
<title>Изучение SQL: триггеры</title>
<guid isPermaLink="false">32</guid>
<link>https://www.stefaniuk.website/all/sql-triggers/</link>
<pubDate>Wed, 27 Mar 2019 14:01:33 -0400</pubDate>
<author></author>
<comments>https://www.stefaniuk.website/all/sql-triggers/</comments>
<description>
&lt;p&gt;Сегодня на работе была задача по созданию триггеров на таблицу БД.&lt;br /&gt;
Суть заключалась в том, что надо сделать механизм, при котором update/insert в одну таблицу, переносил данные в другую таблицу, а предыдущую удалял.&lt;/p&gt;
&lt;p&gt;Так как я раньше не работал с триггерами, пришлось немного почитать и сделать для себя эту заметку.&lt;/p&gt;
&lt;h2&gt;Типы триггеров.&lt;/h2&gt;
&lt;ol start="1"&gt;
&lt;li&gt;&lt;b&gt;AFTER&lt;/b&gt; — выполняется после выполнения действия. Определяется только для таблиц. Можно определять несколько действий через запятую.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;INSTEAD OF&lt;/b&gt; — выполняется вместо действия (то есть по сути действие — добавление, изменение или удаление — вообще не выполняется). Определяется для таблиц и представлений. Можно определять только одно действие.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Создание простого триггера&lt;/h2&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;CREATE TRIGGER User_Insert_Update_TR
ON Users
AFTER INSERT, UPDATE -- сработает как для INSERT так и для UPDATE скриптов.
AS
BEGIN
    INSERT UsersLog (UserId)
    SELECT Id FROM inserted
END&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Управление триггером&lt;/h2&gt;
&lt;p&gt;Удаление&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;DROP TRIGGER User_Insert_Update_TR&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Отключение&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;DISABLE TRIGGER User_Insert_Update_TR ON Users&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Включение&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;ENABLE TRIGGER User_Insert_Update_TR ON Users&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Работа со значениями запроса&lt;/h2&gt;
&lt;p&gt;Внутри каждого триггера можно работать с данными, которые изменяет тот или иной триггер.&lt;/p&gt;
&lt;ol start="1"&gt;
&lt;li&gt;Добавление — виртуальная табличка INSERTED.&lt;/li&gt;
&lt;li&gt;Удаление — виртуальная табличка DELETED.&lt;/li&gt;
&lt;li&gt;Изменение — содержит 2 таблички.&lt;br /&gt;
INSERTED хранит строки после обновления.&lt;br /&gt;
DELETED до обновления.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;INSTEAD OF&lt;/h2&gt;
&lt;p&gt;Данный тип триггеров работает вместо операций с данными. Также его можно применять не только для таблиц но и для представлений.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Пример&lt;/b&gt;&lt;br /&gt;
Данный триггер не будет удалять данный из таблицы Users, он просто деактивирует запись&lt;/p&gt;
&lt;pre class="e2-text-code"&gt;&lt;code class=""&gt;CREATE TRIGGER User_Delete_TR
ON Users
INSTED OF DELETE
AS
BEGIN
    UPDATE Users
    SET IsActive = 0
    WHERE Id = (SELECT Id FROM deleted)
END&lt;/code&gt;&lt;/pre&gt;</description>
</item>


</channel>
</rss>