Metrika

28 февраля 2011 г.

Миграция данных из PostgreSQL в MS SQL Server

Миграция данных из PostgreSQL 8.3 в SQL Server 2008 R2 Standard  Edition.
Сначала я попытался прилинковать PostgreSQL к SQL Server, используя оригинальный драйвер ODBC. Когда создавал соединение, все было хорошо, т.е. Test Connection проходил, но при попытке линка вываливалась ошибка. Я не стал особо копать, т.к. знаю, что ODBC - один из самых глючных способов линковки к SQL Server. Проблемы возникают даже когда к одному SQL Server линкуется другой SQL Server.
Далее я начал искать другие дрова. Первыми нашел вот это http://pgfoundry.org/frs/?group_id=1000085&release_id=539 , но не стал качать, т.к. смутила дата последнего релиза  - 17.04.2006.
Далее на сайте PostgreSQL я нашел список различных дров: http://www.postgresql.org/download/products/2. По описанию мне понравились вот эти PGNP, я пробовал, работает отлично, порадовало так же поддержка SSIS, но из-за платности, я от них отказался.
Далее мой выбор пал на продукт, под названием Npqsql, по сути это библиотека для .Net, бесплатная, на ней я и остановился, но для миграции данных потребовалось написать код на C#:
using System;
using System.Collections.Generic;
using System.Text;

using System.Configuration;
using System.IO;

using Npgsql;

namespace Zapuskatr
{
    class Program
    {
        static string COLS_DELIMITER = "|";
        static string ROW_DELIMITER = "\n";
        static string CNN_STRING = null;
        static string QUERY = null;
        static string PATH = null;

        static void Main(string[] args)
        {
            if (CNN_STRING == null)
            {
                Console.WriteLine("ERROR: Connection string is empty.");
                return;
            }

            NpgsqlConnection cnn = null;
            NpgsqlCommand cmd = null;
            NpgsqlDataReader reader = null;
            try
            {

                cnn = new NpgsqlConnection(CNN_STRING);
                cnn.Open();
                if (QUERY != null)
                {
                    cmd = new NpgsqlCommand(QUERY, cnn);
                    reader = cmd.ExecuteReader();
                    int columns = reader.FieldCount;

                    if (PATH == null)
                    {
                        while (reader.Read())
                        {
                            Console.Write(reader[0].ToString());
                            for (int i = 1; i < columns; i++)
                            {
                                Console.Write(COLS_
DELIMITER + reader[i].ToString());
                            }

                            Console.Write(ROW_DELIMITER);
                        }
                    }
                    else
                    {
                        StreamWriter sw = new StreamWriter(PATH, false);

                        while (reader.Read())
                        {
                            sw.Write(reader[0].ToString());
                            for (int i = 1; i < columns; i++)
                            {
                                sw.Write(COLS_
DELIMITER + reader[i].ToString());
                            }

                            sw.Write(ROW_DELIMITER);
                        }

                        sw.Flush();
                        sw.Close();
                    }

                    cmd.Dispose();
                }
                else
                    Console.WriteLine("Version: {0}", cnn.ServerVersion);
            }
            catch (Exception ex)
            {
                Console.WriteLine("ERROR: {0}", ex.Message);
            }
            finally
            {
                cnn.Close();
            }

        }
    }
}

Консольная программа - если PATH не задан, то выводит в консоль. Если QUERY не задан, то выводит версию PostgreSQL. COLS_DELIMITER - разделитель колонок, ROW_DELIMITER - разделитель строк. Код сам определяет количество колонок в запросе и выводит их все.
Далее, основываясь на этом коде, я создавал CSV файлы и с помощью SSIS закачивал их в SQL Server.

18 февраля 2011 г.

Как поменять область видимости переменной в BIDS (SSIS)

Если у кого то, как и у меня стоит английский BIDS, то под областью видимости я понимаю свойство Scope, которое поменять стандартными средствами невозможно. И я совершенно не понимаю почему!!!

Ну да ладно. Для своего пакета SSIS я создал порядка 10 переменных и потом заметил, что все имеют область видимости только в одном элементе, а нужны были во многих. Единственный путь поменять область видимости - это удалить переменные и создать заново, прям как работа над ошибками в школе ;)

Естественно этот путь меня не впечатлил, поэтому я открыл код пакета в текстовом редакторе. В очередной раз ужаснувшись увиденному, я понял, что этот путь тоже не лучший. Поэтому пошел в Google. И нагуглил инструмент под названием BIDS Helper. Детально я в нем не разбирался, судя из увиденного возможностей довольно много. Единственное, чего хотелось, скорее поменять область видимости, как это сделать написано тут. Область видимости поменял, правда после его установки при наведении на перемнную фокуса ввода перестало появляться окошко свойств, но я думаю это фича, а не баг.

11 февраля 2011 г.

Экспорт из MySQL в CSV файл

SELECT *
INTO OUTFILE 'products.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
ESCAPED BY '\\'
LINES TERMINATED BY '\n'
FROM products

На Windows файл появиться в папке текущей БД.

Импорт в MySQL из дамп-файла

$ mysql -u username -p -h localhost data-base-name < data.sql

Подробнее тут.

Работа с логами джобов в SQL Server

Для более удобного анализа истории выполнения джобов, например для анализа времени выполнения, стандартный лог не очень подходит. Но все данные, которые видны в истории выполнения джоба хранятся в таблице msdb..sysjobhistory. Вот ее структура:
CREATE TABLE dbo.sysjobhistory(
    instance_id int IDENTITY(1,1) NOT NULL,
    job_id uniqueidentifier NOT NULL,
    step_id int NOT NULL,
    step_name sysname NOT NULL,
    sql_message_id int NOT NULL,
    sql_severity int NOT NULL,
    message nvarchar(4000) NULL,
    run_status int NOT NULL,
    run_date int NOT NULL,
    run_time int NOT NULL,
    run_duration int NOT NULL,
    operator_id_emailed int NOT NULL,
    operator_id_netsent int NOT NULL,
    operator_id_paged int NOT NULL,
    retries_attempted int NOT NULL,
    server sysname NOT NULL
)
Тут есть описание этой таблицы.
job_id определяется по таблице  msdb..sysjobs, например вот так:
SELECT * FROM msdb..sysjobs WHERE name like '%job%'
Детально со всеми полями не разбирался, назначение большинства понятно из названий. Меня интересовало поле run_duration. Поле имеет тип int, там хранится время выполнения шага (строки, в которых step_id = 0, относятся ко всему джобу, т.е. в такой строке в поле run_duration будет время выполнения всего джоба). Формат: hhmmss, поскольку тип int, нули слева не отображаются. Т.е. значение 709 - это 7 минут 9 сек, 34508 - 3 часа 45 минут 8 секунд.
Для преобразования этого формата в секунды я написал функцию:
-- Конвертация времени из формата HHMMSS типа int в секунды
create function fn_convert_intdt_to_sec ( @ts int )
returns int
as
begin
    if (@ts < 60) return @ts
   
    declare @sec int = @ts - (@ts/100)*100
    declare @min int = (@ts - (@ts/10000)*10000)/100
    declare @hr int = @ts/10000
   
    return @hr*3600 + @min*60 + @sec
end
В принципе если в передаваемом значении часы будут содержать более 2-х знаков, то тоже отработает.

8 февраля 2011 г.

Ошибка в SSIS "[SQL Server Destination [46]] Error: Unable to prepare the SSIS bulk insert for data insertion." при миграции данных из текстового файла в SQL Server

Исходные данные: Microsoft SQL Server Standard Edition 2008 R2 (64-bit) стоит на Microsoft Windows Server 2008 R2 Standard.
Копируются данные их текстового файла в таблицу. Для этого создается SSIS пакет с элементом Data Flow, в котором создается два элемента: Flat File Source и SQL Server Destination. При запуске пакета, еще перед началом считывания данных из текстового файла, пакет останавливается со следующими сообщениями об ошибке:
[SQL Server Destination [46]] Error: Unable to prepare the SSIS bulk insert for data insertion.
[SSIS.Pipeline] Error: component "SQL Server Destination" (46) failed the pre-execute phase and returned error code 0xC0202071.
После недолгого гуглинья я наткнулся на пост,  где описывается подобная проблема. Там пишут, что если SSIS-пакет выполняется не на той же машине, на которую указывает SQL Server Destination, то надо поменять его на OLE DB Destination. У меня все выполняется на одной машине. Тем не менее, я поменял SQL Server Destination на OLE DB Destination и все заработало.

4 февраля 2011 г.

Миграция данных из MySQL в MS SQL Server

Два сервера: 192.168.0.1 (MySQL Community Server 5.5.8) и 192.168.0.2 (SQL Server 2008 R2)
1. На сервере 192.168.0.1 необходимо создать пользователя:
CREATE USER 'user_name'@'%' IDENTIFIED BY 'password';
GRANT select ON db_name.* TO 'user_name'@'%';
'%' - обозначает, что этот пользователь может конектиться к серверу MySQL с любого хоста. Подробнее о создание пользователей в MySQL читайте здесь.
2. На сервер 192.168.0.2 необходимо установить коннектор для Windows.


Далее можно создавать пакет SSIS или сделать единоразовую загрузку.

Для единоразовой загрузки необходимо:
1. В Management Studio тыкаем правой кнопкой на базе, в меню выбираем Tasks -> Export Data...
2. В открывшемся окне выбираем Data Source -> .Net Framework Data Provider for MySQL
3. Заполняем поля:
Database, Server, Password, User Id
4. Далее настраиваем получатель данных: Server, Database
5. В следующем окне выбираем "Write a query to specify the data to transfer"
6. Далее вводим запрос для извлечения данных: SELECT * FROM table_name
7. В следующем окне вставляем имя таблицы в SQL Server и жмем в этом и следующем окне Next.

А теперь о проблемах.
В MySQL в таблице было примерно 2,2 млн. записей. Когда я писал запрос
SELECT * FROM table_name
то данные не копировались, если писал вот так:
SELECT * FROM table_name LIMIT 5
копировалось, как и должно было, 5 строк, но если я писал вот так:
SELECT * FROM table_name LIMIT 3000000
выдавало ошибку.
После долгих мытарств, я решил сохранить этот пакет как файл, получился файл с расширением dtsx, открыл его в текстовом редакторе и заменил LIMIT 5 на LIMIT 3000000, потом выполнил его и он, на удивление, все выполнил.

Настройка PHP в IIS 7

3 февраля 2011 г.

Изменение доменного имени сайта

Для того, что бы корректно (с точки зрания поисковых машин) перенести сайт с одного доменного имени на другое, нужно настроить переадреcацию с помощью кода перенаправления HTTP 301 (Moved Permanently). В заголовке ответа Location указывается, куда была перенесена страница.

В C# код выглядит вот так: 
string location = "new_name";
Response.Clear();
Response.StatusCode = 301;
Response.AddHeader("Location", location);
Response.End();
Код нужно вставить в Global.asax в метод  Application_BeginRequest.

Причем, если написать вот так:
Response.Headers["Location"] = location;
То будет возникать исключение: This operation requires IIS integrated pipeline mode

В своей статье Редирект 301 (Moved Permanently) в IIS 7 я более подробно описываю способы редиректа в IIS 7.