29 сентября 2012 г.

Разработка REST сервиса при помощи WCF.


Всем привет! Сегодня мы поговорим о подходе для разработки сетевой архитектуры под названием REST и способах его реализации на платформе .NET при помощи Windows Communication Foundation.
Для начала, разберемся, что такое REST и с чем его едят. В основе архитектуры REST лежит 3 основополагающих принципа:
  1. Протокол передачи данных HTTP. Для обмена данными между клиентом и сервисом используются классические запросы с командами HTTP: GET, POST, PUT, DELETE (получение, добавление, обновление и удаление данных соответственно);
  2. Ресурсно-ориентированная архитектура. Взаимодействие с сервисом – это обмен данными между клиентом и определенным ресурсом, расположенным на сервисе. При этом каждый ресурс обладает уникальным адресом (URI). Не правда ли, очень похоже на схему функционирования сети Интернет в целом?
  3. Формат передаваемых данных: XML или JSON.

15 сентября 2012 г.

Шаблоны T4 в Visual Studio: Расширенные возможности


В прошлой статье мы познакомились с механизмом генерации текста на основе шаблонов, который предлагает нам Visual Studio и .NET Framework под именем Т4. Мы рассмотрели один из самых распространенных сценариев использования шаблонов – генерация письма, с передачей параметров в шаблон. В этой статье мы обсудим другие функциональные возможности шаблонов Т4, которые могут сделать вашу работу с ними еще более удобной.

8 сентября 2012 г.

Шаблоны T4 в Visual Studio


Всем привет! Сегодня я хочу рассказать вам о замечательной технологии, которую предоставляет разработчикам Visual Studio – среда разработки шаблонов, известная под кодовым именем Т4.
Наверняка многие из вас сталкивались с такой очень распространенной задачей, как генерация писем. Одним из самых распространенных решений для таких задач являются XSLT трансформации. Вы определяете шаблон письма, включая различные операторы XSLT и затем вызываете специальный генератор, который осуществляет трансформацию, на основе переданных данных для письма. Данная технология отлично работала, и я сам ею пользовался, пока не столкнулся с механизмом Т4.

2 сентября 2012 г.

Использование привязки Duplex Binding в WCF


Добрый день. Сегодня мы рассмотрим один из нестандартных типов связи, который предоставляет разработчикам Windows Communication Foundation - двухстороння связь или Duplex mode.
Классической схемой взаимодействия клиента с сервисом является режим запрос - ответ. Клиент посылает сервису запрос и немедленно получает от сервиса ответ с данными. Однако такая схема не всегда способна удовлетворить потребности разработчика, особенно при разработке сервисных систем со сложной архитектурой.

23 июля 2012 г.

Работа с XML данными в Microsoft SQL Server 2008. Особенности работы с XML.


В прошлой статье мы познакомились с типом данных «XML» в Microsoft SQL Server 2008, научились писать разнообразные запросы для выборки данных из XML документов. Однако на этом возможности этого типа данных не исчерпываются. Кроме операций для выборки данных, также предоставляются функции для добавления, обновления и удаления XML данных.
Все вышеперечисленные операции можно выполнить при помощи функции modify().

17 июля 2012 г.

Работа с XML данными в Microsoft SQL Server 2008. Тип данных «xml».



В прошлой статье мы узнали, каким образом из базы можно извлекать реляционные данные в виде XML структуры. Сегодня мы познакомимся с принципами хранения структуры XML данных в базе.
Основной элемент поддержки XML в MS SQL Server – тип данных “xml”. Данный тип может использоваться для объявления переменных и как тип колонки в таблице наравне со стандартными типами данных SQL (int, nvarchar и т.д.).
Для типа данных XML доступны следующие специализированные операции. Приведем их краткое описание:
  1. query() – осуществляет запросы к XML данным;
  2. nodes() – извлекает поддерево из структуры XML.
  3. value() – позволяет извлекать значения атрибутов из XML элемента;
  4. exist() – проверяет существуют ли результаты запроса;
  5. modify() – производит обновление XML данных;

8 июля 2012 г.

Работа с XML в Microsoft SQL Server 2008. Выборка данных в формате XML.


Всем привет! В этой статье мы поговорим о типе представления данных XML и рассмотрим, какие средства предоставляет Microsoft SQL Server 2008 для работы с XML. Для начала, давайте вспомним, что такое XML и как он выглядит. Язык XML – один из самых распространенных форматов представления иерархических данных практических во всех платформах и технологиях. В основе веб-страниц лежит язык HTML, который является подвидом XML. В основе сервисов SOAP лежит обмен данными в формате XML. Данное представление, также пользуется популярностью на многих платформах в качестве хранилища данных.

24 июня 2012 г.

Использование SQL CLR в Microsoft SQL Server 2008


Microsoft SQL Server и язык T -SQL обладают достаточно широким набором команд, параметров и функций, которые позволяют разработчику выполнять практически любые запросы к базам данных. Однако, в некоторых ситуациях данной инфраструктуры оказывается недостаточно для удовлетворения самых изощренных потребностей разработчиков. Здесь на помощь приходит расширение SQL Server под названием SQL CLR.
SQL CLR – это технология, позволяющая значительно увеличить гибкость и расширить возможности разработчиков при работе с базами данных в SQL Server. При помощи SQL CLR возможно писать собственные функции, хранимые процедуры и триггеры на языках программирования C# или Visual Basic, а затем импортировать этот код в среду SQL Server и использовать его при написании запросов на языке T-SQL. Поскольку такой язык как C# предоставляет разработчику гораздо более широкий набор возможностей, чем T-SQL, импортирование C# функций в SQL Server будет отличным решением многих задач, требующих специфичных вычислений.

17 июня 2012 г.

Управление транзакциями в C#


В прошлой статье мы познакомились с таким понятием, как транзакция. Было рассмотрено, каким образом можно управлять транзакциями на уровне SQL Server. Сегодня мы научимся создавать и использовать транзакции из программного кода C#.
В .NET существуют два типа транзакций: локальная транзакция и распределенная транзакция. Локальные транзакции работают в пределах одного соединения к базе данных, а распределенные способны отслеживать команды к нескольким источникам данных одновременно.

10 июня 2012 г.

Управление транзакциями в Microsoft SQL Server 2008


Сегодня мы обсудим одно из фундаментальных понятий в SQL Server 2008 – транзакции. Транзакции позволяют управлять набором команд, которые выполняются в базе данных, как единым целым. Использование транзакции предоставляют разработчику следующие преимущества:
  1. Если две команды работают внутри одной транзакции – то будут выполнены обе команды, либо ни одной;
  2. Если какая-либо команда внутри транзакции завершится с ошибкой – все команды транзакции будут отмены и данные вернутся в то состояние, в каком они были до начала транзакции;
  3. Во время выполнения транзакции данные могут оставаться изолированными и недоступными для других транзакций;
  4. После успешного выполнения команды фиксации транзакции данные гарантированно сохранятся в базе.

18 мая 2012 г.

Windows Live SDK: Аутентификация пользователя на сайте

 

    Здравствуйте друзья! Сегодня я бы хотел вам рассказть о наборе инструментов, который позволяет разработчикам легко организовать взаимодействие их приложений с сервисами семейства Windows Live от компании Microsoft.

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

    Провайдеры внешних сервисов аутентификации (в первую очередь конечно социальные сети) предоставляют широкий набор средств (API) для использования их возможностей извне.

    Не осталась в стороне и компания Microsoft. И хотя сервисы Windows Live нельзя назвать социальной сетью, это достаточно распостраненная система аутентификации для всех, кто так или иначе взаидоействует с ресурсами компании Microsoft.

    В 2012 году Microsoft выпускает первые версии новой операционной системы Windows 8 и анонсирует новую концепцию разработки приложений для платформы Windows – Metro приложения написанные при помощи HTML и JavaScript.

    В связи с этим Майкрософт также выпускает новый пакет средств (SDK) для работы с сервисами Windows Live из Metro приложений при помощи JavaScript. Надо отметить, что сервисы Windows Live существует достаточно давно, и, конечно, раньше тоже существовали средства для работы с ними. Однако, назвать их удобными и понятными явно было нельзя. Поэтому новый SDK приятно всех удивил своей простотой и схожестью с похожимы интерфейсами Twitter и Facebook.

   Сегодня, я хочу вам показать каким образом можно интегрировать аутетификацию через Windows Live в ваш ASP.NET MVC сайт.

  Первым делом необходимо зарегистрировать ваше приложение в центре разработки Windows Live по следующей ссылке: https://manage.dev.live.com/Applications/Index

Необходимо ввести название приложения, адрес возврата ответа от сервиса аутентификации (доменное имя вашего сайта) и скопировать значение “Client ID”. Примеры форм находятся на скриншотах ниже:

 

 

 

 

 

 

 

 

  

   Создадим стандартный сайт шаблона ASP.NET MVC Framework и перейдем в представление Index.cshtml.

   Для использования Windows Live JavaScript SDK необходимо разместить на странице ссылку на следующий скрипт:

<script type="text/javascript" src="http://js.live.net/v5.0/wl.js"></script>

   Далее нам необходимо кнопка, при нажатии на которую пользователь бы начинал процедуру аутентификации через Windows Live Connect. Тут у вас есть выбор, можно создать свой элемент разметки и привязать функции входа к какому-либо событию этого элемента, или попросить Windows Live SDK создать для нас красивую “фирменную” кнопку входа. Мы поступим вторым способом. Для этого в разметку страницы необходимо добавить следующий фрагмент кода:

<p>   
    <div id="signin"></div>
</p>

<script type="text/javascript">
    WL.init({
        client_id: "ваш CLIENT ID",
        redirect_uri: "@(Request.Url.AbsoluteUri)",
        scope: ["wl.signin"],
        response_type: "token"
    });
   
    WL.ui({
        name: "signin",
        element: "signin"
    });
</script>

   В результате на странице будет отображена кнопка следующего вида:

  При нажатии на кнопку будет открываться диалоговое окно, где пользователь сможет ввести свой логин и пароль в сиcтеме Windows Live. На кнопке изображена надпись “Sign In”, когда пользователь неавторизированный, и “Sign Out”, когда пользователь уже прошел аутентификацию.

  Все что нам осталось -  узнать когда пользователь успешно пройдет атентификацию и мы сможем работать с его данными из сервиса Windows Live. Для этого определим JavaScript функцию, которая будет выполнена после того как пользователь успешно авторизируется:

<script type="text/javascript">
    WL.Event.subscribe("auth.login", onLogin);

    function onLogin() {
        WL.api({ path: "me", method: "GET" }).then(
            function (response) {
                $('#userName').text("Hello " + response.name + "!");
            }
        );
    }
</script>

  Теперь мы можем осуществлять запросы к сервисам Windows LIve. Для примера давате загрузим имена и почтовые ящики контактов из почты HotMail. Для этого необходимо сначала модифицировать функцию инициализации среды Windows Live SDK:

<script type="text/javascript">
    WL.init({
        client_id: "00000000480C037B",
        redirect_uri: "@(Request.Url.AbsoluteUri)",
        scope: ["wl.signin", "wl.basic"],
        response_type: "token"
    });
</script>

  Если вы заметили, я добавил еще одну перменную в поле “scope”. В этом поле хранится список ресурсов к которым вы хотите получить доступ. При аутентификации сервис конекта Windows Live покажет пользователю какую информацию запрашивает ваш сайт и спросит его дает ли он свое согласие на использование его персональных данных.

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

<p>
    <ul id="contacts"></ul>
</p>
<script type="text/javascript">
    $('#loadLiveFriends').click(function() {
        WL.api({
            path: "me/contacts",
            method: "GET"
        }).then(
                function (response) {
                    $.each(response.data, function(key, value) {
                        $('#contacts').append('<li>Name - ' + value.name + ‘ Email - ’ + value.email +'</li>');
                    });
                }
            );
    });
</script>

  Данная функцию выведет имя контакта и его почтовый ящик в список на странице. Список – это элемент разметки “ul” с идентификатором “contacts”.

  В данной статье мы рассмотрели пример использования элементарных функций Windows Live SDK для веб платформы. Были описаны функции для аутентификации пользователей и загрузки списка контактов. Полный список предоставляемых функций намного больше, вы можете найти их описание и примеры использования на новом портале MSDN: http://msdn.microsoft.com/en-us/library/live/

21 апреля 2012 г.

Организация работы с базой данных посредством сервисов WCF

 

Часть 1. Создание тестовой базы данных.

   Целью данной статьи является создание сервиса при помощи технологии Windows Communication Foundation для демонстрации слоя доступа к базе данных в контексте сервис-ориентированной архитектуры.

  Первым шагом является разработка тестовой базы данных, к которой будут осуществляться запросы.

  Для этого выполним следующую последовательность действий:

  1) Открыть приложение Microsoft SQL Server Management Studio и подключится к серверу баз данных на локальном компьютере или на удаленном хосте;

  2) Создадим базу данных выполнив следующий скрипт:

CREATE DATABASE [ProductsDB] ON PRIMARY

( NAME = N'ProductsDB',

FILENAME = N'c:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\DATA\ProductsDB.mdf' ,

SIZE = 3072KB , FILEGROWTH = 1024KB )

LOG ON ( NAME = N'ProductsDB_log',

FILENAME = N'c:\Program Files\Microsoft SQL Server\MSSQL10_50.SQLEXPRESS\MSSQL\DATA\ProductsDB_log.ldf' ,

SIZE = 1024KB , FILEGROWTH = 10%) GO

   При необходимости можно изменить параметр FILENAME для файла базы данных и лог-файла.

   3) Создадим таблицу Products в ранее созданной базе данных ProductsDB, выполнив следующий скрипт:

USE [ProductsDB] GO

CREATE TABLE [dbo].[tb_Products]

( [Id] [int] IDENTITY(1,1) NOT NULL, [Name] [nvarchar](50) NOT NULL, [Description] [nvarchar](500) NOT NULL,

[Price] [int] NOT NULL, CONSTRAINT [PK_tb_Products] PRIMARY KEY CLUSTERED

( [Id] ASC ) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO

   4) Для тестирования различных способов взаимодействия с базой данных создадим тестовую хранимую процедуру, которая будет возвращать записи из таблицы tb_Products, у которых поле Name будет совпадать с параметром @Name, переданным в хранимую процедуру:

CREATE PROCEDURE sp_GetProductsByName

@Name nvarchar(50)

AS BEGIN

SELECT Id, Name, Description, Price FROM tb_Products WHERE Name LIKE '%' + @Name + '%';

END GO

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

 

Часть 2. Разработка WCF сервиса.

Следующий этап – создание кода ля выполнения запросов к базе данных средствами языка программирования C#.

Откроем среду разработки Microsoft Visual Studio 2010 и создадим новый проект, выбрав тип проекта – ASP.NET Empty Web Application. Проекты на основе языка программирования ASP.NET идеально подходят для быстрого создания и развертывания сервисов WCF. Назовем проект «WCF_DataAccess».

clip_image002

Теперь приступим к созданию WCF сервиса. Перейдем в Solution Explorer и поставив курсор на узел проекта нажмем правую кнопку мыши. Далее в контекстном меню необходимо выбрать пункт «Add – New Item». Далее на вкладке Web нужно выбрать тип файла – «WCF Service». Назовем файл «DbService.svc».

clip_image004

Среда выполнения добавит в наш проект два новых файла «DbService.svc» и «IDbService.cs»:

clip_image005

Также необходимо создать вспомогательный класс ProductBO, который будет выполнять сразу несколько функций – это будет оболочка для записи из таблицы tb_Products, а также контракт данных для использования его в WCF. Сигнатура класса приведена ниже:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.Serialization;
namespace WCF_DataAccess

{
   [DataContract]
   public class ProductBO
   {
       [DataMember]
       public int Id { get; set; }
       [DataMember]
       public string Name { get; set; }
       [DataMember]
       public string Description { get; set; }
       [DataMember]
       public int Price { get; set; }
}
}

Аттрибут [DataContract] означет что этот класс может использоваться для передачи данных между сервисом WCF и его клиентами. Атрибут [DataMember] указывает какие поля данных должны быть включены в сообщение при передаче по каналам связи WCF.

Теперь мы можем перейти к написанию сервиса. Сервис в WCF сосотит из двух частей:

1) Контракт сервиса – это файл «IDbService.cs». В этом файле находится контракт сервиса – интерфейс на языке C#, который помечен атрибутом [ServiceContract]. Интерфейс содержит набор определений методов, которые помечены атрибутом [OperationContract]. Данный интерфейс называется контрактом сервиса, птомучто он декларирует точное описание методов, которые будет предоставлять своим клиентам WCF сервис. Код интерфейса приведен ниже:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WCF_DataAccess
{
     [ServiceContract]
      public interface IDbService
      {
          [OperationContract]
          List<ProductBO> GetAllProducts();
          [OperationContract]
          void InsertProduct(ProductBO product);
          [OperationContract]
          void DeleteProductById(int productId);
         [OperationContract]
         List<ProductBO> GetProductsByName(string name);
     }
}

2) Реализация сервиса – файл «DbService.svc». Здесь находится класс DbService, который наследует и реализует контракт сервиса – интерфейс IDbService. Поскольку мы будем работать с базой данных через наш сервис, класс DbService будет также реализовывать интерфейс IDisposable.

public class DbService : IDbService, IDisposable

Класс будет содержать одно поле и конструктор:

private SqlConnection _connection;
public DbService()
{
    string connectionString = ConfigurationManager.ConnectionStrings["ProductsDBConnectionString"].ConnectionString;
    _connection = new SqlConnection(connectionString);
    _connection.Open();
}

В конструкторе создается соединение с базой данных. Строка соединения берется из файла конфигурации Web.config, где она имеет следующий вид:

<configuration>
     <connectionStrings>
            <add name="ProductsDBConnectionString" connectionString="Data Source=.\SQLEXPRESS;Initial Catalog=ProductsDB;Integrated Security=True" />
      </connectionStrings>

Приступим к реализации методов сервиса:

Выборка всех продуктов из таблицы:

public List<ProductBO> GetAllProducts()
{
       string query = "SELECT Id, Name, Description, Price FROM tb_Products";
       SqlCommand command = new SqlCommand(query, _connection);
       SqlDataReader dataReader = command.ExecuteReader();
       List<ProductBO> products = new List<ProductBO>();
       while (dataReader.Read())
      {
            products.Add(new ProductBO()
           {
               Id = dataReader.GetInt32(0),
               Name = dataReader.GetString(1),
               Description = dataReader.GetString(2),
               Price = dataReader.GetInt32(3)
          });
     }
     return products;
}

Добавление записи в таблицу продуктов:

public void InsertProduct(ProductBO product)
{
       string query = "INSERT INTO tb_Products VALUES (@name, @desc, @price);";
       SqlCommand command = new SqlCommand(query, _connection);
       command.Parameters.Add(new SqlParameter()
       {
            DbType = DbType.String,
            Value = product.Name,
            ParameterName = "name"
      });
      command.Parameters.Add(new SqlParameter()
      {
           DbType = DbType.String,
           Value = product.Description,
           ParameterName = "desc"
     });
     command.Parameters.Add(new SqlParameter()
     {
          DbType = DbType.Int32,
          Value = product.Price,
          ParameterName = "price"
     });
     command.ExecuteNonQuery();
}

Удаление продукта из таблицы по его Id:

public void DeleteProductById(int productId)
{
        string query = "DELETE FROM tb_Products WHERE Id = @id";
        SqlCommand command = new SqlCommand(query, _connection);
        command.Parameters.Add(new SqlParameter()
       {
            DbType = DbType.Int32,
            Value = productId,
            ParameterName = "id"
       });
       command.ExecuteNonQuery();
}

Выборка продуктов с фильтрацией продуктов по полю Name, при помощи хранимой процедуры:

public List<ProductBO> GetProductsByName(string name)
{
         SqlCommand command = new SqlCommand();
         command.Connection = _connection;
         command.CommandType = CommandType.StoredProcedure;
        command.CommandText = "sp_GetProductsByName";
        command.Parameters.Add(new SqlParameter()
        {
             DbType = DbType.String,
             Value = name,
             ParameterName = "Name"
        });
        SqlDataReader dataReader = command.ExecuteReader();
        List<ProductBO> products = new List<ProductBO>();
        while (dataReader.Read())
        {
              products.Add(new ProductBO()
              {
                    Id = dataReader.GetInt32(0),
                    Name = dataReader.GetString(1),
                    Description = dataReader.GetString(2),
                    Price = dataReader.GetInt32(3)
              });
       }
       return products;
}

И последний метод – член интерфейса IDisposable – Dispose:

public void Dispose()
{
       _connection.Close();
}

Данный метод будет вызываться автоматически после завершения работы сервиса.

На этом написание кода сервиса завершено. Попробуем его запустить, для этого в Solution Explorer необходимо установить курсор на файл DbService.svc и в контекстном меню выбрать пункт “View In Browser”. После нажатия откроется окно браузера. Адрес страницы, которая откроется в браузере, и будет адресом нашего сервиса(он нам понадобится позже, для тестирования сервиса). В окне браузера должно быть приблизительно следующее содержимое:

clip_image007

Это значит, что наш сервис правильно настроен и работает. На этом разработка сервиса завершена. Давайте проверим его в действии.

 

Часть 3. Разработка клиента для тестирования сервиса.

Для тестирования сервиса создадим простое консольное приложение:

clip_image001

Для работы с сервисом нам понадобится ссылка на этот сервис. Чтобы ее добавить необходимо в Solution Explorer установить курсор на узел проекта и выбрать пункт «Add Service Reference». На экране появится следущее окно:

clip_image003

В поле Address необхимо ввести адрес из браузера и нажать кнопку Go. Затем в поле Namespace указать значение «Service Refference» - это пространство имен в котором будут расположены классы для работы с сервисом. В конце жмем кнопку «Ok».

Модифицируем файл Program.cs внеся туда код для использования нашего сервиса:

class Program
{
    static void Main(string[] args)
    {
          var service = new DbServiceClient();
          Console.WriteLine("Add product: ");
          ProductBO newProduct = new ProductBO() { Name = "Book", Description = "Classic", Price = 5 };
          service.InsertProduct(newProduct);
          Console.WriteLine("Success!\n");
          Console.WriteLine("Add product: ");
          newProduct = new ProductBO() { Name = "Pencil", Description = "Red", Price = 1 };
          service.InsertProduct(newProduct);
          Console.WriteLine("Success!\n");
          Console.WriteLine("Products list:");
          List<ProductBO> products = service.GetAllProducts().ToList();
          string productFormat = "Id - {0}, Name - {1}, Desc - {2}, Price - {3}";
          products.ForEach(product => Console.WriteLine(String.Format(productFormat, product.Id, product.Name,      product.Description, product.Price)));
          Console.WriteLine("Search by Name 'Book':");
          products = service.GetAllProducts().ToList();
          products.ForEach(product => Console.WriteLine(String.Format(productFormat, product.Id, product.Name,  product.Description, product.Price)));
          Console.WriteLine("Delete product with Id = 1: ");
          service.DeleteProductById(1);
          Console.WriteLine("Success!");
   }
}

Теперь нажав комбинацию клавиш Ctrl + F5 можно запустить тестовую программу.

На этом написание всех модулей системы завершено. Спасибо за внимание.

10 апреля 2012 г.

Оптимизация баз Microsoft SQL Server: Часть 2. Индексы на практике?

 

   В предыдущей статье мы познакомились со способами оптимизации реляционных баз данных и обсудили принцип действия кластеризованных и некластеризованных индексов в контексте оптимизации времени выполнения запросов к базе данных. Теперь пришло время применить полученные знания на практике, научившись создавать оптимизационные индексы для базы данных MS SQL.  

   Напомню вам определение схемы таблицы Staffs, с которой мы будем работать:

 

Таблица Staffs

Id

Int

Name

Nvarchar(50)

Surname

Nvarchar(50)

Salary

Int

Address

Nvarchar(50)

Telephone

Nvarchar(50)

Job

Nvarchar(50)

Photo

image

  Допустим нам необходимо для таблицы Staffs создать некластеризованный индекс, который будет оптимизировать следующий запрос:

SELECT Id, Name, Job FROM Stuffs WHERE SALARY > 1000 AND Photo IS NOT NULL

   Ключом индекса будут столбцы SALARY и Photo, так как по этим полям производится фильтрация выборки. А столбы Id, Name и Job будут включенными в индекс столбцами.

   Общий синтаксис команды следующий:

USE <database_name> GO

CREATE NONCLUSTERED INDEX <index_name> ON <table_name> ( <column_name> ASC -- столбцы ключа индекса )

INCLUDE ( <column_name> -- включенные столбцы ) GO

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

CREATE NONCLUSTERED INDEX IDX_StaffsSearch ON Stuffs

( Salary, Photo ) INCLUDE ( Id, Name, Job ) GO

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

   Если бы наш код был следующим:

CREATE NONCLUSTERED INDEX IDX_StaffsSearch ON Stuffs

( Salary, Photo ) INCLUDE ( Id ) GO

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

   Кластеризованный индекс создается при помощи следующей команды:

CREATE CLUSTERED INDEX IDX_Stsffsid ON Stuffs (Id)

   Здесь был создан уникальный кластерный индекс, построенный на основе первичного ключа таблицы (столбца Id).

 

Реальный пример

   Давайте теперь разработаем сценарий, на котором сможем реально оценить степень прироста производительности в случае использования индексов.

   Создадим новую базу данных:

CREATE DATABASE TestDB;

   И единственную таблицу Customers, которая будет состоять из четырех столбцов:

USE [TestDB] GO

CREATE TABLE [dbo].[Customers](

[Id] [int] NOT NULL, [Num1] [int] NULL, [Num2] [int] NULL, [Num3] [int] NULL) GO

   Теперь заполним нашу таблицу случайными данными. Столбец Id будет наращиваться в цикле, а остальные три колонки таблицы будут заполняться случайными числами при помощи своеобразного варианта функции random:

USE TestDB GO

DECLARE @i int = 0;

WHILE (@i < 500000) BEGIN INSERT INTO Customers(Id, Num1, Num2, Num3) VALUES(

@i, abs(checksum(newid())), abs(checksum(newid())), abs(checksum(newid())) ) SET @i = @i + 1; END

   Данный скрипт вносит в таблицу полмиллиона записей, поэтому будьте терпеливы, скрипт будет работать не менее 3 минут.

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

  Чтобы включить режим сбора статистики необходимо выполнить следующую команду:

SET STATISTICS IO ON

   Теперь после выполнения каждого запроса на вкладке Messages нам будет доступна статистика выполнения данного запроса, как показано ниже:

clip_image002

   Нас интересует только значение параметра logical reads.

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

  1) SELECT Id, Num1, Num2 FROM Customers WHERE Id = 2000

  2) SELECT Id, Num1, Num2 FROM Customers WHERE Id >= 0 AND Id < 1000

  3) SELECT Id, Num1, Num2 FROM Customers WHERE Id >= 0 AND Id < 5000

  Данные запросы вернут соответственно 1 строку, 1000 строк и 5000 строк. Без индексов показатель производительности(количество логических считываний) для всех запросов одинаковый и равен 1621. Внесем данные в таблицу результатов:

 

Запрос 1

Запрос 2

Запрос 3

Без индексов

1621

1621

1621

  Теперь создадим для таблицы Customers непокрывающий индекс:

USE TestDB GO

CREATE NONCLUSTERED INDEX TestIndex1 ON dbo.Customers(Id);

  Теперь выполним приведенные выше запросы еще раз и посмотрим на счетчик производительности:

 

Запрос 1

Запрос 2

Запрос 3

Без индексов

1621

1621

1621

Непокрывающий индекс

4

1632

1632

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

   Теперь создадим покрывающий индекс, тем самым достигнув максимальной производительности.

   Для начала удалим предыдущий индекс:

USE TestDB GO DROP INDEX Customers.TestIndex1

   И создадим новый индекс:

USE TestDB GO

CREATE NONCLUSTERED INDEX TestIndex2 ON dbo.Customers(Id) INCLUDE (Num1, Num2);

   Теперь выполним наши запросы в третий раз и запишем результаты в таблицу:

 

Запрос 1

Запрос 2

Запрос 3

Без индексов

1621

1621

1621

Непокрывающий индекс

4

1632

1632

Покрывающий индекс

3

7

20

   Нетрудно заметить, что прирост производительности получился колоссальный. Таким образом, мы увеличили скорость выполнения запросов в десятки раз. При эксплуатации базы данных, в которой хранятся миллионы строк, такой прирост производительности будет весьма ощутим.

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

4 апреля 2012 г.

Оптимизация баз Microsoft SQL Server: Часть 1. Зачем нужны индексы?

 

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

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

   1) Создание репликаций базы данных

   2) Шардинг базы данных

   3) Индексирование таблиц базы данных с целью оптимизации запросов

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

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

   Индексы создаются для таблицы и, по сути, являются копией этой таблицы. Различие состоит в том, что индекс может включать не все столбцы исходной таблицы, а лишь те которые указал разработчик. Рассмотрим пример. У нас есть таблица Stuffs, которая содержит 10 колонок и упорядоченный индекс IDX_StuffsSalary с ключом Salary, который содержит только два столбца из таблицы Stuffs:

Таблица Staffs

Id

Int

Name

Nvarchar(50)

Surname

Nvarchar(50)

Salary

Int

Address

Nvarchar(50)

Telephone

Nvarchar(50)

Job

Nvarchar(50)

Photo

image

Индекс IDX_StaffsSalary

Salary

Int

Name

Nvarchar(50)

   Как это работает и в чем тут оптимизация? Допустим, есть следующий запрос:

SELECT Name FROM Stuffs WHERE Salary > 400 AND Salary < 1000

   Если бы не было индекса, данный запрос работал бы только с основной таблицей, и происходило бы следующее: для проверки значения колонки Salary из базы будет извлекаться вся строковая запись, то есть значения всех колонок таблицы. В случае наличия индекса, механизм SQL Server вообще не будет обращаться к таблице Staffs, работая полностью с данными в индексе, так как индекс содержит все, необходимые для выполнения запроса, данные. Таким образом, вместо извлечения на каждой итерации десяти колонок – извлекается только две. Поскольку индекс уже отсортирован по его ключу – полю Salary, запрос выполнится еще быстрее, за счет прохождения меньшего количества строк. Когда в таблице миллионы записей, прирост производительности колоссальный.

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

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

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

  Столбы в индексе могут быть двух типов:

  1) Столбцы в ключе индекса

  2) Столбы, включенные в индекс

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

  В зависимости от общего числа столбцов в индексе, индексы делятся на кластеризованные и некластеризованные индексы.

  Кластеризованный индекс – это индекс, в который входят все столбцы исходной таблицы (в качестве ключа или включенного столбца). По сути, кластеризованный индекс является копией исходной таблицы. Оптимизация при создании такого индекса достигается за счет упорядочивания данных в индексе по ключу. Кластеризованные индексы рекомендуется создавать для первичного ключа и внешних ключей базовой таблицы.

  Некластеризованный индекс – это индекс, в который включена только часть столбцов базовой таблицы. Пример такого индекса приведен выше.

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

29 марта 2012 г.

Полнотекстовый поиск в MS SQL 2008: Часть 2. Написание полнотекстовых запросов

 

   В предыдущей статье мы создали и настроили механизм полнотекстового поиска в базе данных AdventureWorks. Теперь перейдем к написанию полнотекстовых поисковых запросов, на основе созданной нами инфраструктуры.

   Осуществлять полнотекстовые запросы можно двумя базовыми способами. С помощью предикатов CONTAINS и FREETEXT или функций CONTAINSTABLE и FREETEXTTABLE.

 

CONTAINS и CONTAINSTABLE

   Поиск при помощи предиката CONTAINS осуществляется следующим образом:

SELECT * FROM dbo.ProductSearchView WHERE CONTAINS(Description, 'bike');

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

   Вторым параметром идет непосредственно поисковый запрос. В примере выше – это поиск точного вхождения слова «bike». Однако, поисковый механизм очень гибкий и кроме поиска точного вхождения нам доступен ряд других типов поиска:

   1) Поиск по префиксному выражению. Такой запрос имеет следующий вид:

SELECT * FROM dbo.ProductSearchView WHERE CONTAINS(Description, '"bik*"');

   После слова запроса ставится звездочка, и все выражение обязательно берется в двойные кавычки. В результате данного запроса будут выведены все результаты, в которых есть слово, которое начинается с выражения «bik».

   2) Поиск синтаксических форм слов. Это один из самых мощных режимов поиска. Для работы в этом режиме используется ключевое слово FORMSOF и два параметра поиска: INFLECTIONAL и THESAURUS.

SELECT * FROM dbo.ProductSearchView WHERE CONTAINS(Description, 'FORMSOF(INFLECTIONAL, bike)');

   В данном запросе будут выбраны все строки, которые содержат слово «bike» или какую-либо форму этого слова, например «biking». Параметр INFLECTIONAL задает поиск по синтаксическим формам слова.

   Если вместо INFLECTIONAL подставить параметр THESAURUS, то будут выбраны строки, которые содержат данное слово, либо его синоним из словаря для конкретного языка.

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

SELECT * FROM dbo.ProductSearchView WHERE CONTAINS(Description, 'bike NEAR quick')

   В данном запросе будут выбраны строки, в которых слово «bike» находится в тексте недалеко, по отношению к слову «quick». Запрос может содержать любую цепочку слов, соединенных оператором NEAR.

   4) Поиск с учетом веса слов. В данном режиме каждому слову в поисковом запросе можно присвоить коэффициент веса (от 0 до 1). Механизм полнотекстового поиска будет строить результирующую выборку данных на основе вхождения слов с наибольшими коэффициентами значимости.

   Запрос будет выглядеть следующим образом:

SELECT * FROM dbo.ProductSearchView WHERE CONTAINS(Description, 'ISABOUT(bike weight(0.8), quick weight(0.5))');

   После ключевого слова ISABOUT в скобках через запятую указывается список слов запроса. После каждого слова идет ключевое слово weight и коэффициент веса.

   5) Комбинированный режим поиска. В этом режиме разработчик может комбинировать при помощи логических операторов AND, AND NOT, или OR различные механизмы поиска, перечисленные выше. Такой режим позволяет максимально гибко создавать высокоэффективные поисковые механизмы, ориентируясь на задачи конкретной системы или приложения.

   Этими пять режимов составляют ядро полнотекстового поиска с использованием предиката CONTAINS.

   Функция CONTAINSTABLE работает в тех же режимах, что и предикат CONTAINS. Различие заключается в форме ее вызова и более расширенном представлении результатов. Запрос, с использованием данной функции выглядит следующим образом:

SELECT * FROM CONTAINSTABLE(dbo.ProductSearchView,

Description, 'ISABOUT(bike weight(0.8), quick weight(0.5))', 10) as result

   Первым параметром функции идет имя таблицы или представления, по которому производится поиск.

   Второй параметр – перечень столбцов представления или таблицы, в которых необходимо проводить поиск текста.

   Третий параметр – поисковый запрос (формируется таким же образом, как и в предикате CONTAINS).

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

   Данный запрос вернет следующий результат:

KEY

RANK

1201

87

690

75

554

60

209

55

249

55

746

37

64

29

88

29

168

29

320

29

   Столбец KEY – это первичный ключ записи в представлении ProductSearchView, а RANK – это коэффициент релевантности данных. Для каждого результата механизм SQL Server устанавливает коэффициент, который определяет, насколько данный результат соответствует поисковому запросу. Отсортировав результаты по полю RANK, разработчик сможет вернуть в приложение наиболее точные результаты поиска. При задании в функции CONTAINSTABLE четвертого параметра, результаты сортируются по полю RANK автоматически.

 

FREETEXT и FREETEXTABLE

   Данный тип поиска является наиболее интеллектуальным во всем механизме полнотекстового поиска SQL Server. Поиск при помощи CONTAINS основан на дословном поиске результатов. Предикат FREETEXT не просто ищет конкретные слова и формы в тексте, а проводит интеллектуальный анализ поискового запроса и ищет подходящие строки по смыслу запроса.

   Приведем пример такого запроса:

SELECT * FROM dbo.ProductSearchView WHERE FREETEXT(Description, 'provides a light stiff ride')

   Данный запрос вернет 34 результата. В случае, если переписать данный запрос с предикатом CONTAINS, то получим лишь один результат.

   Функция FREETEXTTABLE (аналогично CONTAINSTABLE), отличается от предиката FREETEXT формой вызова и наличие ранга релевантности результатов:

SELECT * FROM FREETEXTTABLE(dbo.ProductSearchView, Description, 'provides a light stiff ride', 40) as result

 

   Выводы

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

21 марта 2012 г.

Полнотекстовый поиск в MS SQL 2008: Часть 1. Подготовка базы

 

    Одна из наиболее мощных функций, которые предоставляются Microsoft SQL Server 2008 – это механизм полнотекстового поиска. В большинстве случаев, приложениям, которые используют базы данных, требуется механизм поиска текста внутри базы (например, поиск на веб-сайтах). Основные требования, которые предъявляются к такому механизму – это скорость, гибкость и простота использования. И, конечно, банальный поиск в запросе со словом LIKE следующего вида:

SELECT * FROM Products WHERE Description LIKE ‘…..’;

никак не может удовлетворить такие требования.

   К счастью Microsoft SQL Server предоставляет высокоуровневый, интеллектуальный механизм полнотекстового поиска, который позволяет разработчикам организовать высокоэффективный поиск по базе данных, затрачивая при этом минимальное количество ресурсов. Данная опция является достаточно распространенной единицей SQL Server, поэтому доступна во всех версиях Microsoft SQL Server кроме Express. Я буду использовать SQL Server 2008 Developer.

   Стоит отметить, что механизм полнотекстового поиска в SQL Server 2008 значительно эволюционировал, по сравнению с предыдущей версией SQL Server 2005. Были значительно улучшены поисковые алгоритмы, а также была добавлена поддержка 28 новых языков. И теперь в SQL Server 2008 разработчики могут писать полнотекстовые запросы поиска на 33 различных языках.

   В этой статье мы познакомимся с функциями, которые предоставляет полнотекстовый поиск в SQL Server 2008, научимся создавать полнотекстовые индексы в своих базах данных.

   Для примера мы будем использовать базу данных AdventureWorks. Поиск будет осуществляться по таблице ProductDescription. Для настройки механизмов полнотекстового поиска необходимо выполнить следующую последовательность операций:

   1) Открыть программу SQL Server Management Studio и подключится к удаленному серверу;

   2) Для оптимизации производительности рекомендуется создавать механизм полнотекстового поиска не на основной таблице, а на представлении этой таблицы. Поэтому мы создадим представление dbo. ProductSearchView при помощи следующего скрипта:

USE [AdventureWorks] GO

CREATE VIEW [dbo].[ProductSearchView] WITH SCHEMABINDING

AS SELECT ProductDescriptionID, Description FROM Production.ProductDescription

GO

   Мы создали представление ProductSearchView на основе таблицы ProductDescription, которое включает в себя два столбца:

  • ProductDescriptionID – уникальный идентификатор продукта;
  • Description – текстовое поле с описанием продукта, по содержимому которого будет производится полнотекстовый поиск.

   Заметьте, что обязательным является параметр WITH SCHEMABINDING, если его не указать при создании представления, то в дальнейшем SQL Server не разрешит нам создавать индексы внутри этого представления.

   3) Следующий этап – создание уникального кластерного индекса в ранее созданном представлении. Данный индекс будет служить основой для полнотекстового поиска. Для этого необходимо выполнить следующий скрипт:

USE [AdventureWorks] GO

CREATE UNIQUE CLUSTERED INDEX [IDX_SEARCH] ON [dbo].[ProductSearchView]

( [ProductDescriptionID] ASC ) ON [PRIMARY] GO

   Мы создали уникальный кластерный индекс с именем IDX_SEARCH внутри представления ProductSearchView. Данный индекс строится на основе колонки ProductDescriptionID, которая может уникально идентифицировать все строки представления.

   4) Следующая составляющая полнотекстового поиска – полнотекстовый каталог. Данный каталог создается на уровне базы данных и представляет группу полнотекстовых индексов. Создадим такой каталог следующим скриптом:

USE AdventureWorks GO

CREATE FULLTEXT CATALOG ftCatalog AS Default

   Наш каталог будет называться – ftCatalog.

   5) И, наконец, последний этап – непосредственное создание полнотекстового индекса. Для начала приведем общий формат команды:

CREATE FULLTEXT INDEX ON имя_таблицы_или_представления

   [ ( { имя_колонки [ TYPE COLUMN название_типа ]  [ LANGUAGE код_языка ] } [ ,...n]  ) ]

    KEY INDEX имя_индекса  [ ON имя_полнотекстового_каталога ]

    [ WITH [ ( ] перечень_опций [ ,...n] [ ) ] ] [;]

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

USE AdventureWorks GO

CREATE FULLTEXT INDEX ON dbo.ProductSearchView (Description LANGUAGE 1033)

KEY INDEX IDX_SEARCH ON ftCatalog

WITH (CHANGE_TRACKING = AUTO, STOPLIST = SYSTEM) GO

      Разберем параметры команды более детально:

  • ON dbo.ProductSearchView – здесь указывается для какого объекта создается полнотекстовый индекс(в данном случае это наше представление);
  • (Description LANGUAGE 1033) – в скобках идет перечисление всех полей, по которым будет производится поиск. Для каждого поля указывается код языка, в зависимости от его содержимого. Для каждого поля может быть указан только один язык поиска. Код 1033 – это английский язык. Чтобы узнать код какого-либо языка необходимо запросить системное представление sys.syslanguages;
  • KEY INDEX IDX_SEARCH – здесь указывается имя уникального кластерного индекса, который мы создали в пункте 3;
  • ON ftCatalog – здесь необходимо указать имя полнотекстового каталога, который мы создали в пункте 4;
  • WITH (CHANGE_TRACKING = AUTO, STOPLIST = SYSTEM) – последним параметром идут опции полнотекстового индекса.
  • CHANGE_TRACKING = AUTO – это значит, что все изменения в основной таблице будут автоматически переноситься в полнотекстовый индекс.
  • STOPLIST = SYSTEM – обозначает, что при поиске будет использоваться системный список стоп-слов. Стоп-слова – это «шумовые» слова, которые не являются ключевыми в поисковом запросе и отрицательно влияют на качество результатов поиска. Например, при поиске по базе объявлений, в поисковом запросе «куплю iPhone» слово «куплю» будет являться шумовым словом. При таком запросе пользователь хочет, чтобы ему вернулись объявления со словами «куплю iPhone» или «iPhone», но он явно не хочет получить все объявления, где встречается слово «куплю», так как это слово встречается почти в каждом объявлении базы. В большинстве случаев стандартного списка стоп-слов бывает достаточно для обеспечения качественного поиска, но в случае необходимости разработчик может создать свой собственный список шумовых слов.

   Теперь все механизмы базы данных созданы и готовы к работе. В следующей статье мы перейдем к написанию полнотекстовых поисковых запросов, на основе созданной нами инфраструктуры.