Борьба с SQL BCP (uniqidentifier, значения по умолчанию для столбцов)

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

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

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

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

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

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

Спасибо!

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

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

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

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

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

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

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

Любая помощь будет оценена.


person Mamsaac    schedule 24.05.2011    source источник


Ответы (2)


Я бы создал новую таблицу с 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 для чтения данных в то время.

person Community    schedule 24.05.2011
comment
Я знаю несколько языков программирования :) Моя текущая проблема связана с разным количеством столбцов. Я подписался на msdn.microsoft.com/en-us /library/ms179250(v=SQL.105).aspx для первого метода, но он не работает. Я собираюсь обновить вопрос с этой информацией. - person Mamsaac; 25.05.2011

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

person Mamsaac    schedule 25.05.2011
comment
это было давно, но действительно это был единственный путь. BCP по-прежнему быстрее, чем SSIS, даже если вам нужно заменить текст с помощью C#, и он лучше масштабируется. Удивительно, учитывая, что SSIS существует уже некоторое время... у него гораздо больше функциональных возможностей, но он медленнее. - person Mamsaac; 27.09.2011