четверг, 24 июля 2014 г.

Функции ранжирования в MS SQL

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

Итак, собственно, функции ранжирования. Представим, что у нас есть вот такая табличка:


И мы хотим получить порядок строк отсортированных по типу оборудования. Для этого можно использовать функция ROW_NUMBER().

SELECT ROW_NUMBER() OVER (ORDER BY PositionType DESC) as RowNumber
      ,[maker]
      ,[positionName]
      ,[positionType]
      ,[price]
  FROM [test].[dbo].[rowNumbering]


В результате выполнения этого запроса мы получим вот такой результат:

Как вы заметили, для указания на базе какого столбца будет сделана нумерация в запросе используется конструкция ORDER BY. На больших наборах данных из-за этого может пострадать производительность, но, если порядок следования рядов в результате выдачи вам не важен, то сортировки можно избежать, используя примерно такую конструкцию:

SELECT ROW_NUMBER() OVER (ORDER BY (SELECT TOP 1 1 FROM [test].[dbo].[rowNumbering])) as RowNumber
      ,[maker]
      ,[positionName]
      ,[positionType]
      ,[price]
  FROM [test].[dbo].[rowNumbering]




среда, 23 июля 2014 г.

"Задачки на сообразительность" на интервью

Наткнулся на интересную запись в блоге Эрика Липперта, не могу не поделиться, это волшебно :) Да, перевод взят из официальной русской версии блога Эрика.

----

Никто из моих знакомых в компании Microsoft больше не задает эти ужасные задачки «на нестандартную логику». Хотя, может быть, кто-то еще их задает, я не знаю. Но ходят слухи, что многие другие компании все еще следуют тому, как компания Microsoft проводила собеседования в 90-х. Продолжая эту старую традицию, я хочу представить продолжение истории Кейта Митчела о нестандартном мышлении. Итак, мы еще раз постараемся ответить на вопрос: «насколько успешно лауреат Нобелевской премии по физике, Ричард Филипс Фейнман прошел бы техническое собеседование в сотфверную компанию?»

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

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

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

Интервьюер: Хм… Да, пожалуй, этот способ сработает. Но что, если у вас нет больших зеркал, или вы не можете установить их правильным образом?

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


понедельник, 21 июля 2014 г.

Использование типа DateTime в CAML запросах.

Предположим, что вы хотите создать CAML запрос в котором проверяете значение поле типа DateTime. Естественно, первое, что приходит на ум написать что-то типа этого:

Query = @"<Where>
          <Eq>
             <FieldRef name='SomeDateField'>
                <Value type='DateTime'>" + DateTime.Now.ToString() + @"</value>
             </FieldRef>
          </Eq>
          </Where>"

Что, естественно, работать не будет по причине того, что даты хранятся в Sharepoint в формате ISO8601, то есть вот в таком виде: 2014-01-01T10:11:12Z
Получить дату в таком формате можно как руками используя, например, формат в методе .ToString(), а можно поступить проще, используя вот такой метод из SPUtilities:

Query = @"<Where>
          <Eq>
             <FieldRef name='SomeDateField'>
                <Value type='DateTime'>" + SPUtility.CreateISO8601DateTimeFromSystemDateTime(DateTime.Now) + @"</value>
             </FieldRef>
          </Eq>
          </Where>"


В таком случае даты будут корректно обрабатываться. Но, только даты. Временная составляющая в таком случае будет игнорироваться. Чтобы это исправить нужно немного поправить тег Value


Запуск рабочего процесса из кода

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

во-первых, подключить пространство имен Microsoft.SharePoint.Workflow,
а во-вторых использовать метод SPWorkflowManager.StartWorkflow. Существует несколько перегрузок этого метода, удобных в разных специфических ситуациях (например, если вам нужно только создать объект рабочего процесса, а запустить его на выполнение позднее), самый же простой и функциональный вариант - вот этот:

public SPWorkflow StartWorkflow(
 SPListItem item,
 SPWorkflowAssociation association,
 string eventData,
 bool isAutoStart
)

Вместо объяснения что значат все эти параметры и как из использовать  (см. MSDN ;)) я предлагаю вам взглянуть на вот такую небольшую функцию, которую я написал для запуска рабочих процессов. В комментариях небольшие пояснения, а так, в общем-то, функция вполне self-explaining.


        public SPWorkflow RunWorkflow(string workflowName, string listName, int itemId, string activationData = "<root/>")
        {
            using (SPSite site = new SPSite(siteUrl))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    //получаем элемент списка для которого нужно выполнить рабочий процесс
                    SPList itemsList = web.Lists.TryGetList(listName);
                    SPListItem item = itemsList.GetItemById(itemId);
                    //создаем объект ассоциации шаблона рабочего процесса
                    SPWorkflowAssociation workflowAssociation = web.WorkflowAssociations.GetAssociationByName(workflowName, System.Globalization.CultureInfo.InvariantCulture);
                    if (workflowAssociation != null)
                    {
                        //ну и, собственно, запускаем рабочий процесс. StartWorkflow возвращает ссылку на экземпляр рабочего процесса
                        //через которую можно с ним в дальнейшем взаимодействовать, например, проверять состояние.
                        return site.WorkflowManager.StartWorkflow(item, workflowAssociation, activationData, true);
                    }
                    else return null;
                }
            }
        }


Нюансы Mail.SmtpClient

Недавно столкнулся с одной интересной ошибкой, которая может возникнуть при использовании класса System.Mail.SntpMail.

Имеем вот такой код:

MailMessage mail = new MailMessage();

//........

SmtpClient client = new SmtpClient("mail.yourhost.com", 25);
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.Credentials = new System.Net.NetworkCredential("username", "password");
client.UseDefaultCredentials = false;

client.Send(mail);


В результате выполнения этого кода вы, скорее всего получите ошибку:
Почтовый ящик недоступен. Отклик сервера: 5.7.1 Unable to relay

а все потому, что при установке client.UseDefaultCredentials = false, client.Credentials становится равен null. Что самое интересное, о такой особенности этого свойства на MSDN нет ни слова.

Ну да ладно, дальше - интереснее, если закомментировать строку с UseDefaultCredentials, то, если ваш сервер настроен на проверку подлинности клиента перед отправкой почты (стандартное поведение для Microsoft Exchange), то вы получите другую ошибку, которая, кстати, не чуть не более очевидна:
Почтовый ящик недоступен. Отклик сервера: 5.7.1 Client does not have permissions to send as this sender

И итоге, чтобы все работало как нужно нужно просто поменять местами строки с UseDefaultCredentials и с client.Credentials. И все. Вот правильная версия кода:
MailMessage mail = new MailMessage();

//........

SmtpClient client = new SmtpClient("mail.yourhost.com", 25);
client.DeliveryMethod = SmtpDeliveryMethod.Network;
client.UseDefaultCredentials = false;
client.Credentials = new System.Net.NetworkCredential("username", "password");

client.Send(mail);