Борьба с SQL BCP (uniqidentifier, значения по умолчанию для столбцов ...) - PullRequest
0 голосов
/ 25 мая 2011

РЕДАКТИРОВАТЬ: моя единственная нерешенная проблема - c) (Истина и Ложь в файле, бит в базе данных, я не могу изменить ни файл, ни схему базы данных, есть сотни терабайт, к которым я не могу прикоснуться).

Система получает файл (на самом деле сотни тысяч) с определенным форматом. Вещи являются: а) Первый тип является уникальным идентификатором (подробнее об этом позже) b) В базе данных первые 4 значения таблицы генерируются базой данных (они связаны с датами), что означает, что эти 4 значения не найдены в файлах (все остальные - и находятся в порядке - даже если это всегда их представление в виде текста или они пусты) c) Значения бит представлены в файле как False / True.

Итак, проблема для 1 заключается в том, что в текстовом файле, который я получаю в качестве входных данных, uniqidentifier использует скобки. Когда я попытался сгенерировать файл с параметрами форматирования nul с помощью командного инструмента bcp, он сделал бы его sqlchar с 37 символами (что для меня не имеет смысла, так как это будет либо 36, либо 38).

Разделитель строк - «+++ \ r \ n», разделитель столбцов - «© ® ©».

Как бы мне было сгенерировать файлы формата? Я застрял с этим в течение некоторого времени, я никогда раньше не использовал bcp, и ошибки, которые у меня есть, мало что говорят («неожиданный EOF, обнаруженный в файле данных BCP»)

Должен ли я указывать все столбцы в файле формата или только те, которые я хочу прочитать из файлов, которые я получаю?

Спасибо!

ПРИМЕЧАНИЕ. Я не могу предоставить схему SQL, поскольку она предназначена для компании, в которой я работаю. Но это в значительной степени: smalldate, tinyint tinyint tinyint (эти четыре сгенерированы БД), uniqidentifier, символы, символы, больше varchars, некоторые биты, больше varchars, некоторые nvarchar. ВСЕ значения, кроме тех, которые сгенерированы БД, принимают ноль.

Моя текущая проблема с пропуском первых 4 столбцов.

http://msdn.microsoft.com/en-us/library/ms179250(v=SQL.105).aspx

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

Это тот, который сгенерирован с помощью bcp (с форматом nul -c). Заметьте, я поставил его как ссылку, потому что он не такой короткий. http://pastebin.com/4UkpPp1n

Второй, который должен делать то же самое, но игнорируя первые 4 столбца, находится в следующей вставке: http://pastebin.com/Lqj6XSbW

Все же это не работает. Ошибка «Ошибка = [Microsoft] [Собственный клиент SQL] Число полей, предоставленных для операции bcp, меньше, чем число столбцов на сервере.», Что должно было стать целью всего этого.

Любая помощь будет принята с благодарностью.

Ответы [ 2 ]

0 голосов
/ 26 мая 2011

Похоже, что BCP не может понимать True и False как битовые значения. Лучше либо использовать SSIS, либо сначала заменить содержимое текста (не очень хорошая идея создавать представления или что-то в этом роде, это более затратно).

0 голосов
/ 25 мая 2011

Я бы создал новую таблицу с CHAR(38) для GUID.Импортируйте ваши данные в эту промежуточную таблицу, а затем переведите ее с CAST(SUBSTRING(GUID, 2, 36) AS UNIQUEIDENTIFIER), чтобы импортировать промежуточные данные в вашу постоянную таблицу.Этот подход также хорошо работает для дат в нечетных форматах, чисел с символами валюты или вообще для любого вида плохо отформатированного ввода.

Файлы формата BCP немного обидчивы, но в принципе не слишком сложны.Если эта часть по-прежнему доставляет вам проблемы, один из вариантов - импортировать всю строку в виде одного поля VARCHAR(1000), а затем разделить его на SQL - если вам удобна обработка текста SQL.

В качестве альтернативы, если вы знакомы с каким-либо другим языком программирования, например Perl или C #, вы можете создать сценарий для предварительной обработки ваших входных данных в более удобную форму, например, разделенную табуляцией.Если вы не знакомы с каким-либо другим языком программирования, я предлагаю вам выбрать один и начать!SQL - отличный язык, но иногда вам нужен другой инструмент;это не очень хорошо для обработки текста.

Если вы знакомы с C #, вот мой код для генерации файла формата.Никто не смеется над моим отступом в Уайтстоуне: P

private static string  CreateFormatFile(string filePath, SqlConnection connection, string tableName, string[] sourceFields, string[] destFields, string fieldDelimiter, string fieldQuote)
    {
    string         formatFilePath = filePath + ".fmt";
    StreamWriter   formatFile     = null;
    SqlDataReader  data           = null;
    try
        {
        // Load the metadata for the destination table, so we can look up fields' ordinal positions
        SqlCommand  command = new SqlCommand("SELECT TOP 0 * FROM " + tableName, connection);
                    data    = command.ExecuteReader(CommandBehavior.SchemaOnly);
        DataTable   schema  = data.GetSchemaTable();

        Dictionary<string, Tuple<int, int>>  metadataByField = new Dictionary<string, Tuple<int, int>>();
        foreach (DataRow row in schema.Rows)
            {
            string  fieldName = (string)row["ColumnName"];
            int     ordinal   = (int)row["ColumnOrdinal"] + 1;
            int     maxLength = (int)row["ColumnSize"];
            metadataByField.Add(fieldName, new Tuple<int, int>(ordinal, maxLength));
            }

        // Begin the file, including its header rows
        formatFile = File.CreateText(formatFilePath);
        formatFile.WriteLine("10.0");
        formatFile.WriteLine(sourceFields.Length);

        // Certain strings need to be escaped to use them in a format file
        string  fieldQuoteEscaped     = fieldQuote     == "\"" ? "\\\"" : fieldQuote;
        string  fieldDelimiterEscaped = fieldDelimiter == "\t" ? "\\t"  : fieldDelimiter;

        // Write a row for each source field, defining its metadata and destination field
        for (int i = 1;  i <= sourceFields.Length;  i++)
            {
            // Each line contains (separated by tabs): the line number, the source type, the prefix length, the field length, the delimiter, the destination field number, the destination field name, and the collation set
            string  prefixLen   = i != 1 || fieldQuote == null ? "0" : fieldQuote.Length.ToString();
            string  fieldLen;
            string  delimiter   = i < sourceFields.Length ? fieldQuoteEscaped + fieldDelimiterEscaped + fieldQuoteEscaped : fieldQuoteEscaped + @"\r\n";
            string  destOrdinal;
            string  destField   = destFields[i - 1];
            string  collation;
            if (destField == null)
                {
                // If a field is not being imported, use ordinal position zero and a placeholder name
                destOrdinal = "0";
                fieldLen    = "32000";
                destField   = "DUMMY";
                collation   = "\"\"";
                }
            else
                {
                Tuple<int, int>  metadata;
                if (metadataByField.TryGetValue(destField, out metadata) == false)  throw new ApplicationException("Could not find field \"" + destField + "\" in table \"" + tableName + "\".");
                destOrdinal = metadata.Item1.ToString();
                fieldLen    = metadata.Item2.ToString();
                collation   = "SQL_Latin1_General_CP1_CI_AS";
                }
            string  line = String.Join("\t", i, "SQLCHAR", prefixLen, fieldLen, '"' + delimiter + '"', destOrdinal, destField, collation);
            formatFile.WriteLine(line);
            }

        return formatFilePath;
        }
    finally
        {
        if (data       != null)  data.Close();
        if (formatFile != null)  formatFile.Close();
        }
    }

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

...