2016-04-13 1 views
5

У меня есть сложный источник данных с плоскими файлами. Данные сгруппированы, например:
Как загрузить сгруппированные данные с помощью SSIS

Country City 
U.S.  New York 
      Washington 
      Baltimore 
Canada  Toronto 
      Vancouver 

Но я хочу, чтобы этот формат, когда он загружен в базу данных:

Country City 
U.S.  New York 
U.S.  Washington 
U.S.  Baltimore 
Canada  Toronto 
Canada  Vancouver 

Любой встретил такую ​​проблему раньше? У вас есть идея справиться с этим?
Единственная идея, которую я получил сейчас, это использовать курсор, но он слишком медленный.
Спасибо!

+0

Если у вас нет другого столбца, такого как идентификатор, чтобы указать, в какой стране расположены города, для СУБД невозможно получить то, что вам нужно. потому что они не идут в порядке. –

ответ

3

ответ на тя будет работать, но вот другой в случае, если необходимо делать это в SSIS остроумие hout временные/промежуточные таблицы:

Вы можете запустить поток данных через трансформацию сценария, которая использует переменную уровня DataFlow. Поскольку каждая строка входит в скрипт, проверяется значение столбца Страна.

Если оно имеет непустое значение, заполните эту переменную значением и передайте ее в потоке данных.

Если страна имеет пустое значение, перепишите ее на значение переменной, которое будет последним непустым значением страны, которое вы получили.

EDIT: Я посмотрел вверх ваше сообщение об ошибке и узнал что-то новое о компонентах сценариев (инструмент потока данных, в отличие от задачи сценария, инструмент Flow Control):

Коллекция ReadWriteVariables доступна только в методе PostExecute, чтобы максимизировать производительность и минимизировать риск конфликтов блокировки . Поэтому вы не можете напрямую увеличивать значение переменной переменной пакета при обработке каждой строки данных.Затем добавьте значение локальной переменной и установите значение переменной для значения локальной переменной в методе PostExecute после того, как все данные обработаны. Вы также можете использовать свойство VariableDispenser, чтобы обойти это ограничение, как , описанное далее в этом разделе. Однако запись непосредственно в пакет переменной при обработке каждой строки негативно скажется на производительности и повысит риск конфликтов при блокировке.

Это происходит из this MSDN article, который также имеет более подробную информацию о переменной Диспенсер обходным, если вы хотите идти по этому пути, но, видимо, я вводить в заблуждение вас выше, когда я сказал, что вы можете установить значение переменной пакета в скрипте. Вы должны использовать переменную, которая является локальной для скрипта, а затем изменить ее в обработчике событий Post-Execute. Я не могу сказать из статьи, означает ли это, что вы не сможете прочитать переменную в скрипте, и если это так, то единственным вариантом будет Диспетчер переменных. Или, я полагаю, вы могли бы создать другую переменную, для которой сценарий будет иметь доступ только для чтения, и установить его значение в выражение, чтобы оно всегда имело значение переменной read-write. Это может сработать.

+0

Спасибо, друг. Я попытался использовать Cursor, который, как я думаю, похож на вашу идею. Но у меня на самом деле есть 20k + строк, поэтому запуск строки за строкой слишком медленный ...... –

+0

Хорошо, я попробовал ваш метод, но у меня всегда появилось сообщение об ошибке «Коллекция переменных, заблокированных для чтения и записи не доступен за пределами PostExecute ". Но я помещаю назначение переменной в PostExecute ....... знак, не знаю, что пошло не так. Спасибо –

+0

Возможно, вам не разрешено читать переменную ReadWrite в скрипте. Я отредактировал свой ответ с некоторой новой информацией, которую я узнал. –

3

Да, это возможно. Прежде всего, необходимо загрузить данные в таблицу со столбцом IDENTITY:

-- drop table #t 
CREATE TABLE #t (id INTEGER IDENTITY PRIMARY KEY, 
Country VARCHAR(20), 
City VARCHAR(20)) 

INSERT INTO #t(Country, City) 
SELECT a.Country, a.City 
FROM OPENROWSET(BULK 'c:\import.txt', 
    FORMATFILE = 'c:\format.fmt', 
    FIRSTROW = 2) AS a; 

select * from #t 

Результат будет:

id   Country    City 
----------- -------------------- -------------------- 
1   U.S.     New York 
2        Washington 
3        Baltimore 
4   Canada    Toronto 
5        Vancouver 

А теперь немного рекурсивных CTE магии вы можете заполнить недостающие детали:

;WITH a as(
    SELECT Country 
      ,City 
      ,ID 
    FROM #t WHERE ID = 1 
    UNION ALL 
    SELECT COALESCE(NULLIF(LTrim(#t.Country), ''),a.Country) 
      ,#t.City 
      ,#t.ID 
    FROM a INNER JOIN #t ON a.ID+1 = #t.ID 
    ) 
SELECT * FROM a 
OPTION (MAXRECURSION 0) 

Результат:

Country    City     ID 
-------------------- -------------------- ----------- 
U.S.     New York    1 
U.S.     Washington   2 
U.S.     Baltimore   3 
Canada    Toronto    4 
Canada    Vancouver   5 

Update:

Как Tab Аллеман предлагаемых ниже того же результата можно достичь без рекурсивного запроса:

SELECT ID 
    , COALESCE(NULLIF(LTrim(a.Country), ''), (SELECT TOP 1 Country FROM #t t WHERE t.ID < a.ID AND LTrim(t.Country) <> '' ORDER BY t.ID DESC)) 
    , City 
FROM #t a 

BTW, формат файла для ввода данных является это (если вы хотите попробовать сценарии сохранить введенные данные в качестве C: \ import.txt и формат файла ниже, как C: \ format.fmt):

9.0 
    2 
    1  SQLCHAR  0  11  ""  1  Country  SQL_Latin1_General_CP1_CI_AS 
    2  SQLCHAR  0  100  "\r\n" 2  City   SQL_Latin1_General_CP1_CI_AS 
+0

Спасибо cha. Я попробовал вашу идею, но в моем случае это не сработало. Потому что у меня на самом деле есть 20k + строк в моей таблице. Поэтому я получил сообщение об ошибке «CTE recurs более 100 раз». –

+0

Вы можете использовать самосоединение вместо рекурсивного CTE, чтобы избежать ограничения рекурсии. –

+0

Спасибо @TabAlleman. Я обновил свой ответ, чтобы включить версию без рекурсии. – cha

 Смежные вопросы

  • Нет связанных вопросов^_^