Будем разбираться дальше.
понедельник, 13 августа 2012 г.
Linux клиент для Blogspot (blogilo)
Будем разбираться дальше.
среда, 27 июня 2012 г.
MSSQL : Преобразование varbinary в hex строку и обратно
Нужно было передать в 1С двоичные данные (varbinary), но как говорится все проблемы на стыке систем. 1С восприняла данные как объект, каковыми они не являлись. Памятуя о том что 1С любит строковые данные, я начал пытаться преобразовать их в строку. И вот что у меня получилось на выходе:
- Использование не документированных функций [sys].[fn_varbintohexstr] и [sys].[fn_varbintohexsubstring]
Если стоит простое преобразование, то можно воспользоваться [sys].[fn_varbintohexstr] , она принимает только один параметр: бинарную переменную.
Вот ее полное описание.
Как вы видете функция [sys].[fn_varbintohexstr] является оберткой для [sys].[fn_varbintohexsubstring], и вызывает ее всегда с одними и темиже параметрами.
- CREATE FUNCTION [sys].[fn_varbintohexstr]
- (
- @pbinin varbinary(max)
- )
- returns nvarchar(max)
- as
- begin
- return sys.fn_varbintohexsubstring(1,@pbinin,1,0)
- end
* This source code was highlighted with Source Code Highlighter.
[sys].[fn_varbintohexsubstring], в свою очередь имеет более продвинутый функционал. Например, позволяет преобразовывать в строку не всю последовательность, а некую "подстроку". К таким выводам я пришел после просмотра заголовочной части:
Остается только проблема обратного преобразования, дело в том что в SQL нет встроенной функции для такого действия. Точнее не было, в MS SQL 2008 есть, но об этом чуть позже.
- CREATE FUNCTION [sys].[fn_varbintohexsubstring] (
- @fsetprefix bit = 1 -- добавлять '0x' в вывод
- ,@pbinin varbinary(max) -- входной binary поток
- ,@startoffset int = 1 -- начальное смещение
- ,@cbytesin int = 0 -- Длина входной части, 0 означает до конца строки
- )
* This source code was highlighted with Source Code Highlighter.
Со своими коллегами я договорился что им будут отдавать строку вида 0x0123456789, соответственно и мне они будут отдавать уже не в виде строки, а в виде binary, например так:
В общем на этом и можно было бы и закончить, если бы не было интересно. Я прекрасно понимал что какой-то выход должен быть, и он был найден.
- INSERT INTO [tab] ([ColBin])
- VALUES (0x0123456789)
* This source code was highlighted with Source Code Highlighter.
- Использование XML запроса.
Вот сразу табличный пример использования:
В CTE выражении я просто готовлю даные, для дальнейшего использования. Самое интересно начинается в SELECT:
- ;WITH [cte_d] AS (
- SELECT [ColBin], [sys].[fn_varbintohexstr]([ColBin]) [ColChar] FROM [dbo].[tab]
- )
- select
- cast('' as xml).value('xs:hexBinary( substring(sql:column("ColChar"), 3) )', 'varbinary(max)') [ColBin_xml]
- , '0x' + cast('' as xml).value('xs:hexBinary(sql:column("ColBin") )', 'varchar(max)') [ColChar_xml]
- , [ColBin]
- , [ColChar]
- FROM [cte_d]
* This source code was highlighted with Source Code Highlighter.
- В поле [ColBin_xml] мы плучаем солонку [ColChar] из [cte_d] - команда sql:column("ColChar")
- Дальше берем подстроку от этой колонки начиная с 3-го знака (отрезаем "0x") - функция substring(,,)
- Заставляем xml процессор считать полученую строку как hexBinary
- А в момент возврата данных они интерпретируются как varbinary(max)
- Берем колонку [ColBin] sql:column("ColBin")
- Заставляем процессор считать ее как hexBinary
- И возвращаем как varchar(max)
- И по желанию можно добавить в начало строки "0x"
- Использование MS SQL 2008
Ура, Microsoft реализовал преобразование в рамках функции convert :)
Если у Вас MS SQL 2008 и выше, то вы можете воспользоваться функцией convert. В этом случае она будет принимать 2 или 3 аргумента.
CONVERT ( тип_данных [ ( длина ) ] , выражение [ , стиль ] )
При конвертировании из бинарного представления в строковый мы можем воспользоваться одним из 3-х стилей
- 0 - преобразование в соответствии с ASCII кодами (используется по умолчанию)
- 1 - текстовое представление с добавлением "0x" в начале строки, например так "0x0123456789"
- 2 - текстовое представление без добавленя "0x" в начале строки, например так "0123456789"
результат
- DECLARE @hexbin varbinary(max);
- SET @hexbin = 0x48656C6C6F20576F726C64212121;
- SELECT 1, CONVERT(varchar(MAX), @hexbin)
- union
- SELECT 2, CONVERT(varchar(MAX), @hexbin,1)
- union
- SELECT 3, CONVERT(varchar(MAX), @hexbin,2)
* This source code was highlighted with Source Code Highlighter.
При конвертировании в обратную сторону ( из hexString в binary) у нас теже 3 стиля, которые задают условие преобразования.---------------------------------------------------- style result ----------- ---------------------------------------- 0 Hello World!!! 1 0x48656C6C6F20576F726C64212121 2 48656C6C6F20576F726C64212121
- DECLARE @hexstring varchar(max);
- SET @hexstring = '0x48656C6C6F20576F726C64212121';
- SELECT 0 style, CONVERT(varbinary(MAX), @hexstring) result
- union
- SELECT 1, CONVERT(varbinary(MAX), @hexstring,1)
* This source code was highlighted with Source Code Highlighter.
результат
- DECLARE @hexstring varchar(max);
- SET @hexstring = '48656C6C6F20576F726C64212121';
- SELECT 0 style, CONVERT(varbinary(MAX), @hexstring) result
- union
- SELECT 2, CONVERT(varbinary(MAX), @hexstring,2)
* This source code was highlighted with Source Code Highlighter.
----------------------------------------------------------------------------- style result ----------- ----------------------------------------------------------------- 0 0x307834383635364336433646323035373646373236433634323132313231 1 0x48656C6C6F20576F726C64212121
----------------------------------------------------------------------------- style result ----------- ----------------------------------------------------------------- 0 0x34383635364336433646323035373646373236433634323132313231 2 0x48656C6C6F20576F726C64212121
Msg 8114, Level 16, State 5, Line 1 Ошибка при преобразовании типа данных varchar к varbinary.
суббота, 23 июня 2012 г.
Подсветка синтаксиса
Каждый, кто ведет блог связаный с программированием, хотел бы, выкладывая свой код, делать его "красивым" и в соответствии с общепринятой подсветкой, аналогичной используемому редактору. Вот для таких случаев и пригодятся найденые мною online сервисы.
http://source.virtser.net/default.aspx
http://www.dpriver.com/pp/sqlformat.htm
http://poorsql.com/
http://highlight.hohli.com/
http://tohtml.com/sql/
http://quickhighlighter.com/
http://markup.su/highlighter/
http://hilite.me/
понедельник, 11 июня 2012 г.
MSSQL : Повторная отправка почтовых сообщений
Нашел в архиве :)
На работе переключили почтовый сервер, и конечно забыли исправить в MSSQL.
Поменяли, и за одно повторно отправили сообщения :)
DECLARE @mailitem_id [INT]DECLARE @rc INT,
@sendmailxml VARCHAR(max)
DECLARE @checkDate [DATETIME]
SELECT @checkDate = '20120326'
SELECT @mailitem_id = Min([mailitem_id])
FROM msdb.dbo.sysmail_mailitems
WHERE [sent_status] = 2
AND [send_request_date] > @checkDate
WHILE @mailitem_id IS NOT NULL
BEGIN
SET @sendmailxml =
'<requests:SendMail xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.microsoft.com/databasemail/requests RequestTypes.xsd" xmlns:requests="http://schemas.microsoft.com/databasemail/requests"><MailItemId>'
+ CONVERT(NVARCHAR(20), @mailitem_id)
+ N'</MailItemId></requests:SendMail>'
-- Send the send request on queue.
EXEC @rc = Sp_sendmailqueues
@sendmailxml
IF @rc <> 0
BEGIN
RAISERROR(14627,16,1,@rc,'send mail')
--GOTO ErrorHandler;
END
SELECT @mailitem_id = Min([mailitem_id])
FROM msdb.dbo.sysmail_mailitems
WHERE [sent_status] = 2
AND [send_request_date] > @checkDate
AND [mailitem_id] > @mailitem_id
END
четверг, 7 июня 2012 г.
MSSQL : Как использовать row_number() в фильтре where
Надеюсь меня не лишат моего маленького звания MSTS....
Сегодня пока решал проблему отправки сообщений, наткнулся на то что row_number() нельзя напрямую использовать в фильтре where.
Честно говоря, когда мною использовалась нумерация, она обычно лежала где-нибудь в подзапросах или CTE, а тут хотел все и сразу, на что MS SQL сказал "неа....".
Собственно если вы попытаетесь выполнить такой код:
- USE AdventureWorks
- GO
-
- SELECT ROW_NUMBER() OVER (
- ORDER BY addressline1
- ) AS rowNum
- ,addressline1
- ,city
- FROM person.address
- WHERE rowNum > 3;
* This source code was highlighted with Source Code Highlighter.То получите такую ошибку:
Server: Msg 207, Level 16, State 1, Line
6 Invalid column name 'rowNum'А решать проблему будем по старинке.
Вариант первый (подзапрос):
- SELECT *
- FROM (
- SELECT ROW_NUMBER() OVER (
- ORDER BY addressline1
- ) AS rowNum
- ,addressline1
- ,city
- FROM person.address
- ) AS x
- WHERE rowNum > 3;
* This source code was highlighted with Source Code Highlighter.Вариант второй (CTE):
- WITH x (
- rowNum
- ,addressline1
- ,city
- )
- AS (
- SELECT ROW_NUMBER() OVER (
- ORDER BY addressline1
- ) AS rowNum
- ,addressline1
- ,city
- FROM person.address
- )
- SELECT *
- FROM X
- WHERE rowNum > 3;
* This source code was highlighted with Source Code Highlighter.
Linux : Ubuntu 12.04 - Пока размышления
Собственно как и многие из Вас дождался в апреле новой версии LTS 12.04 (26 апреля она вышла же если память не изменяет).
Не стал писать о первых впечатлениях, так как они были ммм.... сильно отрицательны.
Нет, не интерфейс, хотя сам до сих пор на 10.04 сижу, эта проблема для меня наименее значительна.
Проблема была в работе экрана, который при загрузке решил поморгать, с частотой где-то 2-3 раза в секунду.
К счастью или к сожалению времени разбираться у меня не было, по этой причине откатился назад на уже стабильную предыдущую версию (10.04).
А пост размышлениями назвал, так как подумываю может что-то изменилось за эти 2 месяца, и может стоит попробовать еще раз установить, например на ближайших выходных?
MSSQL : Расчет нарастающим итогом, без курсора
Обожаю классические задачи, вот и сегодня наткнулся на одну из них.
Суть была такова:
Сейчас переписываем систему оповещений о результатах выполнения процедур.
Результат кладется в таблицы и с использованием sp_send_dbmail рассылается адресатам.
Но есть одна проблема, рассылаемый объем не может превышать 1000000 байт (ограничение MS SQL).
Ладно ок, SQL не хочет, тогда надо вводить ограничение, а как?
Имперический метод себя не оправдает, так как объем вычисляется по суммарной длине строк подзапроса (про запрос см. MSDN) и ограничение по ширине строки тоже, тогда нам необходимо заранее рассчитать: а сколько байт будет наше сообщение.
Обратившись к статистике и финансовому учету (ага забавно как эти дисциплины помогают решать проблемы), можно вспомнить о таком термине как "нарастающий итог" (google расскажет о методе :) ).
Так давайте решим задачу "нарастающего итога"
Пусть у нас есть таблица только с одной ячейкой самого сообщения, например:
- CREATE TABLE msgbodytosend
- (
- mgsstring VARCHAR(max)
- );
* This source code was highlighted with Source Code Highlighter.Первый вариант - использование одного запроса.
Нам необходимо будет посчитать длины строк и их пронумеровать, в оригинале у нас нет уникального поля.
Дальше используя произведение (INNER JOIN) таблиц и суммирование длин, получаем необходимый результат.
Подзапросы я предпочитаю оборачивать в CTE выражения. На выходе получаем следующий код:
- WITH
- cte_msgbody AS (
- SELECT row_number() (ORDER BY msgstring) row
- , msgstring
- , len(msgstring) msgstring_len
- )
- SELECT
- mb_l.row
- , mb_l.msgstring
- , sum(mb_r.msgstring_len) Running_Total
- FROM cte_msgbody mb_l
- INNER JOIN cte_msgbody mb_r ON mb_l >= mb_r
- GROUP BY
- mb_l.row
- , mb_l.msgstring
- HAVING sum(mb_r.msgstring_len) < 1000000
* This source code was highlighted with Source Code Highlighter.Второй вариант разбить решение на составные части.
В данном случае мы убираем CTE выражение и делаем предварительный расчет во временной таблице.
- -- Удалим временную таблицу если вдруг она использовалась ранее в тексте
- IF object_id('tempdb..#msgbody')
- DROP TABLE #msgbody;
-
- -- И создадим ее за ново.
- CREATE TABLE #msgbody (
- row INT identity(1, 1) PRIMARY KEY NOT NULL
- , msgstring VARCHAR(max) NULL
- , msgstring_len INT NULL
- , running_total INT NULL
- );
-
- -- Заполняем таблицу
- INSERT INTO #msgbody (
- msgstring
- , msgstring_len
- )
- SELECT msgstring
- , len(msgstring)
- FROM msgbodytosend
-
- -- Теперь сделаем расчет
- UPDATE mb
- SET running_total = (
- SELECT sum(msgstring_len)
- FROM #msgbody mb_i
- WHERE mb_i.row <= mb.row
- )
- FROM #msgbody mb
- -- Добавим небольшое ограничение, этот кусок можно улучшить
- WHERE (
- SELECT sum(msgstring_len)
- FROM #msgbody mb_i
- WHERE mb_i.row <= mb.row
- ) < 1000000
* This source code was highlighted with Source Code Highlighter.Вот собственно и все, есть еще вариант решения этой задачи с использованием цикла и курсора, но мне они не кажутся столь интересными.

