• Если ваш запрос выполняется подозрительно долго или выглядит зависшим, возможно, он попал в блокировку. Можно просто подождать в надежде, что тот, кто нас заблокировал, вскоре закончит работу и отпустит блокировку… Но обычно хочется узнать, кто же нас заблокировал.

    Разумеется, SQL Server отслеживает кто кого и почему блокирует. И у нас даже есть несколько инструментов, чтобы это увидеть. Самый простой способ — системное представление (DMV) sys.dm_Exec_Requests.

    Сначала нужно узнать номер нашего процесса (сеанса). Этот номер можно узнать, заглянув в системную переменную @@SPID:

    SELECT @@SPID
    

    Правда, если запрос уже выполняется, то опросить эту переменную вы не сможете. Но обычно этот номер видно в клиентском приложении:

    Теперь, зная номер нашего процесса, заглянем в sys.dm_Exec_Requests. В этом представлении уже содержится ответ на интересующий нас вопрос — столбец Blocking_Session_ID указывает на того, кто нас блокирует.

    Теперь можем посмотреть на атрибуты процесса, который нам мешает. Наиболее информативно в этом плане представление sys.dm_Exec_Sessions. Из него вы узнаете кто, с какого клиентского компьютера и из какого приложения запустил блокирующий процесс.

    Возьмите на вооружение эти DMV, они постоянно нужны в самых разнообразных административных сценариях:


    Если ваш запрос выполняется подозрительно долго или выглядит зависшим, возможно, он попал в блокировку. Можно просто подождать в надежде, что тот, кто нас заблокировал, вскоре закончит работу и отпустит блокировку… Но обычно хочется узнать, кто же нас заблокировал.

    Разумеется, SQL Server отслеживает кто кого и почему блокирует. И у нас даже есть несколько инструментов, чтобы это увидеть. Самый простой способ — системное представление (DMV) sys.dm_Exec_Requests.

    Сначала нужно узнать номер нашего процесса (сеанса). Этот номер можно узнать, заглянув в системную переменную @@SPID:

    SELECT @@SPID
    

    Правда, если запрос уже выполняется, то опросить эту переменную вы не сможете. Но обычно этот номер видно в клиентском приложении:

    Теперь, зная номер нашего процесса, заглянем в sys.dm_Exec_Requests. В этом представлении уже содержится ответ на интересующий нас вопрос — столбец Blocking_Session_ID указывает на того, кто нас блокирует.

    Теперь можем посмотреть на атрибуты процесса, который нам мешает. Наиболее информативно в этом плане представление sys.dm_Exec_Sessions. Из него вы узнаете кто, с какого клиентского компьютера и из какого приложения запустил блокирующий процесс.

    Возьмите на вооружение эти DMV, они постоянно нужны в самых разнообразных административных сценариях:


  • Работая в режиме командной строки, мы ожидаем от интерфейса быстрого доступа к истории выполненных команд. Кнопки «­­­⬆️» и «⬇️» мы считаем настолько естественными, что вспоминаем про них только, когда они не срабатывают. Но есть штука поудобнее этих кнопок.

    Это клавиша «F7».

    Теперь, когда вы знаете про эту клавишу, вам будет остро не хватать её, к примеру, в пауэр-шелле.

    Средствами PowerShell что-то подобное можно смастерить так:

    Invoke-Expression (Get-History | Out-GridView -PassThru).CommandLine
    


    Работая в режиме командной строки, мы ожидаем от интерфейса быстрого доступа к истории выполненных команд. Кнопки «­­­⬆️» и «⬇️» мы считаем настолько естественными, что вспоминаем про них только, когда они не срабатывают. Но есть штука поудобнее этих кнопок.

    Это клавиша «F7».

    Теперь, когда вы знаете про эту клавишу, вам будет остро не хватать её, к примеру, в пауэр-шелле.

    Средствами PowerShell что-то подобное можно смастерить так:

    Invoke-Expression (Get-History | Out-GridView -PassThru).CommandLine
    


  • Тренд Slow Living — это не только про жизнь, но и про ИТ.

    Вот команда, которую, я уверен, вы не знали. Но теперь вы уже не сможете без неё жить. Потому что она делает как раз то, что вам нужно — показывает структуру папок в виде дерева.

    Эта винтажная команда называется Tree.

    Тренд Slow Living — это не только про жизнь, но и про ИТ.

    Вот команда, которую, я уверен, вы не знали. Но теперь вы уже не сможете без неё жить. Потому что она делает как раз то, что вам нужно — показывает структуру папок в виде дерева.

    Эта винтажная команда называется Tree.

  • Бывает необходимо перенести файлы на новое место, сохранив все выданные на эти файлы разрешения. Как это сделать простым способом? Ведь при копировании в Проводнике все разрешения теряются. Да и при перемещении файлов информация о разрешениях переносится вместе с файлами только в пределах тома.

    Для сложного копирования файлов и структур каталогов предназначена штатная утилита XCopy. Она гибко настраивается и очень удобна для автоматизации процедур массового копирования файлов или обновления вложенных структур каталогов с файлами.

    Взгляните на пример использования XCopy:

    XCopy, конечно, выглядит более профессиональным инструментом, чем простые команды копирования. Но у Микрософта есть кое-что покруче.

    RoboCopy — вот то, что вам на самом деле нужно. Эта утилита умеет всё то же самое, а также то, что обычно требуется в сценариях файловой репликации:

    • восстанавливает копирование после разрыва связи,
    • регулирует полосу пропускания

    Описание RoboCopy на сайте Микрософта:

    Графический интерфейс для RoboCopy:



    Бывает необходимо перенести файлы на новое место, сохранив все выданные на эти файлы разрешения. Как это сделать простым способом? Ведь при копировании в Проводнике все разрешения теряются. Да и при перемещении файлов информация о разрешениях переносится вместе с файлами только в пределах тома.

    Для сложного копирования файлов и структур каталогов предназначена штатная утилита XCopy. Она гибко настраивается и очень удобна для автоматизации процедур массового копирования файлов или обновления вложенных структур каталогов с файлами.

    Взгляните на пример использования XCopy:

    XCopy, конечно, выглядит более профессиональным инструментом, чем простые команды копирования. Но у Микрософта есть кое-что покруче.

    RoboCopy — вот то, что вам на самом деле нужно. Эта утилита умеет всё то же самое, а также то, что обычно требуется в сценариях файловой репликации:

    • восстанавливает копирование после разрыва связи,
    • регулирует полосу пропускания

    Описание RoboCopy на сайте Микрософта:

    Графический интерфейс для RoboCopy:



  • Когда Илон Маск заходит в бар, все его посетители становятся в среднем миллиардерами! :)

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

    Вот классический пример — посмотрите на список сотрудников:

    Какова средняя зарплата в компании? Если использовать популярную функцию AVG, то получим среднее арифметическое — чуть больше миллиона рублей.

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

    А вот профсоюз работников скорее всего с такой статистикой не согласится. Ведь если оценивать «по честному», то зарплата в среднем чуть больше ста рублей!

    Правильное решение — использовать не среднее арифметическое, а медиану. Медианная зарплата — это цифра, которая делит список сотрудников ровно пополам: половина получает зарплату меньше медианы, а другая половина — больше.

    А теперь заново просмотрите все ваши аналитические SELECT’ы — наверняка во многих местах вы почувствуете необходимость заменить среднее арифметическое на медиану.

    В SQL вычисление медианы можно выполнить при помощи функции PERCENTILE_CONT. Эта функция рассчитывает процентили, так что для получения медианы нужно указать параметр 0,5.

    Конечно, у такого метода есть и недостатки, которые обязательно следует принять во внимание. Их два:

    Когда Илон Маск заходит в бар, все его посетители становятся в среднем миллиардерами! :)

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

    Вот классический пример — посмотрите на список сотрудников:

    Какова средняя зарплата в компании? Если использовать популярную функцию AVG, то получим среднее арифметическое — чуть больше миллиона рублей.

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

    А вот профсоюз работников скорее всего с такой статистикой не согласится. Ведь если оценивать «по честному», то зарплата в среднем чуть больше ста рублей!

    Правильное решение — использовать не среднее арифметическое, а медиану. Медианная зарплата — это цифра, которая делит список сотрудников ровно пополам: половина получает зарплату меньше медианы, а другая половина — больше.

    А теперь заново просмотрите все ваши аналитические SELECT’ы — наверняка во многих местах вы почувствуете необходимость заменить среднее арифметическое на медиану.

    В SQL вычисление медианы можно выполнить при помощи функции PERCENTILE_CONT. Эта функция рассчитывает процентили, так что для получения медианы нужно указать параметр 0,5.

    Конечно, у такого метода есть и недостатки, которые обязательно следует принять во внимание. Их два:

  • У каждого, кто работает с многомерными запросами, рано или поздно обязательно возникает желание задействовать переменные. Но, вот беда, переменных-то в языке MDX нет (или есть, но менеджмент-студия про них не знает)!

    Что же делать, когда очень хочется написать вот такую конструкцию:

    Есть два универсальных способа использования переменных в MDX-запросах. По крайней мере один из них вам обязательно подойдёт.

    Во-первых, вы можете формировать MDX-запрос динамически, причём средствами Transact-SQL. А для выполнения передавать его аналитическому серверу через механизм связанных серверов (Linked Server). Вот так:

    Здесь MDX_AW — это связанный SSAS-сервер, ссылающийся на вашу многомерную базу данных. Результат, разумеется, вы получите в табличном виде, как и положено в языке SQL. Кстати, обратите внимание на названия столбцов.

    Такое решение, конечно, нельзя назвать изящным — это классический костыль. Зато, он выручит вас в любой ситуации, это универсальный способ.

    Во-вторых, в некоторых ситуациях вы можете попробовать обойтись родными средствами MDX. Зачем вам понадобились переменные в MDX-сценарии? Скорее всего для параметризации запроса. А параметризовать запрос можно и штатными средствами, если переписать его в формате XMLA:

    Разумеется, для этого ваша аналитическая система должна уметь принимать XMLA-сценарии из приложения. А также учтите, что вам скорее всего придётся работать в XML-формате не только с самим запросом, но и с результатом:

    Зато, в отличие от варианта со связанным OLAP-сервером, вы получите настоящий многомерный результат, а не табличный.

    У каждого, кто работает с многомерными запросами, рано или поздно обязательно возникает желание задействовать переменные. Но, вот беда, переменных-то в языке MDX нет (или есть, но менеджмент-студия про них не знает)!

    Что же делать, когда очень хочется написать вот такую конструкцию:

    Есть два универсальных способа использования переменных в MDX-запросах. По крайней мере один из них вам обязательно подойдёт.

    Во-первых, вы можете формировать MDX-запрос динамически, причём средствами Transact-SQL. А для выполнения передавать его аналитическому серверу через механизм связанных серверов (Linked Server). Вот так:

    Здесь MDX_AW — это связанный SSAS-сервер, ссылающийся на вашу многомерную базу данных. Результат, разумеется, вы получите в табличном виде, как и положено в языке SQL. Кстати, обратите внимание на названия столбцов.

    Такое решение, конечно, нельзя назвать изящным — это классический костыль. Зато, он выручит вас в любой ситуации, это универсальный способ.

    Во-вторых, в некоторых ситуациях вы можете попробовать обойтись родными средствами MDX. Зачем вам понадобились переменные в MDX-сценарии? Скорее всего для параметризации запроса. А параметризовать запрос можно и штатными средствами, если переписать его в формате XMLA:

    Разумеется, для этого ваша аналитическая система должна уметь принимать XMLA-сценарии из приложения. А также учтите, что вам скорее всего придётся работать в XML-формате не только с самим запросом, но и с результатом:

    Зато, в отличие от варианта со связанным OLAP-сервером, вы получите настоящий многомерный результат, а не табличный.

  • Опытные разработчики и администраторы прекрасно знают, что никогда и ни при каких обстоятельствах нельзя назначать разрешения отдельным пользователям. Только группам или ролям!

    Это действительно правильный подход, но если вы его исповедуете, время от времени у вас будет возникать необходимость проверять членство конкретного пользователя (как правило, текущего) к той или иной группе или его принадлежность к конкретной роли.

    В SQL-сервере есть простые функции и представления для таких проверок.

    Для демонстрации этих механизмов я создам несколько ролей и групп разных типов. Разумеется, проверим, учитывает ли проверка вложенность. В моих примерах проверяемый пользователь входит только в самую глубоко вложенную группу/роль — № 3 (помимо членства в одной-двух стандартных).

    Роли базы данных

    Анализируя системные представления Sys.Database_Principals и Sys.Database_Role_Members, вы получите полную картину о членстве в группах.

    Однако, как правило, полная картина нам не требуется. Достаточно просто проверить принадлежность текущего пользователя к одной конкретной роли. Для такой задачи имеется более простая функция Is_Member. Удобно, что она сама умеет работать с вложенными ролями.

    У этой функции есть расширенная версия — Is_RoleMember, у которой есть второй параметр, через который можно указать идентификатор пользователя (database_principal), членство которого хотим проверить.

    Группы Active Directory

    Функция Is_Member умеет также работать с группами Active Directory. И тоже с учётом вложенности.

    Учтите, что эта функция ориентируется на SID’ы групп, рассчитанные при входе пользователя. Поэтому, если пользователь был включён в группу после соединения с сервером, придётся переподключиться.

    Опытные разработчики и администраторы прекрасно знают, что никогда и ни при каких обстоятельствах нельзя назначать разрешения отдельным пользователям. Только группам или ролям!

    Это действительно правильный подход, но если вы его исповедуете, время от времени у вас будет возникать необходимость проверять членство конкретного пользователя (как правило, текущего) к той или иной группе или его принадлежность к конкретной роли.

    В SQL-сервере есть простые функции и представления для таких проверок.

    Для демонстрации этих механизмов я создам несколько ролей и групп разных типов. Разумеется, проверим, учитывает ли проверка вложенность. В моих примерах проверяемый пользователь входит только в самую глубоко вложенную группу/роль — № 3 (помимо членства в одной-двух стандартных).

    Роли базы данных

    Анализируя системные представления Sys.Database_Principals и Sys.Database_Role_Members, вы получите полную картину о членстве в группах.

    Однако, как правило, полная картина нам не требуется. Достаточно просто проверить принадлежность текущего пользователя к одной конкретной роли. Для такой задачи имеется более простая функция Is_Member. Удобно, что она сама умеет работать с вложенными ролями.

    У этой функции есть расширенная версия — Is_RoleMember, у которой есть второй параметр, через который можно указать идентификатор пользователя (database_principal), членство которого хотим проверить.

    Группы Active Directory

    Функция Is_Member умеет также работать с группами Active Directory. И тоже с учётом вложенности.

    Учтите, что эта функция ориентируется на SID’ы групп, рассчитанные при входе пользователя. Поэтому, если пользователь был включён в группу после соединения с сервером, придётся переподключиться.

  • Не всякую базу данных можно перенести с Enterprise-редакции на SQL Server Standard Edition. Имеется специальный механизм, показывающий список задействованных enterprise-механизмов, мешающих миграции на стандартную редакцию.

    Приходилось ли вам при восстановлении базы данных из резервной копии или при присоединении файлов базы к серверу наблюдать сообщения об ошибке, при том, что на исходном сервере база восстанавливается/присоединяется нормально?

    Часто это происходит при попытке переноса базы с корпоративной (Enterprise Edition) на стандартную (Standatd Edition) редакцию SQL-сервера.

    Прежде чем осуществлять такой перенос, полезно проверить, а не используются ли в вашей базе механизмы, работающие только в корпоративной редакции SQL-сервера. Если задействована хоть одна enterprise-функция, то перенести базу на standard-редакцию не получится.

    А в случае, если вы твёрдо вознамерились перенести базу на сервер стандартной редакции, было бы здорово сразу увидеть список всех механизмов, которые придётся отключить.

    Проверить использование корпоративных механизмов, которые помешают такой миграции можно при помощи специального системного представления http://Sys.DM_DB_Persisted_SKU_Features.

    Вот так:


    Не всякую базу данных можно перенести с Enterprise-редакции на SQL Server Standard Edition. Имеется специальный механизм, показывающий список задействованных enterprise-механизмов, мешающих миграции на стандартную редакцию.

    Приходилось ли вам при восстановлении базы данных из резервной копии или при присоединении файлов базы к серверу наблюдать сообщения об ошибке, при том, что на исходном сервере база восстанавливается/присоединяется нормально?

    Часто это происходит при попытке переноса базы с корпоративной (Enterprise Edition) на стандартную (Standatd Edition) редакцию SQL-сервера.

    Прежде чем осуществлять такой перенос, полезно проверить, а не используются ли в вашей базе механизмы, работающие только в корпоративной редакции SQL-сервера. Если задействована хоть одна enterprise-функция, то перенести базу на standard-редакцию не получится.

    А в случае, если вы твёрдо вознамерились перенести базу на сервер стандартной редакции, было бы здорово сразу увидеть список всех механизмов, которые придётся отключить.

    Проверить использование корпоративных механизмов, которые помешают такой миграции можно при помощи специального системного представления http://Sys.DM_DB_Persisted_SKU_Features.

    Вот так:


  • В редакторе запросов SQL Server Management Studio можно исполнять не только SQL-код!

    Обратите внимание на пункт меню студии, который выделен на картинке:

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

    Во-первых, ничего не запуская и никуда не выходя, вы можете исполнять команды операционной системы (на клиентском компьютере). Для этого начните строку с двух восклицательных знаков:

    Только не выполняйте таким образом команды, требующие ввода или реакции пользователя! Например, тот же NetSh, запущенный без параметров, будет ожидать дальнейших указаний.

    Во-вторых, в одном сценарии теперь вы можете переключаться между разными серверами:

    В-третьих, теперь можно использовать макропеременные:

    И в четвёртых, так же, как и в самой утилите SQLCmd, можно работать с входными (сценарии) и выходными (результат) файлами:

    Как видите, в именах файлов тоже можно использовать макропеременные.

    В редакторе запросов SQL Server Management Studio можно исполнять не только SQL-код!

    Обратите внимание на пункт меню студии, который выделен на картинке:

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

    Во-первых, ничего не запуская и никуда не выходя, вы можете исполнять команды операционной системы (на клиентском компьютере). Для этого начните строку с двух восклицательных знаков:

    Только не выполняйте таким образом команды, требующие ввода или реакции пользователя! Например, тот же NetSh, запущенный без параметров, будет ожидать дальнейших указаний.

    Во-вторых, в одном сценарии теперь вы можете переключаться между разными серверами:

    В-третьих, теперь можно использовать макропеременные:

    И в четвёртых, так же, как и в самой утилите SQLCmd, можно работать с входными (сценарии) и выходными (результат) файлами:

    Как видите, в именах файлов тоже можно использовать макропеременные.

  • Современная методология проектирования многомерных баз данных подталкивает нас к объединению в одном кубе как можно большего количества разных групп мер и измерений. Одним из побочных эффектов такой консолидации является всё более и более разреженная матрица связей между размерностями и группами мер.

    Работая с многомерными базами данных, вы наверняка наблюдали такой эффект, когда мера не делится по некоторым измерениям.

    Вот я пытаюсь разложить показатели розничных продаж по реселлерам:

    Но ни количество проданных единиц товара, ни выручка не распадаются на слагаемые.

    Такой же результат я наблюдаю, подключившись к базе из экселя:

    В этом нет ничего странного, потому что я пытаюсь проделать некорректную операцию. Если заглянуть в структуру куба, то мы увидим, что размерность реселлеров вообще никак не связана с розничными продажами. Срезы по реселлерам и их атрибутам имеют смысл при анализе оптовых продаж. А прикладывать их к рознице нет никакого смысла. Поэтому сервер и не может разложить розничные меры по значениям чисто оптового атрибута Reseller.[Business Type].

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

    Хорошо, что мы помним, как устроены наши кубы и не будем пытаться в запросах резать меры по несуществующим связям. Однако, отсутствие сообщения об ошибке или какого-то предупреждения означает, что пользователи время от времени будут пытаться строить некорректные отчёты и тратить время на поиск ошибок.

    В конструкторе куба есть возможность устранить такую возможность. Это флаг IgnoreUnrelatedDimensions в настройках группы мер:

    По умолчанию он разрешает пользователю связывать в MDX-запросе что угодно с чем угодно.

    Полезно сбросить этот флаг для устранения возможности случайно или в результате ошибки получить некорректные данные в отчётах:

    Современная методология проектирования многомерных баз данных подталкивает нас к объединению в одном кубе как можно большего количества разных групп мер и измерений. Одним из побочных эффектов такой консолидации является всё более и более разреженная матрица связей между размерностями и группами мер.

    Работая с многомерными базами данных, вы наверняка наблюдали такой эффект, когда мера не делится по некоторым измерениям.

    Вот я пытаюсь разложить показатели розничных продаж по реселлерам:

    Но ни количество проданных единиц товара, ни выручка не распадаются на слагаемые.

    Такой же результат я наблюдаю, подключившись к базе из экселя:

    В этом нет ничего странного, потому что я пытаюсь проделать некорректную операцию. Если заглянуть в структуру куба, то мы увидим, что размерность реселлеров вообще никак не связана с розничными продажами. Срезы по реселлерам и их атрибутам имеют смысл при анализе оптовых продаж. А прикладывать их к рознице нет никакого смысла. Поэтому сервер и не может разложить розничные меры по значениям чисто оптового атрибута Reseller.[Business Type].

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

    Хорошо, что мы помним, как устроены наши кубы и не будем пытаться в запросах резать меры по несуществующим связям. Однако, отсутствие сообщения об ошибке или какого-то предупреждения означает, что пользователи время от времени будут пытаться строить некорректные отчёты и тратить время на поиск ошибок.

    В конструкторе куба есть возможность устранить такую возможность. Это флаг IgnoreUnrelatedDimensions в настройках группы мер:

    По умолчанию он разрешает пользователю связывать в MDX-запросе что угодно с чем угодно.

    Полезно сбросить этот флаг для устранения возможности случайно или в результате ошибки получить некорректные данные в отчётах:

  • Триггер входа — очень полезный механизм. Однако, обращаться с ним следует осторожно. LOGON-триггеры срабатывают ещё до того, как пользователь начнёт работу с SQL-сервером, поэтому если такой триггер написан неаккуратно или срабатывает с ошибкой, пользователи, включая администраторов, просто не смогут подключиться к серверу.

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

    Теперь при попытке установить соединение с SQL-сервером, получаем сообщение об ошибке, случившейся во время выполнения триггера входа:

    Если вы попали в такую ситуацию, придётся воспользоваться специальной лазейкой, которая позволяет администратору подключиться к SQL-серверу в обход LOGON-триггеров. Такое «сервисное» подключение доступно только членам серверной роли SysAdmin. Обратите внимание, что возможность удалённого сервисного подключения может быть отключена в настройках, тогда придётся работать прямо на сервере.

    В утилите SQLCmd воспользоваться сервисным подключением можно, указав параметр -A:

    А при подключении из студии используйте префикс ADMIN: перед именем сервера. Вот так:

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

    Триггер входа — очень полезный механизм. Однако, обращаться с ним следует осторожно. LOGON-триггеры срабатывают ещё до того, как пользователь начнёт работу с SQL-сервером, поэтому если такой триггер написан неаккуратно или срабатывает с ошибкой, пользователи, включая администраторов, просто не смогут подключиться к серверу.

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

    Теперь при попытке установить соединение с SQL-сервером, получаем сообщение об ошибке, случившейся во время выполнения триггера входа:

    Если вы попали в такую ситуацию, придётся воспользоваться специальной лазейкой, которая позволяет администратору подключиться к SQL-серверу в обход LOGON-триггеров. Такое «сервисное» подключение доступно только членам серверной роли SysAdmin. Обратите внимание, что возможность удалённого сервисного подключения может быть отключена в настройках, тогда придётся работать прямо на сервере.

    В утилите SQLCmd воспользоваться сервисным подключением можно, указав параметр -A:

    А при подключении из студии используйте префикс ADMIN: перед именем сервера. Вот так:

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

  • В интеграционных службах SQL-сервера не хватает полезной трансформации Row_Number. И встроенной функции, через которую можно было бы реализовать эту трансформацию, тоже нет. Так что, засучив рукава, придётся взяться за скрипты.

    Решение очень простое и гибкое — у вас не будет с ним никаких проблем. Готовый пакет прикреплён (см. ниже).

    В пакете демонстрируется два роунамбера:

    1. Простая расстановка номеров
    2. Расстановка номеров с секционированием

    Ниже пошаговое решение.

    Сначала сортируем строки в том порядке, в каком надо генерировать номера:

    Отсортированный поток заводим в Script Transformation и создаём в нём дополнительную колонку для роунамбера:

    Далее открываем редактор кода:

    Далее добавляем в сгенерированный шаблон кода пару строк:

    Всё.

    Ранжирование с секционированием чуть посложнее. Надо чуть больше кода написать — вот так:

    В интеграционных службах SQL-сервера не хватает полезной трансформации Row_Number. И встроенной функции, через которую можно было бы реализовать эту трансформацию, тоже нет. Так что, засучив рукава, придётся взяться за скрипты.

    Решение очень простое и гибкое — у вас не будет с ним никаких проблем. Готовый пакет прикреплён (см. ниже).

    В пакете демонстрируется два роунамбера:

    1. Простая расстановка номеров
    2. Расстановка номеров с секционированием

    Ниже пошаговое решение.

    Сначала сортируем строки в том порядке, в каком надо генерировать номера:

    Отсортированный поток заводим в Script Transformation и создаём в нём дополнительную колонку для роунамбера:

    Далее открываем редактор кода:

    Далее добавляем в сгенерированный шаблон кода пару строк:

    Всё.

    Ранжирование с секционированием чуть посложнее. Надо чуть больше кода написать — вот так:

  • SQL Server умеет работать c временными таблицами. Названия у таких таблиц начинаются с одной или с двух решёток (#). Временные таблицы, очевидно, придуманы для того, чтобы хранить в них данные временно. Например, для сохранения промежуточных результатов каких-либо вычислений. Или для передачи каких-то данных от одного процесса другому.

    Иногда требуется проверить, была ли уже создана определённая временная таблица или нет. Как это правильно сделать?

    Если речь идёт о глобальной временной таблице, то проверить её наличие можно традиционным способом — заглянув в список таблиц. Только не забудьте сделать поправку на то, что все временные таблицы — и глобальные и локальные — создаются не в текущей пользовательской базе данных, а в системной базе TempDB.

    Посмотрите на пример. Один процесс создал глобальную временную таблицу. Теперь этот и все другие процессы могут обращаться к ней, как к обыкновенной таблице. В том числе, они увидят её в системных каталогах sys.Tables и Information_Schema.Tables.

    Годится. Однако, такой механизм проверки не сработает, если речь идёт о локальных временных таблицах. Ведь такие таблицы создаются индивидуально для каждого сеанса, и в разных сеансах могут оказаться таблицы с одинаковыми названиями. Чтобы не было конфликта имён, сервер переименовывает локальные таблицы, создавая их в TempDB.

    Вот я создаю в двух сеансах локальные временные таблицы с одинаковыми названиями. В каждом сеансе сервер отличает свою таблицу от чужих, но в системных каталогах они видны все сразу:

    Если посмотреть внимательнее, то можно заметить, что к названиям таблиц сервер дописывает уникальное окончание, по которому он и определяет чья это таблица:

    Возиться с этими окончаниями в SQL-коде не хотелось бы. И не нужно. Потому что у нас есть функция Object_ID, которая автоматически выбирает нужную таблицу:

    Если объект с заданным названиям существует, то функция Object_ID вернёт его идентификатор, а если нет — NULL. Таким образом мы сможем удобно и надёжно проверить наличие даже такого необычного объекта, как локальная временная таблица:

    У этой функции есть необязательный второй параметр, который можно использовать для указания конкретного типа объекта. «U» означает, что мы ищем именно таблицу.

    SQL Server умеет работать c временными таблицами. Названия у таких таблиц начинаются с одной или с двух решёток (#). Временные таблицы, очевидно, придуманы для того, чтобы хранить в них данные временно. Например, для сохранения промежуточных результатов каких-либо вычислений. Или для передачи каких-то данных от одного процесса другому.

    Иногда требуется проверить, была ли уже создана определённая временная таблица или нет. Как это правильно сделать?

    Если речь идёт о глобальной временной таблице, то проверить её наличие можно традиционным способом — заглянув в список таблиц. Только не забудьте сделать поправку на то, что все временные таблицы — и глобальные и локальные — создаются не в текущей пользовательской базе данных, а в системной базе TempDB.

    Посмотрите на пример. Один процесс создал глобальную временную таблицу. Теперь этот и все другие процессы могут обращаться к ней, как к обыкновенной таблице. В том числе, они увидят её в системных каталогах sys.Tables и Information_Schema.Tables.

    Годится. Однако, такой механизм проверки не сработает, если речь идёт о локальных временных таблицах. Ведь такие таблицы создаются индивидуально для каждого сеанса, и в разных сеансах могут оказаться таблицы с одинаковыми названиями. Чтобы не было конфликта имён, сервер переименовывает локальные таблицы, создавая их в TempDB.

    Вот я создаю в двух сеансах локальные временные таблицы с одинаковыми названиями. В каждом сеансе сервер отличает свою таблицу от чужих, но в системных каталогах они видны все сразу:

    Если посмотреть внимательнее, то можно заметить, что к названиям таблиц сервер дописывает уникальное окончание, по которому он и определяет чья это таблица:

    Возиться с этими окончаниями в SQL-коде не хотелось бы. И не нужно. Потому что у нас есть функция Object_ID, которая автоматически выбирает нужную таблицу:

    Если объект с заданным названиям существует, то функция Object_ID вернёт его идентификатор, а если нет — NULL. Таким образом мы сможем удобно и надёжно проверить наличие даже такого необычного объекта, как локальная временная таблица:

    У этой функции есть необязательный второй параметр, который можно использовать для указания конкретного типа объекта. «U» означает, что мы ищем именно таблицу.

  • Если вы озабочены защитой данных, то в вашем распоряжении большой набор механизмов, предоставляемых СУБД. Однако, большинство из них работают только тогда, когда данные находятся в работающей базе. А что происходит, когда данные покидают SQL-сервер, например, после резервного копирования? Если злоумышленник украл резервную копию вашей базы данных, то, восстановив её на своём сервере, он увидит все ваши данные!

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

    SQL-сервер шифрует резервную копию ключом, который сохраняется вместе с самой резервной копией. Поэтому этот ключ тоже нужно зашифровать. Это делается при помощи сертификата, который хранится отдельно от базы и не попадает в резервную копию.

    Сам этот сертификат защищается мастер-ключом. Если вы раньше не использовали криптографию в SQL-сервере, то, возможно, мастер-ключа у вас тоже нет и его придётся создать. Вот так:

    Обратите внимание, что создав сертификат, я сразу сохранил его в файл. Этот файл понадобится, если вы захотите восстановить базу данных из зашифрованной резервной копии на другом сервере. Не потеряйте его!

    Всё, теперь можно создавать зашифрованные резервные копии. Если вы делаете это командой BACKUP DATABASE, то добавьте указание WITH ENCRYPTION:

    А если пользуетесь интерфейсом, то загляните на последнюю закладку:

    Посмотрим на результаты. Вот что получит злоумышленник, украв не защищённую резервную копию:

    Как видите, всё содержимое легко читается. А вот так выглядит зашифрованная копия:

    При планировании защиты резервных копий учтите, что такое шифрование имеется не во всех версиях SQL-сервера. Если вы пользуетесь версией младше 2014-й, то придётся либо шифровать саму базу, либо пользоваться дополнительными инструментами.

    Если вы озабочены защитой данных, то в вашем распоряжении большой набор механизмов, предоставляемых СУБД. Однако, большинство из них работают только тогда, когда данные находятся в работающей базе. А что происходит, когда данные покидают SQL-сервер, например, после резервного копирования? Если злоумышленник украл резервную копию вашей базы данных, то, восстановив её на своём сервере, он увидит все ваши данные!

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

    SQL-сервер шифрует резервную копию ключом, который сохраняется вместе с самой резервной копией. Поэтому этот ключ тоже нужно зашифровать. Это делается при помощи сертификата, который хранится отдельно от базы и не попадает в резервную копию.

    Сам этот сертификат защищается мастер-ключом. Если вы раньше не использовали криптографию в SQL-сервере, то, возможно, мастер-ключа у вас тоже нет и его придётся создать. Вот так:

    Обратите внимание, что создав сертификат, я сразу сохранил его в файл. Этот файл понадобится, если вы захотите восстановить базу данных из зашифрованной резервной копии на другом сервере. Не потеряйте его!

    Всё, теперь можно создавать зашифрованные резервные копии. Если вы делаете это командой BACKUP DATABASE, то добавьте указание WITH ENCRYPTION:

    А если пользуетесь интерфейсом, то загляните на последнюю закладку:

    Посмотрим на результаты. Вот что получит злоумышленник, украв не защищённую резервную копию:

    Как видите, всё содержимое легко читается. А вот так выглядит зашифрованная копия:

    При планировании защиты резервных копий учтите, что такое шифрование имеется не во всех версиях SQL-сервера. Если вы пользуетесь версией младше 2014-й, то придётся либо шифровать саму базу, либо пользоваться дополнительными инструментами.

  • Начиная с 2008-й версии, администраторы Windows уже не являются по умолчанию администраторами SQL-сервера. Это означает, что может возникнуть ситуация, при которой нет ни одной учётной записи, способной управлять SQL-сервером.

    Например, вы назначили какую-то учётную запись Windows администратором SQL-сервера, а потом эту учётную запись удалили. Или забыли пароли ко всем учётным записям, которые исполняли роль sysadmin.

    Это не просто теоретическая возможность! На практике я периодически сталкиваюсь с SQL-серверами, над которыми потерян контроль. То есть сам по себе SQL-сервер работает нормально, все базы данных функционируют, но никто не может этим сервером управлять.

    Для выхода из такой ситуации Микрософт оставил специальную лазейку.

    Найдите в списке windows-служб SQL Server и откройте окно со свойствами:

    Остановите службу:

    Когда служба остановится, на той же закладке окна свойств службы откроется возможность задать дополнительные параметры здя запуска службы. Введите в это поле параметр «-m» и снова запустите службу.

    Запущенный таким образом SQL-сервер позволит подключиться локальному администратору независимо от настроек аутентификации. Вы можете от имени локального администратора запустить Management Studio или SQLCmd и восстановить административные учётные записи. После этого перезапустите службу в обычном режиме (без параметра «-m»)

    Начиная с 2008-й версии, администраторы Windows уже не являются по умолчанию администраторами SQL-сервера. Это означает, что может возникнуть ситуация, при которой нет ни одной учётной записи, способной управлять SQL-сервером.

    Например, вы назначили какую-то учётную запись Windows администратором SQL-сервера, а потом эту учётную запись удалили. Или забыли пароли ко всем учётным записям, которые исполняли роль sysadmin.

    Это не просто теоретическая возможность! На практике я периодически сталкиваюсь с SQL-серверами, над которыми потерян контроль. То есть сам по себе SQL-сервер работает нормально, все базы данных функционируют, но никто не может этим сервером управлять.

    Для выхода из такой ситуации Микрософт оставил специальную лазейку.

    Найдите в списке windows-служб SQL Server и откройте окно со свойствами:

    Остановите службу:

    Когда служба остановится, на той же закладке окна свойств службы откроется возможность задать дополнительные параметры здя запуска службы. Введите в это поле параметр «-m» и снова запустите службу.

    Запущенный таким образом SQL-сервер позволит подключиться локальному администратору независимо от настроек аутентификации. Вы можете от имени локального администратора запустить Management Studio или SQLCmd и восстановить административные учётные записи. После этого перезапустите службу в обычном режиме (без параметра «-m»)

  • Администраторам часто бывает необходимо перебрать все таблицы базы данных, чтобы над каждой таблицей произвести какое-то действие. Например, перестроить индексы.

    Традиционно для такого перебора можно использовать курсор. Но есть способ проще - процедура sp_MSForEachTable. Ей можно передать текст команды или запроса, который будет выполнен для каждой таблицы в базе. Команда, разумеется, параметризуется - вместо вопросительного знака будет подставлено название таблицы.

    Посмотрите на сигнатуру этой процедуры:

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

    Каждая из трёх команд может содержать больше одного SQL-запроса. При написании фильтра @WhereAnd учтите, что ваша строка будет встроена внутри процедуры в более сложный запрос к системным таблицам, поэтому используйте для фильтрации столбцы из SysObjects. Например:

    Имеется аналогичная процедура для перебора всех баз данных на сервере - sp_MSForEachDB:

    С её помощью вы сможете выполнить однотипный набор действий над каждой базой на сервере:


    Администраторам часто бывает необходимо перебрать все таблицы базы данных, чтобы над каждой таблицей произвести какое-то действие. Например, перестроить индексы.

    Традиционно для такого перебора можно использовать курсор. Но есть способ проще - процедура sp_MSForEachTable. Ей можно передать текст команды или запроса, который будет выполнен для каждой таблицы в базе. Команда, разумеется, параметризуется - вместо вопросительного знака будет подставлено название таблицы.

    Посмотрите на сигнатуру этой процедуры:

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

    Каждая из трёх команд может содержать больше одного SQL-запроса. При написании фильтра @WhereAnd учтите, что ваша строка будет встроена внутри процедуры в более сложный запрос к системным таблицам, поэтому используйте для фильтрации столбцы из SysObjects. Например:

    Имеется аналогичная процедура для перебора всех баз данных на сервере - sp_MSForEachDB:

    С её помощью вы сможете выполнить однотипный набор действий над каждой базой на сервере:


  • При работе с данными часто приходится использовать «строки соединения с источником данных» (connection string). Синтаксис этих строк и набор параметров, которые можно в них использовать, зависит от типа хранилища и версии драйвера. Держать всё это в уме проблематично.

    К счастью, есть способ быстро и без ошибок сконструировать такую строку. Редактор для работы со строками соединения уже имеется в Windows.

    Создайте пустой файл с расширением «UDL»:

    По двойному щелчку на UDL-файле по умолчанию открывается конструктор соединений. Он-то нам и нужен! Здесь мы удобным образом настраиваем все параметры подключения, причём этот редактор использует свой вариант интерфейса для каждого драйвера.

    Когда всё настроили, нажмите кнопку проверки соединения, чтобы убедиться, что подключение проходит, а затем кнопку «Ок» для сохранения настроек в файле.

    UDL — это обыкновенный текстовый формат. Откройте теперь этот файл блокнотом…

    …и получите готовую строку соединения:


    При работе с данными часто приходится использовать «строки соединения с источником данных» (connection string). Синтаксис этих строк и набор параметров, которые можно в них использовать, зависит от типа хранилища и версии драйвера. Держать всё это в уме проблематично.

    К счастью, есть способ быстро и без ошибок сконструировать такую строку. Редактор для работы со строками соединения уже имеется в Windows.

    Создайте пустой файл с расширением «UDL»:

    По двойному щелчку на UDL-файле по умолчанию открывается конструктор соединений. Он-то нам и нужен! Здесь мы удобным образом настраиваем все параметры подключения, причём этот редактор использует свой вариант интерфейса для каждого драйвера.

    Когда всё настроили, нажмите кнопку проверки соединения, чтобы убедиться, что подключение проходит, а затем кнопку «Ок» для сохранения настроек в файле.

    UDL — это обыкновенный текстовый формат. Откройте теперь этот файл блокнотом…

    …и получите готовую строку соединения:


  • Это классическая задача по созданию списка с разделителями. При работе с многомерной базой данных такой список вы вероятнее всего захотите сгенерировать из элементов множества. Собрать такую строку в MDX-запросе очень просто!

    Итак, при помощи MDX-запроса мы сформировали желаемый список. Но что если приложению непременно требуется этот список в виде строки с разделителями? Например, в отчёте требуется отобразить найденные элементы через запятую. Или такой формат необходим для передачи результата в какую-то другую систему для дальнейшей обработки.

    Как из элементов списка (столбца, набора) собрать длинную строку с разделителями?

    Обычно в такие моменты сразу вспоминается функция StrToSet, придуманная специально для этого. Она принимает на вход множество, а возвращает строку, в которой элементы перечислены через запятую.

    Однако, не торопитесь задействовать StrToSet! Работая с этой функцией, вам каждый раз придётся решать четыре сопутствующих проблемы:

    1. StrToSet заключает результирующую строку в фигурные скобки, которые нам скорее всего не нужны. Надо не забыть от них избавиться.
    2. Вы скорее всего хотели получить список из названий или значений элементов множества. А StrToSet сконвертирует в строку не названия, а ключи. Придётся подумать, как заменить их на требуемые имена.
    3. Разделитель строки, возвращаемой StrToSet — всегда запятая. Если нужно что-то другое, потребуется дополнительная обработка.
    4. И, конечно, надо будет избавиться от обрамления в виде префиксов и квадратных скобок.

    Есть другой способ решения этой задачи. Гораздо более удобный и управляемый. Один из режимов работы функции Generate как раз предназначен для формирования длинной строки с разделителями. При этом вы сами выбираете и значения и разделители.

    Вот так:

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

    Вот ещё пара примеров.

    Давайте составим список не из названий, а из произвольных свойств:

    Это классическая задача по созданию списка с разделителями. При работе с многомерной базой данных такой список вы вероятнее всего захотите сгенерировать из элементов множества. Собрать такую строку в MDX-запросе очень просто!

    Итак, при помощи MDX-запроса мы сформировали желаемый список. Но что если приложению непременно требуется этот список в виде строки с разделителями? Например, в отчёте требуется отобразить найденные элементы через запятую. Или такой формат необходим для передачи результата в какую-то другую систему для дальнейшей обработки.

    Как из элементов списка (столбца, набора) собрать длинную строку с разделителями?

    Обычно в такие моменты сразу вспоминается функция StrToSet, придуманная специально для этого. Она принимает на вход множество, а возвращает строку, в которой элементы перечислены через запятую.

    Однако, не торопитесь задействовать StrToSet! Работая с этой функцией, вам каждый раз придётся решать четыре сопутствующих проблемы:

    1. StrToSet заключает результирующую строку в фигурные скобки, которые нам скорее всего не нужны. Надо не забыть от них избавиться.
    2. Вы скорее всего хотели получить список из названий или значений элементов множества. А StrToSet сконвертирует в строку не названия, а ключи. Придётся подумать, как заменить их на требуемые имена.
    3. Разделитель строки, возвращаемой StrToSet — всегда запятая. Если нужно что-то другое, потребуется дополнительная обработка.
    4. И, конечно, надо будет избавиться от обрамления в виде префиксов и квадратных скобок.

    Есть другой способ решения этой задачи. Гораздо более удобный и управляемый. Один из режимов работы функции Generate как раз предназначен для формирования длинной строки с разделителями. При этом вы сами выбираете и значения и разделители.

    Вот так:

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

    Вот ещё пара примеров.

    Давайте составим список не из названий, а из произвольных свойств:

  • В современных инструментах визуализации можно запросто упаковать в одну страницу отчёта сразу дюжину факторов. Для этого используются специальные редуцированные версии диаграмм — спарклайны, встраиваемые гистограммы и индикаторы. Но работая с этими инструментами, следует всегда помнить про контекст.

    В отчётных службах SQL-сервера имеются удобные виды диаграмм, специально предназначенные для встраивания в таблицы и списки. Это спарклайны, гистограммы (датабары) и индикаторы. Они предельно компактны и хорошо подходят для сложных визуализаций с большим количеством факторов.

    Назначение таких «упрощённых» компонентов — помочь читателю быстрее сориентироваться в отчёте и сфокусироваться на самом важном. Однако, имеется один подводный камень, который запросто может всё испортить.

    Посмотрите как в течение года изменялась выручка от продаж в разных странах:

    Благодаря спарклайнам у меня получился очень компактный и наглядный отчёт.

    А как вы думаете, где выручка была больше: в Канаде или в США?

    Кажется, что разница в объёмах продаж по этим странам различается не существенно. Хотя, конечно, надо бы посмотреть на числовые показатели, чтобы сказать наверняка. Стоп! Ведь мы и заморочились этими спарклайнами как раз ради того, чтобы сделать отчёт более компактным и не перегружать его числами!

    Давайте посмотрим на внутренности нашего спарклайна:

    Зайдя в настройки вертикальной оси, мы обнаружим, что синхронизация шкалы между спарклайнами по умолчанию отключена. А это означает, что верхняя точка на каждом графике означает совершенно разные значения! Получается, такой спарклайн нельзя сравнивать с соседними графиками, но это странно — ведь я как раз хотел, чтобы у читателя с первого взгляда сложилась картина соотношения показателей продаж по разным странам.

    А теперь давайте синхронизируем шкалу по вертикальной оси:

    Совсем другое дело! Теперь сразу понятно как идут дела в США, а как в Канаде:

    В современных инструментах визуализации можно запросто упаковать в одну страницу отчёта сразу дюжину факторов. Для этого используются специальные редуцированные версии диаграмм — спарклайны, встраиваемые гистограммы и индикаторы. Но работая с этими инструментами, следует всегда помнить про контекст.

    В отчётных службах SQL-сервера имеются удобные виды диаграмм, специально предназначенные для встраивания в таблицы и списки. Это спарклайны, гистограммы (датабары) и индикаторы. Они предельно компактны и хорошо подходят для сложных визуализаций с большим количеством факторов.

    Назначение таких «упрощённых» компонентов — помочь читателю быстрее сориентироваться в отчёте и сфокусироваться на самом важном. Однако, имеется один подводный камень, который запросто может всё испортить.

    Посмотрите как в течение года изменялась выручка от продаж в разных странах:

    Благодаря спарклайнам у меня получился очень компактный и наглядный отчёт.

    А как вы думаете, где выручка была больше: в Канаде или в США?

    Кажется, что разница в объёмах продаж по этим странам различается не существенно. Хотя, конечно, надо бы посмотреть на числовые показатели, чтобы сказать наверняка. Стоп! Ведь мы и заморочились этими спарклайнами как раз ради того, чтобы сделать отчёт более компактным и не перегружать его числами!

    Давайте посмотрим на внутренности нашего спарклайна:

    Зайдя в настройки вертикальной оси, мы обнаружим, что синхронизация шкалы между спарклайнами по умолчанию отключена. А это означает, что верхняя точка на каждом графике означает совершенно разные значения! Получается, такой спарклайн нельзя сравнивать с соседними графиками, но это странно — ведь я как раз хотел, чтобы у читателя с первого взгляда сложилась картина соотношения показателей продаж по разным странам.

    А теперь давайте синхронизируем шкалу по вертикальной оси:

    Совсем другое дело! Теперь сразу понятно как идут дела в США, а как в Канаде:

  • В реляционной базе данных нельзя назначать права на строки. Поэтому, для защиты отдельных строк часто используют представление (VIEW) с привязкой к идентификатору текущего пользователя. Это, пожалуй, самый распространённый, но далеко не единственный сценарий использования в коде обращений к текущему контексту безопасности.

    Как отлаживать такой код? Можно открыть несколько соединений с сервером от имени разных пользователей и смотреть на результаты одного и того же запроса в разных окнах. Но если это приходится делать по многу раз, переключение между соединениями начинает утомлять и отвлекать.

    Альтернативный вариант — создайте для пользователя свой собственный связанный сервер (Linked Server), который ссылается сам на себя. Вот так:

    И в настройках этого связанного сервера укажите контекст безопасности, в котором хотите провести отладку:


    Теперь можно смотреть на данные глазами разных пользователей через одно подключение и даже из одного окна:

    Описанный приём — не единственный. Но он самый простой и не требует вмешательства в саму отлаживаемую базу данных.

    В реляционной базе данных нельзя назначать права на строки. Поэтому, для защиты отдельных строк часто используют представление (VIEW) с привязкой к идентификатору текущего пользователя. Это, пожалуй, самый распространённый, но далеко не единственный сценарий использования в коде обращений к текущему контексту безопасности.

    Как отлаживать такой код? Можно открыть несколько соединений с сервером от имени разных пользователей и смотреть на результаты одного и того же запроса в разных окнах. Но если это приходится делать по многу раз, переключение между соединениями начинает утомлять и отвлекать.

    Альтернативный вариант — создайте для пользователя свой собственный связанный сервер (Linked Server), который ссылается сам на себя. Вот так:

    И в настройках этого связанного сервера укажите контекст безопасности, в котором хотите провести отладку:


    Теперь можно смотреть на данные глазами разных пользователей через одно подключение и даже из одного окна:

    Описанный приём — не единственный. Но он самый простой и не требует вмешательства в саму отлаживаемую базу данных.