이 명시 적 캐스트가 연결된 서버에서만 문제를 일으키는 이유는 무엇입니까?


21

원본 서버의보기를 통해 연결된 서버에서 데이터를 쿼리하고 있습니다. 뷰는 다음과 같은 표준화 된 열 몇을 포함하는 Created, Modified그리고 Deleted있지만,이 경우 원본 서버의 테이블은 적절한 정보가 없습니다. 따라서 열은 해당 유형으로 명시 적으로 캐스트됩니다. 열을 변경하여보기를 업데이트했습니다.

NULL AS Modified

CAST(NULL as DateTime) as Modified

그러나이 업데이트를 수행 한 후보기에서 다음 오류 메시지가 표시됩니다.

메시지 7341, 수준 16, 상태 2, 줄 3 연결된 서버 ""에 대한 OLE DB 공급자 "SQLNCLI11"에서 열 "(사용자 생성 식) .Expr1002"의 현재 행 값을 가져올 수 없습니다.

우리는 걱정없이 원래 서버에서이 "명시 적 캐스트"변경을 일반적으로 수행했으며 문제가 관련된 서버 버전과 관련이있을 것으로 생각합니다. 이 캐스트를 실제로 적용 할 필요 는 없지만 깨끗하게 느껴집니다. 지금은 왜 이런 일이 일어나는지 궁금합니다.

서버 버전 (원본) :

Microsoft SQL Server 2012-11.0.5058.0 (X64) 2014 년 5 월 14 일 18:34:29 저작권 (c) Windows NT 6.1 (빌드 7601 : 서비스 팩 1)의 Microsoft Corporation Enterprise Edition (64 비트) (하이퍼 바이저)

서버 버전 (연결됨) :

Microsoft SQL Server 2008 R2 (SP1)-10.50.2500.0 (X64) 2011 년 6 월 17 일 00:54:03 저작권 (c) Windows NT 6.1 (빌드 7601 : 서비스 팩 1)의 Microsoft Corporation Enterprise Edition (64 비트) (하이퍼 바이저 )

편집
방금 문제의 모든 열을 게시하지 않으면 서 실수를했다는 사실을 깨달았으며 중요한 세부 정보를 남기지 않은 것에 대해 사과해야합니다. 나는 이것을 더 빨리 알지 못했다. 그래도 문제는 여전히 남아 있습니다.

잘못된 캐스트는 DateTime으로 캐스트 할 때 발생하지 않지만 열이 UniqueIdentifier로 캐스트 될 때 발생합니다.

이것은 범인입니다.

CAST(NULL AS UniqueIdentifier) AS [GUID]

UniqueIdentifiers는 SQL Server 2008 R2에서 지원되며 주석에서 언급 한 것처럼 뷰에서 수행 된 쿼리는 연결된 서버에서 제대로 실행됩니다.


각 서버마다 서로 다른 ANSI NULL 설정이 있습니까? 다른 데이터 정렬?
랜돌프 웨스트 1

두 서버 모두 ANSI NULL = 0입니다. 원본 서버에는 데이터 정렬이 Danish_Norwegian_CI_AS있고 연결된 서버에는 데이터 정렬 SQL_Danish_Pref_CP1_CI_AS이 있지만이 COLLATE절은 DateTime열에 적용 할 수 없으므로 더 이상 얻지 못했습니다!
크리 시타

select Null from ...WITH 또는 중첩 쿼리와 CAST다른 쿼리 가 있는 경우 실패 합니까?
Stoleg

명시 적 캐스트가 없으면 INT데이터 유형을 변경 한 것으로 간주됩니다 . 그래도 그 오류 메시지가 나타나는 이유를 모르겠습니다.
Martin Smith

이전에 CTE에서 선택한 값을 실제 값으로 래핑하려고 시도한 다음 값을 선택하고 CTE 다음의 명령문에서 캐스트 된 NULL을 운없이 고정시킵니다. CTE에 NULL을 유지하고 CTE를 쿼리하는 문에서 캐스팅을 제안했지만 동일한 오류가 발생합니다.
krystah

답변:


13

따라서 CAST원격 인스턴스가 아닌 로컬에서 수행되고 있음을 깨닫고 오류를 재현 할 수있었습니다 . 이전에이 문제를 해결하기 위해 SP3로 업그레이드하는 것이 좋습니다 (부분적으로 SP3에서 오류를 재현 할 수없고 부분적으로는 좋은 아이디어이기 때문에). 그러나 이제 오류를 재현 할 수 있으므로 SP3로 이동하면 여전히 좋은 생각이지만이 문제를 해결하지 못할 것입니다. 또한 SQL Server 2008 R2 RTM 및 2014 SP1에서 오류를 재현했습니다 (세 가지 경우 모두 "루프백"로컬 연결 서버 사용).

이 문제와 함께 할 것 같다 경우 쿼리가 실행 또는 적어도 어디 부분 (들) 이 실행된다의. 나는 CAST작업을 수행 할 수 있었지만 로컬 DB 객체에 대한 참조를 포함 시켜야만하기 때문에 이것을 말합니다 .

SELECT rmt.*, CAST(NULL AS UNIQUEIDENTIFIER) AS [GUID]
FROM [Local].[database_name].[dbo].[table_name] rmt
CROSS JOIN (SELECT TOP (1) 1 FROM [sys].[data_spaces]) tmp(dummy);

실제로 작동합니다. 그러나 다음은 원래 오류가 발생합니다.

SELECT rmt.*, CAST(NULL AS UNIQUEIDENTIFIER) AS [GUID]
FROM [Local].[database_name].[dbo].[table_name] rmt
CROSS JOIN (VALUES (1)) tmp(dummy);

로컬 참조가 없으면 전체 쿼리가 원격 시스템으로 전달되어 실행될 수 있으며 어떤 이유로 NULLs로 변환 할 수 없거나 OLE DB 드라이버가 잘못 변환 한 UNIQUEIDENTIFIER것으로 추측됩니다 NULL.


내가 수행 한 테스트를 기반으로, 이것은 버그 인 것처럼 보이지만 버그가 SQL Server 또는 SQL Server Native Client / OLEDB 드라이버 내에 있는지 확실하지 않습니다. 그러나, 변환 오류가 OLEDB 드라이버 내에서 발생하는, 그래서 반드시에서 변환 문제가되지 않습니다 INTUNIQUEIDENTIFIER드라이버 때문에 (SQL 서버에서 허용되지 않는 변환) 변환을 (SQL 서버도하지 않습니다 할 SQL 서버를 사용하지 않는 변환 수 INTDATE테스트 중 하나)에 도시 한 바와 같이, 아직 성공적 OLEDB 드라이버 핸들.

나는 세 가지 테스트를 실행했다. 성공한 두 가지에 대해 원격으로 실행중인 쿼리를 보여주는 XML 실행 계획을 살펴 보았습니다. 세 가지 모두에 대해 SQL Profiler를 통해 예외 또는 OLEDB 이벤트를 캡처했습니다.

행사 :

  • 오류 및 경고
    • 주의
    • 예외
    • 실행 경고
    • 사용자 오류 메시지
  • OLEDB
    • 모든
  • TSQL
    • 다음을 제외한 모두 :
      • SQL : StmtRecompile
      • XQuery 정적 유형

열 필터 :

  • 응용 프로그램 이름
    • % Intellisense %와 같지 않음
  • SPID
    • 50 이상

테스트

  • 시험 1

    • CAST(NULL AS UNIQUEIDENTIFIER) 작동합니다

    SELECT TOP (2) CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
                 , (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
    FROM [Local].[TEMPTEST].[sys].[objects] rmt;

    XML 실행 계획의 관련 부분 :

              <DefinedValue>
                <ColumnReference Column="Expr1002" />
                <ScalarOperator ScalarString="NULL">
                  <Const ConstValue="NULL" />
                </ScalarOperator>
              </DefinedValue>
      ...
    <RemoteQuery RemoteSource="Local" RemoteQuery=
     "SELECT 1 FROM &quot;TEMPTEST&quot;.&quot;sys&quot;.&quot;objects&quot; &quot;Tbl1001&quot;"
     />
  • 시험 2

    • CAST(NULL AS UNIQUEIDENTIFIER) 그 실패

    SELECT TOP (2) CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
             --  , (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
    FROM [Local].[TEMPTEST].[sys].[objects] rmt;

    (참고 : 하위 쿼리를 거기에 보관하고 주석 처리 했으므로 XML 추적 파일을 비교할 때 차이가 적습니다.)

  • 시험 3

    • CAST(NULL AS DATE) 작동합니다

    SELECT TOP (2) CAST(NULL AS DATE) AS [Something]
             --  , (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
    FROM [Local].[TEMPTEST].[sys].[objects] rmt;

    (참고 : 하위 쿼리를 거기에 보관하고 주석 처리 했으므로 XML 추적 파일을 비교할 때 차이가 적습니다.)

    XML 실행 계획의 관련 부분 :

              <DefinedValue>
                <ColumnReference Column="Expr1002" />
                <ScalarOperator ScalarString="[Expr1002]">
                  <Identifier>
                    <ColumnReference Column="Expr1002" />
                  </Identifier>
                </ScalarOperator>
              </DefinedValue>
     ...
    <RemoteQuery RemoteSource="Local" RemoteQuery=
     "SELECT TOP (2) NULL &quot;Expr1002&quot; FROM &quot;TEMPTEST&quot;.&quot;sys&quot;.&quot;objects&quot; &quot;Tbl1001&quot;" 
     />

테스트 # 3을 보면 SELECT TOP (2) NULL, "원격"시스템에서 작동하는 것입니다. SQL 프로파일 러 추적은이 원격 필드의 데이터 유형이 실제로 있음을 보여줍니다 INT. 추적은 또한 클라이언트 측의 필드 (예 : 쿼리를 실행하는 위치)가 DATE예상대로 있음을 보여줍니다. SQL Server에서 오류가 발생하는 에서 INT로 변환 DATE하는 것은 OLEDB 드라이버 내에서 잘 작동합니다. 원격 값은 NULL이므로 직접 반환되므로 <ColumnReference Column="Expr1002" />.

테스트 # 1을 보면 SELECT 1, "원격"시스템에서 수행됩니다. SQL 프로파일 러 추적은이 원격 필드의 데이터 유형이 실제로 있음을 보여줍니다 INT. 추적은 또한 클라이언트 측의 필드 (예 : 쿼리를 실행하는 위치)가 GUID예상대로 있음을 보여줍니다. 에서 변환 INT하려면 GUIDOLEDB를 드라이버 내에서 잘 작동, SQL 서버에 오류가 발생합니다 뭔가를 (이 드라이버 내에서 수행되며, OLEDB는 "GUID"이라고 부른다, 기억). 원격 값이 없다 NULL 가 리터럴로 대체되므로 NULL, 이에 따라이 <Const ConstValue="NULL" />.

테스트 # 2는 실패하므로 실행 계획이 없습니다. 그러나 "원격"시스템을 성공적으로 쿼리하지만 결과 집합을 전달할 수는 없습니다. SQL 프로파일 러가 캡처 한 쿼리는 다음과 같습니다.

SELECT TOP (2) NULL "Expr1002" FROM "TEMPTEST"."sys"."objects" "Tbl1001"

그것은 테스트 # 1에서 수행되는 것과 똑같은 쿼리이지만 여전히 실패합니다. 다른 사소한 차이가 있지만 OLEDB 통신을 완전히 해석 할 수는 없습니다. 그러나 원격 필드는 여전히 INT(wType = 3 = adInteger / 4 바이트 부호있는 정수 / DBTYPE_I4)로 표시되고 "client"필드는 여전히 GUID(wType = 72 = adGUID / 전역 고유 식별자 / DBTYPE_GUID)로 표시됩니다. OLE DB 설명서는 GUID 데이터 형식 변환 , DBDATE 데이터 형식 변환I4 데이터 형식 변환이 I4 에서 GUID 또는 DBDATE 로 변환하는 것이 지원되지 않지만 DATE쿼리는 작동 하는 것으로 나타 났으므로 도움 이되지 않습니다 .

세 가지 테스트에 대한 Trace XML 파일은 PasteBin에 있습니다. 각 테스트와 다른 테스트의 차이점에 대한 세부 정보를 보려면 로컬로 저장 한 다음 "diff"를 수행하십시오. 파일은 다음과 같습니다

  1. NullGuidSuccess.xml
  2. NullGuidError.xml
  3. NullDateSuccess.xml

어고?

어떻게해야합니까? SQL Native Client-- SQLNCLI11는 SQL Server 2012에서 더 이상 사용되지 않는다는 점을 감안할 때 맨 위 섹션에서 언급 한 해결 방법 일 것 입니다. SQL Server Native Client 주제에 대한 대부분의 MSDN 페이지에는 상단:

경고

SNAC (SQL Server Native Client)는 SQL Server 2012 이상에서 지원되지 않습니다. 새로운 개발 작업에서 SNAC를 사용하지 말고 현재이를 사용하는 응용 프로그램을 수정하십시오. SQL Server 용 Microsoft ODBC 드라이버는 마이크로 소프트 SQL 서버와 마이크로 소프트 애저 SQL 데이터베이스에 윈도우에서 기본 연결을 제공합니다.

자세한 내용은 다음을 참조하십시오.


ODBC ??

다음을 통해 ODBC 연결된 서버를 설정했습니다.

EXEC master.dbo.sp_addlinkedserver
  @server = N'LocalODBC',
  @srvproduct=N'{my_server_name}',
  @provider=N'MSDASQL',
  @provstr=N'Driver={SQL Server};Server=(local);Trusted_Connection=Yes;';

EXEC master.dbo.sp_addlinkedsrvlogin
  @rmtsrvname=N'LocalODBC',
  @useself=N'True',
  @locallogin=NULL,
  @rmtuser=NULL,
  @rmtpassword=NULL;

그런 다음 시도했습니다.

SELECT CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
FROM [LocalODBC].[tempdb].[sys].[objects] rmt;

다음과 같은 오류가 발생했습니다.

연결된 서버 "LocalODBC"에 대한 OLE DB 공급자 "MSDASQL"이 "요청 된 변환이 지원되지 않습니다."라는 메시지를 반환했습니다.
메시지 7341, 수준 16, 상태 2, 줄 53 줄
"(사용자 생성 식) .Expr1002"의 현재 행 값을 연결된 서버 "LocalODBC"에 대한 OLE DB 공급자 "MSDASQL"에서 가져올 수 없습니다.


추신

원격 서버와 로컬 서버 간의 GUID 전송과 관련하여 NULL이 아닌 값은 특수 구문을 통해 처리됩니다. 실행했을 때 SQL 프로파일 러 추적에서 다음 OLE DB 이벤트 정보를 발견했습니다 CAST(0x00 AS UNIQUEIDENTIFIER).

<RemoteQuery RemoteSource="Local" RemoteQuery=
 "SELECT {guid'00000000-0000-0000-0000-000000000000'} &quot;Expr1002&quot; FROM &quot;TEMPTEST&quot;.&quot;sys&quot;.&quot;objects&quot; &quot;Tbl1001&quot;" 
 />

PPS

또한 OPENQUERY다음 쿼리 를 통해 테스트했습니다 .

SELECT TOP (2) CAST(NULL AS UNIQUEIDENTIFIER) AS [Something]
     --, (SELECT COUNT(*) FROM sys.[data_spaces]) AS [lcl]
FROM   OPENQUERY([Local], N'SELECT 705 AS [dummy] FROM [TEMPTEST].[sys].[objects];') rmt;

로컬 객체 참조가 없어도 성공했습니다. SQL 프로파일 러 추적 XML 파일은 다음 위치에 PasteBin에 게시되었습니다.

NullGuidSuccessOPENQUERY.xml

XML 실행 계획은 NULL테스트 # 1과 같은 상수를 사용하여 보여줍니다 .


2012 sp3-cu1에서이 문제를 재현했습니다.
Bob Klimes

@BobKlimes 재미있는. 2008 R2 인스턴스 또는 루프백에 연결된 서버를 사용하고 있습니까?
Solomon Rutzky

원격 연결 서버는 2008 R2 sp2 + MS15-058 (10.50.4339.0)입니다.
Bob Klimes

1
버전과 관련이없는 것 같습니다. 2008r2,2012,2014,2016의 여러 콤보를 테스트했으며 지금까지 2008r2-2008r2까지 오류가 발생했습니다.
Bob Klimes

2
너무 모호한 문제입니다. @srutzky에 대한 통찰력과 연구에 감사드립니다. 대단히 감사합니다. 향후 작업에 대해 SNAC의 선언을 명심하고 위에서 언급 한 해결 방법으로 넘어갑니다. 훌륭한 일!
krystah

4

추악한 해결 방법 만 있습니다 . '1900-01-01'대신 날짜 상수를 사용하십시오 null.

CAST('1900-01-01' as DateTime) as Modified

가져온 후에는 열을 1900-01-01다시 Null로 업데이트 할 수 있습니다 .

이것은 here에 따른 일종의 SQL 2012 기능 / 버그 입니다.

편집 : 아래 @a_horse_with_no_name 주석에 따라 1900-00-00유효한 날짜 1900-01-01로 대체되었습니다 .


이 해결 방법은 언급 할 가치가 있었지만 이제 OP가 문제의 원인이 uniqueidentifier열 이라는 사실을 분명히 했으므로 더 이상 관련이 없습니다 . 아니면 아마도 어쩌면 적응할 수 CAST('00000000-0000-0000-0000-000000000000' AS UniqueIdentifier) AS [GUID]있습니까?
Andriy M

고마워 빈 GUID 또는 DateTime으로 캐스팅하면 작동하지만 왜 이런 일이 발생하는지 이해해야합니다. 아무것도 가져 오지 않고 소스 데이터를 변경할 가능성이 없다는 점도 언급 할 가치가 있습니다.
크리 시타

1
1900-00-00날짜가 잘못되어 허용되지 않습니다.
a_horse_with_no_name

@a_horse_with_no_name : 바보 같은 실수가 수정되었습니다.
Anton Krouglov

2

이 문제는 데이터 유형 변환과 관련이 있습니다 (주석에서 적중 한대로).

다음을 고려하세요:

SELECT NULL as NullColumn INTO SomeTable;
EXEC sp_help SomeTable;
DROP TABLE SomeTable;

(가) 있습니다 NullColumn유형입니다 int. SQL Server는 int값을 로 변환하는 것을 좋아하지 않습니다 uniqueidentifier. 이 SELECT명령문은 데이터 유형 변환에서 실패합니다.

--Just a SELECT from nothing
SELECT CAST(CAST(NULL as int) as uniqueidentifier);
--
--or to see it from a physical table:
SELECT NULL as NullColumn INTO SomeTable;
SELECT CAST(NullColumn as uniqueidentifier) FROM SomeTable;
DROP TABLE SomeTable;

메시지 529, 수준 16, 상태 2, 줄 3

데이터 유형 int에서 uniqueidentifier 로의 명시 적 변환은 허용되지 않습니다.

이 특정 값 (NULL)을 GUID로 캐스트 할 수 있지만 SQL Server는 특정 값을보기 전에 데이터 형식 변환에 따라 오류를 발생시킵니다. 대신, 당신은 여러 단계로 수행해야합니다 CAST변화를 암시 가서 작업 int으로 깨끗하게 변환 할 수있는 데이터 형식에 uniqueidentifer먼저 캐스팅을 실현합니다 수단 varchar다음에 uniqueidentifier:

--Just a SELECT from nothing
SELECT CAST(CAST(CAST(NULL as int) as varchar) as uniqueidentifier);
--
--or to see it from a physical table:
SELECT NULL as NullColumn INTO SomeTable;
SELECT CAST(CAST(NullColumn as varchar(32)) as uniqueidentifier) FROM SomeTable;
DROP TABLE SomeTable;

이 문제는 실제로는 SQL Server 내부가 아닌 데이터 형식 변환으로 인한 것이 아닙니다. 세부 사항은 내 대답 에 있지만 SQL Server가 아닌 OLEDB 드라이버 내에서 변환이 수행되고 있으며 변환 규칙이 동일하지 않습니다.
Solomon Rutzky

1

OP는 궁극적으로 이것이 적절한 답변인지 결정할 수 있습니다.

나는 '절대적인'증거를 가지고 있지는 않지만, 문제는 UniqueIdentifer가 서버에 의존적이며 아마도 공급자가이 고유 식별자를 가져 오기 위해 어느 서버 (로컬 또는 원격)가이 고유 식별자를 가져 오는지를 알아내는 데 어려움이 있다는 사실에서 비롯됩니다. 없는. 따라서이 시나리오에서는 다른 데이터 유형을 성공적으로 캐스팅 할 수 있지만 uniqueidentifier는 불가능합니다. UNIQUEIDENTIFIERS 및 DATETIMEOFFSET과 같이 '서버'에 의존하는 데이터 유형은 발생하는 오류를 제공합니다.

4- 파트 이름 대신 OPENQUERY를 사용하면 작동합니다.

set nocount on  
DECLARE @cmd nVARCHAR(max)
DECLARE @datatype SYSNAME

DECLARE _CURSOR CURSOR LOCAL FORWARD_ONLY STATIC READ_ONLY
FOR
SELECT NAME
FROM sys.types 

OPEN _CURSOR

FETCH NEXT
FROM _CURSOR
INTO @datatype

WHILE @@FETCH_STATUS = 0
BEGIN
    BEGIN TRY
        SET @cmd = 'select top 1 cast(null as ' + @Datatype + ') as CastedData from remoteserver.remotedatabase.remoteschema.remotetable'
        PRINT @cmd
        EXECUTE sp_executesql @cmd
    END TRY

    BEGIN CATCH
        PRINT Error_message()
    END CATCH

FETCH NEXT
FROM _CURSOR
INTO @datatype
END --End While

CLOSE _CURSOR

DEALLOCATE _CURSOR

당신은에 의해 정확히 무엇을 의미합니까 UniqueidentifierDateTimeOffset서버 - 의존? 이에 대한 소스가 있습니까?
krystah

@krystah-이 링크에서 ( technet.microsoft.com/en-us/library/ms190215(v=sql.105).aspx ) "uniqueidentifier 데이터 형식은 GUID (Globally Unique Identifier)로 작동하는 16 바이트 이진 값을 저장합니다. GUID는 고유 한 이진수이며, 다른 컴퓨터에서는 해당 GUID 값의 복제본을 생성하지 않으며 GUID의 주된 용도는 많은 사이트에 많은 컴퓨터가있는 네트워크에서 고유해야하는 식별자를 할당하는 것입니다. "
Scott Hodgin

DATETIMEOFFSET (들어 msdn.microsoft.com/en-us/library/bb630289.aspx ) 시간대 인식을 가지고 있으며, 24 시간을 기준으로 하루의 시간과 결합 된 날짜를 정의
스콧 Hodgin

나는이 두 가지 데이터 유형이 시나리오에서 오류를주는 유일한 유형으로 간주되고 해당 데이터 유형이 '서버에 따라'인 것이기 때문에 '추론 적'입니다. 이것이 문제의 원인입니다. OPENQUERY를 사용하여보기에서 결과를 검색하고 추가 널 캐스트를 추가하는 경우 제공자가 해당 정보를 얻을 수있는 '위치'를 알고 있기 때문에 작동해야합니다.
Scott Hodgin

@krystah와 Scott :이 두 가지 데이터 유형은 서버에 의존하지 않으며 적어도 설명하고 암시하는 방식이 아닙니다. GUID는 기본 이진 표현 (자세한 내용은 여기 참조) 측면에서 "아키텍처" 이지만 GUID가 문제인 경우 GUID가 제대로 전송되지 않습니다. DATETIMEOFFSET의 경우 실제 시간대 / TimeZoneID가 아니라 오프셋 만 알고 있습니다. 둘 다 시스템 정보를 사용하여 새 값을 생성하지만 여기서는 새 값이 생성되지 않으며 값이 있으면 제공자에서 생성되지 않습니다.
Solomon Rutzky

0

해결 방법 : 허용 대답은 OLEDB를 드라이버를 지원하지 않기 때문에 변환에 필요한 로컬 일어날 것을 나타내는 것 같다.

따라서 간단한 해결 방법 (적어도 uniqueidentifier재귀 적 CTE의 기본 경우 null 을 선택하는 쿼리의 경우 )은 null 변수를 선언하는 것입니다.

declare @nullGuid as uniqueidentifier = null;

--Instead of...
CAST(NULL AS UniqueIdentifier) AS [GUID]

--use
@nullGuid AS [GUID]
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.