SSIS - таблица запросов с использованием идентификаторов из другой базы данных - PullRequest
1 голос
/ 20 февраля 2020

У меня есть таблица, содержащая только идентификаторы на SQL сервере, она имеет почти 1 миллион идентификаторов. Теперь я должен использовать эти идентификаторы для поиска в таблице в Oracle и вернуть только те строки, которые соответствуют этим миллионам идентификаторов (однозначное соответствие).

Проблема в Oracle, данных Oracle не из одной таблицы, это запрос с несколькими объединениями разных таблиц.

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

Идентификатор будет go в запросе в предложении where, например

where ID in (select ID from sql_server_table)

Каков эффективный способ сделать это в SSIS?

Ответы [ 2 ]

2 голосов
/ 20 февраля 2020

У меня такое ощущение, что этот ответ может быть излишним, но я думаю, что все это полезно при обработке передачи большого объема ETL с Oracle на SQL Сервер.

В этом примере я собираюсь потянуть детали счета. Это записи отдельных позиций для счетов-фактур (т.е. общих продаж) для крупной компании. Таблица, из которой я извлекаю в Oracle, содержит около 1 миллиарда строк, и мне нужно около 300 миллионов записей каждый раз. После того, как я их перетяну, я сравниваю их с тем, что у меня есть на SQL Server, и затем делаю необходимые ОБНОВЛЕНИЯ, ВСТАВКИ или УДАЛЕНИЯ. И я делаю это несколько раз в день, и теперь это занимает всего около 20 минут на весь процесс. Но в начале весь процесс занял несколько часов.

Чтобы извлечь необработанные данные из Oracle, я использую довольно простой пакет, который генерирует запрос для использования в Oracle, обрезает мой SQL сервер промежуточная таблица импорта (у меня есть еще несколько промежуточных таблиц позже), извлекает данные из Oracle, а затем вызывает pro c, который выполняет объединение (не фактическое MERGE, как мы увидим). Поток управления выглядит следующим образом:

enter image description here

Первым важным шагом является максимально возможное ограничение данных на стороне Oracle. Я не хочу тянуть все 1 миллиард строк каждый раз, когда запускается этот пакет. Поэтому я ограничиваю записи только теми записями, где Invoice Date больше или равно 1 января два года go. Но поскольку форматы даты между SQL Server и Oracle не очень совместимы (лучший метод, который я нашел для преобразования обоих в один и тот же строковый формат), я должен сделать некоторое форматирование, и потому что я хочу дату диапазон всегда должен быть корректным, исходя из дня, в который я работаю, мне нужно немного подсчитать встроенную дату.

SELECT
CAST("Data Source Code" AS VARCHAR2(3)) AS "DataSourceCode"  
,CAST("Order#" AS VARCHAR2(11)) AS "OrderNum"
,CAST("Invoice#" AS VARCHAR2(15)) AS "InvoiceNum"
,CAST("Item#" AS VARCHAR2(10)) AS "ItemNumber"
,CAST("Order Line Type" AS VARCHAR2(10)) AS "OrderLineType"
,CAST("Order Status" AS VARCHAR2(1)) AS "OrderStatus"
,"Order Date Time" AS "OrderDate"
,CAST("Fiscal Invoice Period" AS VARCHAR2(6)) AS "FiscalInvoicePeriod"
,"Invoice Date" AS "InvoiceDate"
,CAST("Ship To Cust#" AS VARCHAR2(8)) AS "ShipToCustNum"
,CAST("Billing Account #" AS VARCHAR2(8)) AS "BillingAccountNumber"
,CAST("Sales Branch Number" AS VARCHAR2(4)) AS "SalesBranchNumber"
,CAST("Price Branch Number" AS VARCHAR2(4)) AS "PriceBranchNumber"
,CAST("Ship Branch Number" AS VARCHAR2(4)) AS "ShippingBranchNumber"
,"Sold Qty" AS "SoldQty"
,"Unit Price" AS "UnitPrice"
,"Sales Amount" AS "SalesAmount"
,"Handling Amount" AS "HandlingAmount"
,"Freight Amount" AS "FreightAmount"
,"Unit Cogs Amount" AS "UnitCogsAmount"
,"Cogs Amount" AS "CogsAmount"
,"Unit Commcost Amount" AS "UnitCommcostAmount"
,"Commcost Amount" AS "CommcostAmount"
,CAST("GL Period" AS VARCHAR2(6)) AS "GLPeriod"
,"Order Qty" AS "OrderQty"
,"Margin %" AS "MarginPct"
,CONVERT("PO Number",'AL32UTF8','WE8MSWIN1252') AS "PONumber"
,CAST("Branch Id" AS VARCHAR2(4)) AS "BranchId"
,CAST("Outside Salesrep SSO" AS VARCHAR2(20)) AS "OutsideSalesrepSSO"
,CAST("Inside Salesrep SSO" AS VARCHAR2(20)) AS "InsideSalesrepSSO"
,CAST("Order Writer SSO" AS VARCHAR2(20)) AS "OrderWriterSSO"
,"Line Number" AS "LineNumber"
,CAST(UPPER(RAWTOHEX(SYS.DBMS_OBFUSCATION_TOOLKIT.MD5(input_string =>
    COALESCE(CAST("Data Source Code" AS VARCHAR2(4)),'') || '|' ||
    COALESCE(CAST("Order#" AS VARCHAR2(40)),'') || '|' ||
    COALESCE(CAST("Invoice#" AS VARCHAR2(15)),'') || '|' ||
    COALESCE(CAST("Item#" AS VARCHAR2(10)),'') || '|' ||
    COALESCE(CAST("Order Line Type" AS VARCHAR2(6)),'') || '|' ||
    COALESCE(CAST("Order Status" AS VARCHAR2(3)),'') || '|' ||
    COALESCE(CAST("Order Date" AS VARCHAR2(19)),'') || '|' ||
    COALESCE(CAST("Invoice Date" AS VARCHAR2(19)),'') || '|' ||
    COALESCE(CAST("Ship To Cust#" AS VARCHAR2(10)),'') || '|' ||
    COALESCE(CAST("Billing Account #" AS VARCHAR2(10)),'') || '|' ||
    COALESCE(CAST("Sales Branch Number" AS VARCHAR2(4)),'') || '|' ||
    COALESCE(CAST("Price Branch Number" AS VARCHAR2(4)),'') || '|' ||
    COALESCE(CAST("Ship Branch Number" AS VARCHAR2(4)),'') || '|' ||
    COALESCE(CAST("Sold Qty" AS VARCHAR2(20)),'') || '|' ||
    COALESCE(CAST(CAST("Unit Price" AS NUMBER(18,2)) AS VARCHAR2(20)),'') || '|' ||
    COALESCE(CAST(CAST("Sales Amount" AS NUMBER(18,2)) AS VARCHAR2(20)),'') || '|' ||
    COALESCE(CAST(CAST("Handling Amount" AS NUMBER(18,2)) AS VARCHAR2(20)),'') || '|' ||
    COALESCE(CAST(CAST("Freight Amount" AS NUMBER(18,2)) AS VARCHAR2(20)),'') || '|' ||
    COALESCE(CAST(TO_DATE('01-' || "Fiscal Invoice Period", 'DD-MON-YY') AS VARCHAR2(19)),'') || '|' ||
    COALESCE(CAST(TO_DATE('01-' || "GL Period", 'DD-MON-YY') AS VARCHAR2(19)),'') || '|' ||
    COALESCE(CAST("Order Qty" AS VARCHAR2(20)),'') || '|' ||
    COALESCE(CAST(CAST("Commcost Amount" AS NUMBER(18,2)) AS VARCHAR2(20)),'') || '|' ||
    COALESCE(CAST("Order Writer SSO" AS VARCHAR2(36)),'') || '|' ||
    COALESCE(CAST("Line Number" AS VARCHAR2(10)),'')
))) AS VARCHAR2(32)) AS "HashVal"
FROM MY_SCHEMA.MY_INVOICE_TABLE
WHERE "Invoice Date" >= TO_DATE( CONVERT(VARCHAR(10), DATEADD(YEAR,-2,DATEADD(MONTH,1-(DATEPART(MONTH,DATEADD(DAY,1-(DATEPART(DAY,GETDATE())),GETDATE()))),DATEADD(DAY,1-(DATEPART(DAY,GETDATE())),GETDATE()))), 111), 'yyyy/mm/dd')

Вы также заметите, что я вычисляю MD5 Ha sh ценность также. Эту технику я выбрал у очень классного Энди Леонарда . Функция Ha sh принимает строку и применяет одностороннюю криптографическую кодировку строки c на основе выбранного вами алгоритма. MD5 - самый сильный вариант для DBMS_OBFUSCATION_TOOLKIT, который у меня есть с моими ограниченными разрешениями на сервере Oracle. Сервер SQL также может генерировать те же значения Ha sh в MD5, что очень полезно.

Таким образом, общая идея заключается в том, что вы можете преобразовать все поля записи, которые вам нужны о отслеживании в строки и объединении их вместе, примените алгоритм хеширования, чтобы получить HashVal, затем присоедините исходную запись к записи назначения и сравните HashVals. Если HashVals не совпадают, вы знаете, что исходная запись изменилась, и вам нужно обновить запись. Если HashVal не изменился, то исходная запись, вероятно, не изменилась. Я говорю «вероятно», потому что даже с разделителями, вставленными в объединенную строку (чтобы мы могли различать guish между '1a2b' + '3c' + '4d' и '1a' + '2b3c' + '4d'; без разделителя: '1a2b3c4b' против '1a2b3c4b'; с разделителем: '1a2|b3c|4d' против '1a|2b3c|4d') все еще возможно, что MD5 га sh двух разных строк может быть одинаковым.

Но мы хотим динамически сгенерировать этот запрос, поэтому нам нужно вставить его в Execute SQL Task и присвоить его переменной, а затем сопоставить эту переменную с выходным параметром. Строковый запрос затем становится:

SELECT SourceQuery = 
'SELECT
CAST("Data Source Code" AS VARCHAR2(3)) AS "DataSourceCode"  
,CAST("Order#" AS VARCHAR2(11)) AS "OrderNum"
,CAST("Invoice#" AS VARCHAR2(15)) AS "InvoiceNum"
,CAST("Item#" AS VARCHAR2(10)) AS "ItemNumber"
,CAST("Order Line Type" AS VARCHAR2(10)) AS "OrderLineType"
,CAST("Order Status" AS VARCHAR2(1)) AS "OrderStatus"
,"Order Date Time" AS "OrderDate"
,CAST("Fiscal Invoice Period" AS VARCHAR2(6)) AS "FiscalInvoicePeriod"
,"Invoice Date" AS "InvoiceDate"
,CAST("Ship To Cust#" AS VARCHAR2(8)) AS "ShipToCustNum"
,CAST("Billing Account #" AS VARCHAR2(8)) AS "BillingAccountNumber"
,CAST("Sales Branch Number" AS VARCHAR2(4)) AS "SalesBranchNumber"
,CAST("Price Branch Number" AS VARCHAR2(4)) AS "PriceBranchNumber"
,CAST("Ship Branch Number" AS VARCHAR2(4)) AS "ShippingBranchNumber"
,"Sold Qty" AS "SoldQty"
,"Unit Price" AS "UnitPrice"
,"Sales Amount" AS "SalesAmount"
,"Handling Amount" AS "HandlingAmount"
,"Freight Amount" AS "FreightAmount"
,"Unit Cogs Amount" AS "UnitCogsAmount"
,"Cogs Amount" AS "CogsAmount"
,"Unit Commcost Amount" AS "UnitCommcostAmount"
,"Commcost Amount" AS "CommcostAmount"
,CAST("GL Period" AS VARCHAR2(6)) AS "GLPeriod"
,"Order Qty" AS "OrderQty"
,"Margin %" AS "MarginPct"
,CONVERT("PO Number",''AL32UTF8'',''WE8MSWIN1252'') AS "PONumber"
,CAST("Branch Id" AS VARCHAR2(4)) AS "BranchId"
,CAST("Outside Salesrep SSO" AS VARCHAR2(20)) AS "OutsideSalesrepSSO"
,CAST("Inside Salesrep SSO" AS VARCHAR2(20)) AS "InsideSalesrepSSO"
,CAST("Order Writer SSO" AS VARCHAR2(20)) AS "OrderWriterSSO"
,"Line Number" AS "LineNumber"
,CAST(UPPER(RAWTOHEX(SYS.DBMS_OBFUSCATION_TOOLKIT.MD5(input_string =>
    COALESCE(CAST("Data Source Code" AS VARCHAR2(4)),'''') || ''|'' ||
    COALESCE(CAST("Order#" AS VARCHAR2(40)),'''') || ''|'' ||
    COALESCE(CAST("Invoice#" AS VARCHAR2(15)),'''') || ''|'' ||
    COALESCE(CAST("Item#" AS VARCHAR2(10)),'''') || ''|'' ||
    COALESCE(CAST("Order Line Type" AS VARCHAR2(6)),'''') || ''|'' ||
    COALESCE(CAST("Order Status" AS VARCHAR2(3)),'''') || ''|'' ||
    COALESCE(CAST("Order Date" AS VARCHAR2(19)),'''') || ''|'' ||
    COALESCE(CAST("Invoice Date" AS VARCHAR2(19)),'''') || ''|'' ||
    COALESCE(CAST("Ship To Cust#" AS VARCHAR2(10)),'''') || ''|'' ||
    COALESCE(CAST("Billing Account #" AS VARCHAR2(10)),'''') || ''|'' ||
    COALESCE(CAST("Sales Branch Number" AS VARCHAR2(4)),'''') || ''|'' ||
    COALESCE(CAST("Price Branch Number" AS VARCHAR2(4)),'''') || ''|'' ||
    COALESCE(CAST("Ship Branch Number" AS VARCHAR2(4)),'''') || ''|'' ||
    COALESCE(CAST("Sold Qty" AS VARCHAR2(20)),'''') || ''|'' ||
    COALESCE(CAST(CAST("Unit Price" AS NUMBER(18,2)) AS VARCHAR2(20)),'''') || ''|'' ||
    COALESCE(CAST(CAST("Sales Amount" AS NUMBER(18,2)) AS VARCHAR2(20)),'''') || ''|'' ||
    COALESCE(CAST(CAST("Handling Amount" AS NUMBER(18,2)) AS VARCHAR2(20)),'''') || ''|'' ||
    COALESCE(CAST(CAST("Freight Amount" AS NUMBER(18,2)) AS VARCHAR2(20)),'''') || ''|'' ||
    COALESCE(CAST(TO_DATE(''01-'' || "Fiscal Invoice Period", ''DD-MON-YY'') AS VARCHAR2(19)),'''') || ''|'' ||
    COALESCE(CAST(TO_DATE(''01-'' || "GL Period", ''DD-MON-YY'') AS VARCHAR2(19)),'''') || ''|'' ||
    COALESCE(CAST("Order Qty" AS VARCHAR2(20)),'''') || ''|'' ||
    COALESCE(CAST(CAST("Commcost Amount" AS NUMBER(18,2)) AS VARCHAR2(20)),'''') || ''|'' ||
    COALESCE(CAST("Order Writer SSO" AS VARCHAR2(36)),'''') || ''|'' ||
    COALESCE(CAST("Line Number" AS VARCHAR2(10)),'''')
))) AS VARCHAR2(32)) AS "HashVal"
FROM MY_SCHEMA.MY_INVOICE_TABLE
WHERE "Invoice Date" >= TO_DATE('''+CONVERT(VARCHAR(10), DATEADD(YEAR,-2,DATEADD(MONTH,1-(DATEPART(MONTH,DATEADD(DAY,1-(DATEPART(DAY,GETDATE())),GETDATE()))),DATEADD(DAY,1-(DATEPART(DAY,GETDATE())),GETDATE()))), 111)+''', ''yyyy/mm/dd'')'

В потоке данных у меня есть Oracle Source и OLE DB Destination. Сначала мне пришлось «простить» Oracle Source, настроив его на использование команды SQL в качестве ввода, а затем введя запрос с жестко закодированным сравнением дат. Как только я закончил создание потока данных, я вернулся к потоку управления, открыл свойства Data Flow Task и создал новую запись выражений. Я сопоставил переменную исходного запроса SSIS с [Oracle Source].[SqlCommand].

enter image description here

Также очень важно настроить BatchSize и размер буфера. По умолчанию источник Attunity Oracle использует BatchSize из 100. В отличие от этого, я обнаружил, что этот конкретный перенос хорошо работает с BatchSize из 120000

.

Суть сохраненного pro c, который объединяет данные, такова:

--This is an intermediate work table    
TRUNCATE TABLE dbo.InvoiceWorkTable


--It gets populated from the SQL Server staging table where the raw Oracle data is stored
INSERT INTO dbo.InvoiceWorkTable
(
    ...
)
SELECT 
    ...
FROM dbo.InvoiceTable_Staging


--Update records where the unique key matches and the HashVals don't match
UPDATE psd
SET 
    psd.* = iw.*
FROM dbo.ProductionSalesData psd
INNER JOIN dbo.InvoiceWorkTable iw 
    ON psd.DataSourceCode = iw.DataSourceCode
        AND psd.InvoiceNumber = iw.InvoiceNum
        AND psd.ItemNumber = iw.ItemNumber
        AND psd.LineNumber = iw.LineNumber
        AND psd.IsOpen = iw.IsOpen
WHERE iw.DataSourceCode = 'ABC'
AND iw.HashVal <> iw.HashVal


--Insert new records (i.e. those with unique keys not found in 
INSERT INTO dbo.ProductionSalesData
(
    ...
)
SELECT
    iw.*
FROM dbo.InvoiceWorkTable iw
LEFT JOIN dbo.ProductionSalesData psd 
    ON psd.DataSourceCode = iw.DataSourceCode
        AND psd.InvoiceNumber = iw.InvoiceNum
        AND psd.ItemNumber = iw.ItemNumber
        AND psd.LineNumber = iw.LineNumber
        AND psd.IsOpen = iw.IsOpen
WHERE iw.DataSourceCode = 'ECL'
    AND psd.InvoiceNumber IS NULL

Я ничего не удаляю здесь, потому что это выполняется заданием очистки на конец месяца, которое выполняется отдельно. Я просто выполняю UPDATE для сопоставленных, но измененных записей, INSERT для новых, и все неизменное остается в одиночестве.

Отфильтровывая данные Oracle и используя хешированное кодирование записи для сравнения вместо сравнения каждого столбца, я избегаю очень требовательного к памяти варианта использования Lookup с сотнями миллионов значений. И при этом я не должен пытаться передавать эти идентификаторы между серверами (и платформами!).

Я буду sh Я мог бы сказать, что это был конец этого конкретного рабочего процесса, но это не так. Это очень ресурсоемкий процесс, и мы не можем позволить его запустить на нашем рабочем OLTP-сервере. Таким образом, мы запускаем все наши процессы ETL на совершенно другом сервере. Но нам также нужно, чтобы данные из Oracle были интегрированы в OLTP-сервер, как только они станут доступны. Но какой смысл в быстрой передаче сотен миллионов записей между Oracle и сервером ETL, если для переноса данных на другой сервер SQL все еще требуются часы? Выполнение «kill-and-fill» не сработает, потому что вставка занимает слишком много времени, и приложение слишком часто запрашивает данные. Я могу передавать данные между серверами, но получение новых данных в рабочую таблицу на OLTP-сервере не работает должным образом.

Так что после многих часов проб и ошибок я нашел учебное пособие о том, как используйте ALTER TABLE с SWITCH TO. Я не говорю, что это идеальное решение в каждом случае, так как есть соображения безопасности / разрешений, а также некоторые другие возможные предостережения. Если вам интересно, вам определенно следует прочитать больше из лучших источников, но в основном вы должны убедиться, что ваши исходные и конечные таблицы имеют одинаковое количество столбцов с одинаковыми именами, а также эквивалентные индексы. Наша таблица ProductionSalesData может иметь разные индексы между разными прогонами, поэтому мне пришлось создать этот pro c для динамического удаления и повторного создания индексов.

CREATE  PROCEDURE [dbo].[proc_SwitchOutTables]
AS


DECLARE @CreateStatement NVARCHAR(MAX)
        ,@DropStatement NVARCHAR(MAX)


--this table will hold the current production data as a precaution
TRUNCATE TABLE dbo.ProductionSalesData_Old


--drop indexes on ProductionSalesData_Old
BEGIN

    SELECT
    DropStatement = 'DROP INDEX ' + QUOTENAME(i.name) + ' ON ' + QUOTENAME(t.name)
    INTO #Drops
    FROM sys.tables AS t
    INNER JOIN sys.indexes AS i ON t.object_id = i.object_id
    LEFT JOIN sys.dm_db_index_usage_stats AS u ON i.object_id = u.object_id AND i.index_id = u.index_id
    WHERE t.is_ms_shipped = 0
        AND i.type <> 0
        AND t.name = 'ProductionSalesData_Old'
    ORDER BY QUOTENAME(t.name), is_primary_key DESC


    IF EXISTS (SELECT TOP 1 1 FROM #Drops)
    BEGIN   
        DECLARE drop_cursor CURSOR FOR
        SELECT DropStatement FROM #Drops


        OPEN drop_cursor
        FETCH NEXT FROM drop_cursor INTO @DropStatement
        WHILE @@FETCH_STATUS = 0
        BEGIN 

            EXEC(@DropStatement)

        FETCH NEXT FROM drop_cursor INTO @DropStatement
        END
        --end loop

        --clean up
        CLOSE drop_cursor
        DEALLOCATE drop_cursor

    END


    DROP TABLE #Drops
END



--recreate indexes on ProductionSalesData_Old based on indexes on ProductionSalesData
BEGIN
    SELECT
    CreateStatement = 
        'CREATE ' 
        + CASE WHEN i.type_desc = 'CLUSTERED' THEN 'CLUSTERED' 
                WHEN i.type_desc = 'NONCLUSTERED' AND is_unique=1 THEN 'UNIQUE NONCLUSTERED'
                WHEN i.type_desc = 'NONCLUSTERED' AND is_unique=0 THEN 'NONCLUSTERED'    
            END  
        + ' INDEX ' 
        + QUOTENAME(i.name) 
        + ' ON ' 
        + QUOTENAME(t.name+'_Old')
        + ' ( '  
        + STUFF(REPLACE(REPLACE((
                SELECT QUOTENAME(c.name) + CASE WHEN ic.is_descending_key = 1 THEN ' DESC' ELSE '' END AS [data()]
                FROM sys.index_columns AS ic
                INNER JOIN sys.columns AS c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
                WHERE ic.object_id = i.object_id AND ic.index_id = i.index_id AND ic.is_included_column = 0
                ORDER BY ic.key_ordinal
                FOR XML PATH
            ), '<row>', ', '), '</row>', ''), 1, 2, '') + ' ) '  -- keycols
        + COALESCE(' INCLUDE ( ' +
            STUFF(REPLACE(REPLACE((
                SELECT QUOTENAME(c.name) AS [data()]
                FROM sys.index_columns AS ic
                INNER JOIN sys.columns AS c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
                WHERE ic.object_id = i.object_id AND ic.index_id = i.index_id AND ic.is_included_column = 1
                ORDER BY ic.index_column_id
                FOR XML PATH
            ), '<row>', ', '), '</row>', ''), 1, 2, '') + ' ) ',    -- included cols
            '')

        + COALESCE(' WHERE ' +
            STUFF(REPLACE(REPLACE((
                --SELECT QUOTENAME(c.name) AS [data()]
                SELECT ic.filter_definition AS [data()]
                FROM sys.indexes AS ic
                WHERE ic.index_id = i.index_id 
                    AND ic.object_id = i.object_id
                    AND ic.has_filter = 1
                ORDER BY ic.index_id
                FOR XML PATH
            ), '<row>', ', '), '</row>', ''), 1, 2, ''),    -- filter
            '')
    INTO #Creates
    FROM sys.tables AS t
    INNER JOIN sys.indexes AS i ON t.object_id = i.object_id
    LEFT JOIN sys.dm_db_index_usage_stats AS u ON i.object_id = u.object_id AND i.index_id = u.index_id
    WHERE t.is_ms_shipped = 0
        AND i.type <> 0
        AND t.name = 'ProductionSalesData'
    ORDER BY QUOTENAME(t.name)
        ,is_primary_key DESC


    IF EXISTS (SELECT TOP 1 1 FROM #Creates)
    BEGIN

        DECLARE create_cursor CURSOR FOR
        SELECT CreateStatement FROM #Creates

        OPEN create_cursor
        FETCH NEXT FROM create_cursor INTO @CreateStatement
        WHILE @@FETCH_STATUS = 0
        BEGIN 
            --PRINT @CreateStatement
            EXEC(@CreateStatement)

        FETCH NEXT FROM create_cursor INTO @CreateStatement
        END
        --end loop

        --clean up
        CLOSE create_cursor
        DEALLOCATE create_cursor

    END


    DROP TABLE #Creates
END
--proc continues below

Последний бит про c ниже делает фактический SWITCH TO. Мы используем WITH ( WAIT_AT_LOW_PRIORITY(), чтобы дождаться, пока не поступит ожидающих запросов к нашей производственной таблице. Как только свободный участок очистится, текущая производственная таблица становится версией _Old, а версия _Staging, которую мы перенесли с сервера ETL, становится новой рабочей таблицей. SWITCH TO происходит в течение нескольких миллисекунд. Все, что делает сервер SQL, обновляет метаданные таблицы, в частности адрес памяти первого листа таблицы.

BEGIN TRAN
    ALTER TABLE dbo.ProductionSalesData 
    SWITCH TO dbo.ProductionSalesData_Old
        WITH ( WAIT_AT_LOW_PRIORITY ( MAX_DURATION = 1 MINUTES, ABORT_AFTER_WAIT = BLOCKERS ));  

    --Anyone who tries to query the table after the switch has happened and before
    --the transaction commits will be blocked: we've got a schema mod lock on the table
    ALTER TABLE dbo.ProductionSalesData_Staging 
    SWITCH TO dbo.ProductionSalesData;
COMMIT

GO
0 голосов
/ 20 февраля 2020

1.Первое полное хранение данных в промежуточной таблице из вашей мульти-таблицы.

2.Выберите идентификатор из sql_server_table Data Store в файл кэша с помощью преобразования кэша.

3. Теперь в новый поток данных использует промежуточную таблицу в качестве исходной таблицы и сравнивает данные с файлом кэша, используя поиск для получения дополнительной информации
https://mask-me.net/datamaskingwiki/wiki/169/ssis-help-cache-and-lookup-transform

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...