본문 바로가기
프로그래밍/TSQL

[MSSQL]ID 기준 데이터 복사 구문 동적 쿼리 작성 공유

by 정리 습관(★arranging★) 2023. 6. 8.
728x90

1200px-Microsoft_SQL_Server_Logo.svg.png
132.9 kB

 

/

MSSQL DB에서 특정 데이터를 복사 해야하는 경우가 있습니다.
예를 들어 
COL1,COL2,COL3 
의 데이터를 가진 테이블에 PK가 COL1인 경우 에

INSERT INTO [TARGET_TBL] (COL1,COL2, COL3)
SELECT [NEW_COL1],COL2, COL3
FROM [TARGET_TBL]
WHERE COL1 = [OLD_COL1]

위와 같은 동작을 수행하는 부분입니다.

 동작을 DB 테이블을 조회해서 참조키 해제, 사용중인 트리거 해제  데이터 처리 
이전 설정을 복원하는 구문을 공유 드립니다.

 

USE [TARGET DB INSTANCE]

 

SET NOCOUNT ON;

IF OBJECT_ID ('TEMPDB..#TARGET') IS NOT NULL

             DROP TABLE #TARGET

 

SELECT * INTO #TARGET

FROM (SELECT *,ROW_NUMBER() OVER (PARTITION BY [ID_NEW] ORDER BY [ID_OLD]) RN FROM [대상ID 저장 테이블]) A

WHERE A.RN = 1

 

--■ 1. 외래키 제약 조건 비활성화

EXEC sp_msforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT all'

 

--■ 2. OLD_ID 데이터 기준 NEW_ID  INSERT ID 구문 수행

BEGIN

             DECLARE @TableName NVARCHAR(500);

             DECLARE @ColumnName NVARCHAR(200);

             DECLARE @SelectColumns NVARCHAR(MAX);

             DECLARE @SelectColumns_NEW NVARCHAR(MAX);

             DECLARE @InsertColumns NVARCHAR(MAX);

             DECLARE @SQLQuery NVARCHAR(MAX);

             DECLARE @ID_OLD VARCHAR(100)

             DECLARE @ID_NEW VARCHAR(100)

             DECLARE @TRI_TBL TABLE (TRI_NAME NVARCHAR(300)) --트리거 비활성화,활성화를 위한 테이블

             DECLARE @TriggerName NVARCHAR(300)

 

             DECLARE tableCursor CURSOR FOR

             SELECT DISTINCT A.NAME

             FROM SYSOBJECTS A JOIN SYS.all_columns B ON A.id = B.object_id

             WHERE A.TYPE = 'U' AND (B.NAME = 'ID')

 

             OPEN tableCursor

             FETCH NEXT FROM tableCursor INTO @TableName

             WHILE @@FETCH_STATUS = 0

             BEGIN

                           DELETE @TRI_TBL

                           --사용중인 트리거 비활성화

                           INSERT INTO @TRI_TBL SELECT name FROM sys.triggers WHERE parent_id = OBJECT_ID(@TableName) and is_disabled = 0                                  

                           DECLARE trigger_cursor CURSOR FOR SELECT TRI_NAME FROM @TRI_TBL

                           OPEN trigger_cursor

                           FETCH NEXT FROM trigger_cursor INTO @TriggerName

                           WHILE @@FETCH_STATUS = 0

                           BEGIN

                                        EXEC('DISABLE TRIGGER ' + @TriggerName + ' ON '+@TableName+';')

                                        FETCH NEXT FROM trigger_cursor INTO @TriggerName

                           END    

                           CLOSE trigger_cursor

                          

                           --컬럼 처리

                           BEGIN

                                        -- cursor를 사용하여 인스턴스의 모든 테이블에 대해 반복합니다.

                                        DECLARE columnCursor CURSOR FOR

                                        SELECT  c.COLUMN_NAME

                                        FROM INFORMATION_SCHEMA.COLUMNS c

                                        INNER JOIN INFORMATION_SCHEMA.TABLES t ON c.TABLE_NAME = t.TABLE_NAME

                                        WHERE t.TABLE_TYPE = 'BASE TABLE' AND (t.TABLE_NAME = @TableName);

 

                                        -- cursor를 열고 반복을 시작합니다.

                                        OPEN columnCursor;

                                        FETCH NEXT FROM columnCursor INTO @ColumnName;

 

                                        -- SELECT 및 INSERT에 사용할 컬럼 목록을 초기화합니다.

                                        SET @SelectColumns = '';

                                        SET @InsertColumns = '';

 

                                        WHILE @@FETCH_STATUS = 0

                                        BEGIN

                                                     -- 컬럼 목록에 현재 컬럼을 추가합니다.

                                                     SET @SelectColumns += QUOTENAME(@ColumnName) + N', ';

                                                     SET @InsertColumns += QUOTENAME(@ColumnName) + N', ';                               

 

                                                     -- 다음 컬럼으로 이동합니다.

                                                     FETCH NEXT FROM columnCursor INTO @ColumnName;

                                        END

 

                                        -- 마지막 쉼표를 제거합니다.

                                        SET @SelectColumns = LEFT(@SelectColumns, LEN(@SelectColumns) - 1);

                                        SET @InsertColumns = LEFT(@InsertColumns, LEN(@InsertColumns) - 1);                          

 

                                        CLOSE columnCursor;

                                        DEALLOCATE columnCursor;

                           END

 

                           -- 구문 수행

                           BEGIN

                                        DECLARE @DynamicSQL NVARCHAR(MAX) = '';

                                        DECLARE dataCursor CURSOR FOR

                                        SELECT  CONVERT(VARCHAR(100),[ID_NEW]), CONVERT(VARCHAR(100),[ID_OLD]) FROM #TARGET

 

                                        OPEN dataCursor

                                        FETCH NEXT FROM dataCursor INTO @ID_NEW, @ID_OLD

                                        WHILE @@FETCH_STATUS = 0

                                        BEGIN

                                                     SET @SelectColumns_NEW = REPLACE(@SelectColumns,'[ID]',@ID_NEW)

 

                                                     IF EXISTS (

                                                                                SELECT 1

                                                                                FROM INFORMATION_SCHEMA.COLUMNS

                                                                                WHERE TABLE_NAME = @TableName                                                                          

                                                                                             AND COLUMNPROPERTY(object_id(TABLE_SCHEMA + '.' + TABLE_NAME), COLUMN_NAME, 'IsIdentity') = 1

                                                                   )

                                                     BEGIN

                                                                   --IDENTITY_INSERT 혀용 후 SELECT 및 INSERT 쿼리를 동적으로 생성합니다.

                                                                   SET @DynamicSQL =  N'      SET IDENTITY_INSERT '+ @TableName+' ON;

                                                                                                                                                   INSERT INTO ' + QUOTENAME(@TableName) + N' (                                        ' + @InsertColumns + N')

                                                                                                                                                   SELECT ' + @SelectColumns_NEW + N'

                                                                                                                                                   FROM' + QUOTENAME(@TableName) + N'

                                                                                                                                                   WHERE ID = '+@ID_OLD+';

                                                                                                                                                   SET IDENTITY_INSERT '+ @TableName+' OFF;'

                                                     END

                                                     ELSE

                                                     BEGIN

                                                                   -- SELECT 및 INSERT 쿼리를 동적으로 생성합니다.

                                                                   SET @DynamicSQL =  N'      INSERT INTO ' + QUOTENAME(@TableName) + N' (                              ' + @InsertColumns + N')

                                                                                                                                                   SELECT ' + @SelectColumns_NEW + N'

                                                                                                                                                   FROM' + QUOTENAME(@TableName) + N'

                                                                                                                                                   WHERE ID = '+@ID_OLD+';'

                                                     END

                                       

                                                     -- 동적으로 생성된 쿼리를 실행합니다.

                                                     EXEC sp_executesql @DynamicSQL;

             --                                      print @TableName+':'+ @DynamicSQL;

 

                                                     FETCH NEXT FROM dataCursor INTO @ID_NEW, @ID_OLD

                                        END

                                        CLOSE dataCursor;

                                        DEALLOCATE dataCursor;

                           END

 

                           --트리거 재활성화

                           BEGIN

                                        OPEN trigger_cursor

                                        FETCH NEXT FROM trigger_cursor INTO @TriggerName

                                        WHILE @@FETCH_STATUS = 0

                                        BEGIN

                                                     EXEC('ENABLE TRIGGER ' + @TriggerName + ' ON '+@TableName+';')

                                                     FETCH NEXT FROM trigger_cursor INTO @TriggerName

                                        END

                                        CLOSE trigger_cursor

                                        DEALLOCATE trigger_cursor

                           END

 

                           FETCH NEXT FROM tableCursor INTO @TableName

             END

             CLOSE tableCursor;

             DEALLOCATE tableCursor;

END

 

--■ 3. 외래키 제약 조건 활성화

EXEC sp_msforeachtable 'ALTER TABLE ? CHECK CONSTRAINT all'

 

--■ 4. 검수

 

 

MSSQL의 커서 기능과 SP_MSFOREACHTABLE 등을 활용했습니다.
[
대상ID 저장 테이블] 에는 ID_OLD, ID_NEW  데이터가 적재되어 있음을 가정했습니다.
작업 수순은 아래와 같습니다.

1. 전체 테이블 제약조건 해제
2. 
테이블 조회
    2.1. 
사용 중인 테이블 트리거 조회
        2.1.1. 
사용중인 테이블 트리거 해제
        2.1.2. 
테이블 컬럼 리스트 조회
            2.1.2.1. 
테이블 컬럼 리스트 취합
            2.1.2.2. 
테이블 컬럼 리스트와 데이터 컬럼 리스트 문자열 생성
            2.1.3. 
데이터 리스트 조회
                2.1.3.1. 
데이터컬럼 리스트 가공
                2.1.3.2. 
컬럼  IDENTITY 컬럼이 있는지 확인
                2.1.3.2. INSERT INTO ... SELECT 
구문 생성
                2.1.3.3. 
쿼리 수행
        2.2.2. 
해제된 트리거 재활성화
    2.2. 
테이블 순회    
3. 
전체 테이블 제약조건 활성화
4. 
적용데이터 검수

 내용이 모든 상황에 완벽하게 적용되기 어려울  있지만
여러 모로 활용은 가능  것이라 믿습니다.

----------------------------------------

Sometimes you need to copy certain data from MSSQL DB.
for example
If the PK is COL1 in a table with data of COL1, COL2, COL3

INSERT INTO [TARGET_TBL] (COL1,COL2, COL3)
SELECT [NEW_COL1],COL2, COL3
FROM [TARGET_TBL]
WHERE COL1 = [OLD_COL1]

This is the part that does the same thing as above.

This action is performed by querying the table in the DB, releasing the reference key, releasing the trigger in use, and processing the data.
We share the syntax to restore previous settings.


I used MSSQL's cursor function and SP_MSFOREACHTABLE, etc.
It is assumed that the [Target ID storage table] is loaded with ID_OLD and ID_NEW data.
The work flow is as follows.
-----------------------

USE [TARGET DB INSTANCE]

SET NOCOUNT ON;
IF OBJECT_ID ('TEMPDB..#TARGET') IS NOT NULL
DROP TABLE #TARGET

SELECT * INTO #TARGET 
FROM (SELECT *,ROW_NUMBER() OVER (PARTITION BY [ID_NEW] ORDER BY [ID_OLD]) RN FROM [TARGET ID STORED TABLE]) A
WHERE A.RN = 1

--■ 1. Disable Foreign Key Constraints
EXEC sp_msforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT all'

--■ 2. NEW_ID INSERT ID syntax based on OLD_ID data
BEGIN
DECLARE @TableName NVARCHAR(500);
DECLARE @ColumnName NVARCHAR(200);
DECLARE @SelectColumns NVARCHAR(MAX);
DECLARE @SelectColumns_NEW NVARCHAR(MAX);
DECLARE @InsertColumns NVARCHAR(MAX);
DECLARE @SQLQuery NVARCHAR(MAX);
DECLARE @ID_OLD VARCHAR(100)
DECLARE @ID_NEW VARCHAR(100)
DECLARE @TRI_TBL TABLE (TRI_NAME NVARCHAR(300)) --Table for deactivating and activating triggers Table for deactivating and activating triggers
DECLARE @TriggerName NVARCHAR(300)

DECLARE tableCursor CURSOR FOR
SELECT DISTINCT A.NAME
FROM SYSOBJECTS A JOIN SYS.all_columns B ON A.id = B.object_id
WHERE A.TYPE = 'U' AND (B.NAME = 'ID')

OPEN tableCursor
FETCH NEXT FROM tableCursor INTO @TableName
WHILE @@FETCH_STATUS = 0
BEGIN
DELETE @TRI_TBL
--Disable triggers in use
INSERT INTO @TRI_TBL SELECT name FROM sys.triggers WHERE parent_id = OBJECT_ID(@TableName) and is_disabled = 0
DECLARE trigger_cursor CURSOR FOR SELECT TRI_NAME FROM @TRI_TBL
OPEN trigger_cursor
FETCH NEXT FROM trigger_cursor INTO @TriggerName
WHILE @@FETCH_STATUS = 0
BEGIN
EXEC('DISABLE TRIGGER ' + @TriggerName + ' ON '+@TableName+';')
FETCH NEXT FROM trigger_cursor INTO @TriggerName
END
CLOSE trigger_cursor

--column processing
BEGIN
-- Iterates through all tables in the instance using a cursor.
DECLARE columnCursor CURSOR FOR
SELECT  c.COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS c
INNER JOIN INFORMATION_SCHEMA.TABLES t ON c.TABLE_NAME = t.TABLE_NAME
WHERE t.TABLE_TYPE = 'BASE TABLE' AND (t.TABLE_NAME = @TableName);

-- Open the cursor and start iterating.
OPEN columnCursor;
FETCH NEXT FROM columnCursor INTO @ColumnName;

-- Initializes the list of columns to be used for SELECT and INSERT.
SET @SelectColumns = '';
SET @InsertColumns = '';

WHILE @@FETCH_STATUS = 0
BEGIN
-- Adds the current column to the column list.
SET @SelectColumns += QUOTENAME(@ColumnName) + N', ';
SET @InsertColumns += QUOTENAME(@ColumnName) + N', ';

-- Move to the next column.
FETCH NEXT FROM columnCursor INTO @ColumnName;
END

-- Remove the final comma.
SET @SelectColumns = LEFT(@SelectColumns, LEN(@SelectColumns) - 1);
SET @InsertColumns = LEFT(@InsertColumns, LEN(@InsertColumns) - 1);

CLOSE columnCursor;
DEALLOCATE columnCursor;
END

-- phrase execution
BEGIN
DECLARE @DynamicSQL NVARCHAR(MAX) = '';
DECLARE dataCursor CURSOR FOR
SELECT  CONVERT(VARCHAR(100),[ID_NEW]), CONVERT(VARCHAR(100),[ID_OLD]) FROM #TARGET

OPEN dataCursor 
FETCH NEXT FROM dataCursor INTO @ID_NEW, @ID_OLD
WHILE @@FETCH_STATUS = 0
BEGIN
SET @SelectColumns_NEW = REPLACE(@SelectColumns,'[ID]',@ID_NEW)

IF EXISTS (
SELECT 1
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = @TableName
AND COLUMNPROPERTY(object_id(TABLE_SCHEMA + '.' + TABLE_NAME), COLUMN_NAME, 'IsIdentity') = 1
)
BEGIN
--Dynamically generate SELECT and INSERT queries after accepting IDENTITY_INSERT.
SET @DynamicSQL =  N' SET IDENTITY_INSERT '+ @TableName+' ON;
INSERT INTO ' + QUOTENAME(@TableName) + N' ( ' + @InsertColumns + N')
SELECT ' + @SelectColumns_NEW + N'
FROM' + QUOTENAME(@TableName) + N'
WHERE ID = '+@ID_OLD+';
SET IDENTITY_INSERT '+ @TableName+' OFF;'
END
ELSE
BEGIN
-- Dynamically generate SELECT and INSERT queries.
SET @DynamicSQL =  N' INSERT INTO ' + QUOTENAME(@TableName) + N' ( ' + @InsertColumns + N')
SELECT ' + @SelectColumns_NEW + N'
FROM' + QUOTENAME(@TableName) + N'
WHERE ID = '+@ID_OLD+';'
END

-- Run dynamically generated queries.
EXEC sp_executesql @DynamicSQL;
-- print @TableName+':'+ @DynamicSQL;

FETCH NEXT FROM dataCursor INTO @ID_NEW, @ID_OLD
END
CLOSE dataCursor;
DEALLOCATE dataCursor;
END

--trigger reactivation
BEGIN
OPEN trigger_cursor
FETCH NEXT FROM trigger_cursor INTO @TriggerName
WHILE @@FETCH_STATUS = 0
BEGIN
EXEC('ENABLE TRIGGER ' + @TriggerName + ' ON '+@TableName+';')
FETCH NEXT FROM trigger_cursor INTO @TriggerName
END
CLOSE trigger_cursor
DEALLOCATE trigger_cursor
END

FETCH NEXT FROM tableCursor INTO @TableName
END
CLOSE tableCursor;
DEALLOCATE tableCursor;
END

--■ 3. Enabling Foreign Key Constraints
EXEC sp_msforeachtable 'ALTER TABLE ? CHECK CONSTRAINT all'

--■ 4. Inspection

-----------------------

1. Disable Entire Table Constraints
2. Table lookup
     2.1. Query table triggers in use
         2.1.1. Disable active table triggers
         2.1.2. Query table column list
             2.1.2.1. Collect table column list
             2.1.2.2. Create table column list and data column list string
             2.1.3. Data list search
                 2.1.3.1. Data column list processing
                 2.1.3.2. Check if there is an IDENTITY column among the columns
                 2.1.3.2. Generate an INSERT INTO ... SELECT statement
                 2.1.3.3. query
         2.2.2. Reactivating a released trigger
     2.2. table traversal
3. Enable Full Table Constraints
4. Applied data inspection

Although the above information may not be perfectly applicable in all situations,
I believe that it can be used in many ways.

 

댓글