2017-04-11 8 views
1

나는 마이크로 소프트 SQL 서버 2012에서 SQL CLR 트리거를 사용하여 DB 동기화 엔진을 만드는 오전이 트리거는 저장 프로 시저 또는 함수를 호출 (하여 삽입에 대한 접근 및 삭제 된 의사 테이블이 없습니다 @@ procid에 액세스 할 수는 없습니다. 참조SQL CLR 트리거 - 얻을 소스 테이블

차이 here.

이 "동기화 엔진은"테이블과 필드 맵이 동기화 작업이 무엇인지 결정하기 위해 매핑 테이블을 사용합니다. 대상 테이블과 필드를 (내 매핑 테이블에서) 확인하려면 트리거 자체에서 소스 테이블 이름을 가져와야합니다. Stack Overflow 및 다른 사이트에서이 문제를 해결할 수 없다는 많은 답변을 보았습니다.

잠재적 인 솔루션 :

using (SqlConnection lConnection = new SqlConnection(@"context connection=true")) { 
    SqlCommand cmd = new SqlCommand("SELECT object_name(resource_associated_entity_id) FROM sys.dm_tran_locks WHERE request_session_id = @@spid and resource_type = 'OBJECT'", lConnection); 
    cmd.CommandType = CommandType.Text; 
    var obj = cmd.ExecuteScalar(); 
} 

이 사실 올바른 테이블 이름을 반환하지, 나는 단서를 제공 한 website을 발견했습니다.

질문 :

내 질문이 잠재적 인 솔루션이 어떻게 신뢰할 수 있습니까? @@ spid가 실제로이 단일 트리거 실행으로 제한됩니까? 아니면 다른 동시 트리거가이 프로세스 ID 내에서 중복 될 수 있습니까? 데이터베이스 내에서 동일한 트리거 및/또는 다른 트리거를 여러 번 실행할 수 있습니까? 이 사이트에서

, 그것은 보인다 이드 중복되지 않는 열려있는 연결로 제한 사실상 과정 : here, herehere.

이 내 소스 테이블을 얻을 수있는 안전한 방법이 될 것인가?

왜?

저는 비슷한 질문을 한 것이지만 내 구체적인 상황 (그 중 하나만 제외하고는)에 대한 유효한 대답이 없습니다. 그 이유는 다음과 같습니다.

이 동기화 엔진은 단일 DB에서 작동하며 변경 사항을 대상 테이블에 적용하여 사용자 정의 데이터로 데이터를 변환 할 수 있습니다. 정의 변환 자동 소스 - 타겟 형 변환 및 분석, 심지어 같은 데이터를 변환하기위한 그 맵핑 테이블에 저장된 메소드를 실행하도록에서는 CSharpCodeProvider를 사용할 수있다. 그것은 이미 구축되었으며, 매우 견고하며 우리가하고있는 일에 대한 좋은 성과 척도를 가지고 있습니다. 지금은 1 허용을 구축하기 위해 노력하고있어 : n 및합니다 ('마스터'테이블로 동일한 ID를 필요로 확장 테이블을 포함한) 테이블 변경을 "genericise"코드를하려합니다. 이전에는 각 트리거에 하드 코딩 된 "목표 테이블"정의가 있었고 원본을 결정하기 위해 매핑 테이블을 사용하고있었습니다. 이제 원본 테이블을 가져 와서 매핑 테이블을 사용하여 모든 대상 테이블을 확인하고 싶습니다. 이것은 중간로드 환경에서 사용되며 CRUD 작업을 완료하기 위해 별도의 서버 프로세스가 선택하는 "Change Order Book"의 변경 사항을 푸시합니다.

코멘트에서 언급 한 바와 같이 편집

, 위의 쿼리는 매우 "불확실"입니다. syscolpars 또는 sysidxstats와 같은 시스템 개체를 반환하는 경우가 많습니다 (예 : SQL Server를 다시 시작한 후). 그러나 dm_tran_locks 테이블에는 항상 동일한 object_name을 가진 'RID'(Row ID)와 연관된 resource_type이있는 것으로 보입니다.지금까지 안정적으로 작동 나의 현재 쿼리는 다음과 같다 (만약이 변경을 업데이트하거나 높은 부하 테스트에서 작동하지 않습니다) :

select t1.ObjectName FROM (
    SELECT object_name(resource_associated_entity_id) as ObjectName 
    FROM sys.dm_tran_locks WHERE resource_type = 'OBJECT' and request_session_id = @@spid 
) t1 inner join (
    SELECT OBJECT_NAME(partitions.OBJECT_ID) as ObjectName 
    FROM sys.dm_tran_locks 
    INNER JOIN sys.partitions ON partitions.hobt_id = dm_tran_locks.resource_associated_entity_id 
    WHERE resource_type = 'RID' 
) t2 on t1.ObjectName = t2.ObjectName 

을이 항상 그런 경우, 나는 그것을 발견 할 것이다 테스트 중.

+1

이것은 적합합니다. 엔진이 정확히 하나의 객체, 즉 트리거가 실행하는 테이블에 잠금을 설정한다고 가정합니다. 어쩌면 그렇게 될 수도 있지만 항상 누군가가 당신에게 보증 할 것을 기대할 수는 없습니다. 나는'@@ spid'에 대해별로 염려하지 않을 것입니다. MARS는 둘 이상의 명령문이 연결에서 활성화 될 수있게하지만 이것은 가짜 동시성의 일종입니다. 트리거 실행은 겹치지 않으며 순차적으로 실행됩니다. 방해하는 다른 진술에 대해 걱정할 필요는 없습니다. –

+0

특히'SERIALIZABLE' ('SELECT * FROM T WITH (HOLDLOCK)')에서 실행할 때 트랜잭션을 던질 때 어떤 일이 일어나는지 테스트하십시오. 다른 잠금이 이미있는 트랜잭션에서 트리거가 실행되면 어떻게됩니까? –

+0

하나의 트리거 (또는 아마도 한 세트의 코드)를 하나 이상의 테이블에 대한 트리거로 사용하려고합니다. –

답변

0

이 잠재적 솔루션은 얼마나 신뢰할 수 있습니까?

나는 그것이 작동하지 않는 보여주기 위해 테스트 케이스를 설정하는 시간을 가지고 있지 않지만, 나는 (심지어 편집 섹션에서 계정으로 쿼리를 복용)이 방법을 찾을 수는 "불확실한은"(즉, 은 보장되지 ~ 항상 신뢰할 수 있음).

주요 문제는 다음과 같습니다 계단식

  • 트리거 실행
  • 사용자 (즉, 명시 적/암시 적) 거래
  • 하위 프로세스 (즉, EXECsp_executesql)
(재귀 여부)

이러한 시나리오를 통해 동시에 여러 개체를 잠글 수 있습니다.

@@ SPID는 실제로이 단일 트리거 실행으로 제한됩니까? 아니면 다른 동시 트리거가이 프로세스 ID 내에서 중복 될 수 있습니까?

(질문에 대한 코멘트에서) :

나는 내가 sys.partitions 내 쿼리를 가입하고 개체 이름 'RID'의 유형이있는 dm_trans_lock를 얻을 수 있다고 생각합니다 내 원래 쿼리의 것과 일치합니다.

다음은 완전히 신뢰할 수없는 이유입니다. 세션 ID (즉, @@SPID)는 해당 연결의 모든 요청에 ​​대해 일정합니다. 따라서 모든 하위 프로세스 (예 : EXEC 호출, sp_executesql, 트리거 등)는 모두 @@SPID/session_id입니다. 따라서 하위 프로세스와 사용자 트랜잭션간에 동일한 세션 ID로 여러 자원에 대한 잠금을 매우 쉽게 얻을 수 있습니다.

"OBJECT"또는 "RID"대신 "resources"라고하는 이유는 행, 페이지, 키, 테이블, 스키마, 저장 프로 시저, 데이터베이스 자체 등에서 잠금이 발생할 수 있기 때문입니다. 둘 이상 일은 "OBJECT"로 간주 될 수 있으며 행 잠금 대신 페이지 잠금을 가질 수 있습니다.

데이터베이스 내에서 동일한 트리거 및/또는 다른 트리거를 여러 번 실행할 수 있습니까?

이러한 실행이 다른 세션에서 발생하는 한 그들은 문제가되지 않습니다.

간단한 설명에 따르면 현재의 방법이 어디에서 신뢰할 수 있는지 쉽게 알 수 있습니다.그러나 다른 테이블에 일부 DML을 먼저 수행하는 명시적인 트랜잭션을 포함하는 자세한 테스트를 추가하거나 이러한 테이블 중 하나에서 DML을 수행하는 테이블을 하나 이상 가질 수 있으면 충분히 쉽습니다.

불행히도 T-SQL 트리거에 대해 @@PROCID과 동일한 기능을 제공하는 기본 제공 메커니즘이 없습니다. 이 이러한 다양한 문제를 고려한 SQLCLR 트리거에 대한 부모 테이블을 가져올 수 있도록해야하지만, 테스트 할 기회는 없었습니다. "첫 번째"트리거로 설정된 T-SQL 트리거를 사용하여 SQLCLR 트리거로 검색 할 수있는 정보를 설정해야합니다.

더 간단한 형태는 이미 다른 뭔가를 사용하지 않을 경우 CONTEXT_INFO, 를 사용하여 구성 할 수있다 (그리고 당신은 이미 "최초의"트리거가 설정되어 있지 않은 경우). 이 방법에서는 여전히 T-SQL 트리거를 만든 다음 sp_settriggerorder을 사용하여 "첫 번째"트리거로 설정합니다. 이 트리거에서는 @@PROCID의 부모 인 테이블 이름에 SET CONTEXT_INFO이 있습니다. 그런 다음 SQLCLR 트리거의 컨텍스트 연결에서 CONTEXT_INFO()을 읽을 수 있습니다. 여러 레벨의 트리거가있는 경우 CONTEXT INFO의 값이 겹쳐 쓰여 지므로 해당 값을 읽는 것이 각 SQLCLR 트리거에서 가장 먼저 수행해야합니다.

+0

위험 할 정도로 SQLCLR에 대해 알고 있습니다 만, 트리거 코드에서 SqlTriggerAttribute를 조사하여 속성을 알아내는 방법이 있습니다 (이 경우 대상은 관련있는 것으로 보입니다)? –

+1

@BenThul 의미 있음 예. 메서드 특성은 어셈블리에 저장되며 Reflection을 통해 검색 할 수 있습니다. 그러나 "대상"은 컴파일시 하드 코딩 된 값이됩니다. 따라서이 값은 같은 'SqlTrigger' 메서드의 모든 사용에 걸쳐 동일합니다. 다른 값을 얻는 유일한 방법은 재 컴파일이며 따라서 모든 SqlTrigger 메서드가 공통 메서드를 호출한다고해도 대상 당 하나의 메서드가 있으므로 유지 관리 비용이 증가합니다. 나는 이것이 더 다루기 쉬운 형태로 들어갈 수 있는지 그리고'TriggerContext'가 예상대로 진행되는지를보기 위해 이것을 시험해야 할 것입니다. –

+0

@srutzky 감사합니다. 불행히도 일반적인 방법을 사용하는 각 테이블에 대해 단일 트리거 정의를 갖는 것 이외에 다른 방법을 사용하지는 않습니다. 나는 SqlTriggerAttribute로부터 정보를 얻으려고 시도했지만 그것도 나를 위해 작동하지 않았다. 내가 어떻게 생각해? 개별적으로 각 테이블에 대해 다른 트리거 정의를 사용해야하는 경우, 후속 공통 메소드 호출에서 테이블 이름을 하드 코딩하지 않아도된다고 생각합니다.나는 단지 내가 진정으로 그것을 만들 수있는 방법을 보는 것을 멈추고 싶지 않았다. – Greg