Tip & Tech

SQL Server 2008 부터 사용가능


Union 이나 union all 이 데이터의 합집합을 구하는것이라면


- intersect 는 두개의 테이블에 데이터가 동시에 존재하는 교집합을 구하는 것이고

- except 는 첫번째 테이블에는 있고 두번째 테이블에는 없는 데이터만을 구하는 차 집합을 구하는것이다.


이에반해 join 은 데이터의 수평적 결합이고 union 은 두개의 테이블에 데이터 존재여부를 검사하지 않고 무조건 합치는 데이터의 합집합이다.


기존에 교집합이나 차집합을 구하기 위해 복잡한 쿼리를 작성해야 했다면 이 두 연산자를 통해 기존 join 이나 group by 를 통해 구현했어야 할 복잡한 쿼리를 매우 간단하게 처리할 수 있게 되었다.
Intersect 를 통해 구현할 수 있는 쿼리의 편안함을 예로 들어보자

 
select * into Company1
from (
values ('Bohler Uddeholm Spec Metals','South Boston','VA')
,('Cigital Inc','Sterling','VA')
,('Texas Citrus Exchange','Mission','TX')
,('Garden State Tanning Byron WD','Williamsport','MD')
,('Byram Realty','Lexington','GA')
,('Blue Moon Graphics','Charleston','SC')
,('Lake Tahoe Community College','South Lake Tahoe','CA')
,('Pyramind','San Francisco','CA')
,('Twisp Chamber Of Commerce','Twisp','WA')
,('Dalsin Industries','Bloomington','MN')) Tab (comname, city, state)

select * into Company2
from (
values ('Bohler Uddeholm Spec Metals','South Boston','VA')
,('Cigital Inc','Sterling','VA')
,('Texas Citrus Exchange','Mission','TX')
,('Blue Moon Graphics','Charleston','SC')
,('Lake Tahoe Community College','South Lake Tahoe','CA')
,('Pyramind','San Francisco','CA')
,('Twisp Chamber Of Commerce','Twisp','WA')
,('Dalsin Industries','Bloomington','MN')
,('A Beautiful Celebration','Leesburg','FL')
,('Bumper To Bumper','Valley','AL')
,('Alpha Graphics','Baton Rouge','LA')
,('ARH Air Cond Refrig & Heat','Wewahitchka','FL')
,('Bama Golf Carts','Sylacauga','AL')
,('Baggett Consulting','Fairhope','AL')
,('Byrd''s Western Store','Melbourne','FL')
,('Byrd Tile Distributors','Raleigh','NC')
,('Ronald Mc Donald House','Columbus','OH')) Tab (comname, city, state)



두개의 테이블( company1 과 company2) 에 동시에 존재하는 회사를 뽑기위한 쿼리이다


select comname, city, state, lastname from company1
Intersect
select comname, city, state, lastname from company2

결과는 아래와 같이 두개의 테이블에 조인해서 뽑은 결과와 같이 나온다


위와 같이 단순이 두 쿼리를 intersect 연산자를 이용하여 붙여주면 양쪽 테이블에 모두 존재하는 데이터만 distinct 한 상태로 추출해 준다. 

이것을 intersect 없이 구현하려면 아래와 같이 복잡한 조인 쿼리나 union group by 쿼리를 사용하여 처리해야 한다. 

select distinct a.comname, a.city, a.state
from company1 a inner join company2 b 
	on a.comname = b.comname
	and a.city = b.city
	and a.state = b.state

또는 

select comname, city, state
from (
select distinct comname, city, state from company1
union all
select distinct comname, city, state from company2
) A
group by comname, city, state
having count(*)=2
 

이렇게 두개의 테이블 일때는 그래도 할만하지만 비교해야할 테이블이 4~5 개 이상으로 늘어나면 쿼리가 한페이지가 넘어가고 에러가 날경우 뭐가 잘못된건지 찾는것도 쉽지 않다.


다음으로 except 는 차집합을 구하는 마이너스 쿼리이다. 위 테이블에는 있고 아래 테이블에는 없는 데이터를 추출한다.


위에서 사용한 company1, company2 테스트 테이블로 쿼리를 실행해 보면 company1 에는 있고 company2 에는 없는 데이터를 추출한다.


 
select comname, city, state from company1
except
select comname, city, state from company2
 


위의 except 쿼리를 기존 연산자로 쿼리하려면 not exists subquery 를 이용해야 하는데 중복 체크해야 하는데 칼럼이 많을경우 쿼리가 매우 복잡해지고 명확하지 않다.

select comname, city, state from company1 a
where not exists(
	select 1 from company2 
	where comname=a.comname and city=a.city and state=a.state
)


위 두개의 쿼리 모두 아래와 같이 두건의 데이터를 동일하게 리턴해준다.




위와 같이 intersect 나 except 가 위아래 동일한 칼럼을 추출할때는 매우 유용한 연산자이지만 추출해야 할 칼럼이 다르거나 특정칼럼을 조건으로 존재여부를 체크해야 할경우는 여전히 기존 조인이나 exists 구문을 사용하는 것이 유용한 경우가 훨씬 많다.


따라서 이런 연산자가 있다는 것을 알아두고 필요할경우 사용하면 쿼리문이 단순해 질수 있지만 기존에 사용했던 조인이나 exists 쿼리의 쓰임새를 확실히 알아 두어야 막힘 없이 쿼리를 사용할수 있게 된다.


실제 현업에서 쿼리의 구문을 정확히 알필요는 없다. 자주 사용하는 쿼리의 구문만 알고 있으면 되고, 특정 케이스에 특정 연산자를 사용하면 효율적이다는 것만 알고 있으면 필요할때 구문은 찾아서 작성하면 된다.


DBA 업무를 하다보면 이렇게 두루두루 사용 가능한 기능들을 인지하고 있는것이 도움이 되는경우가 많이 있다.


Sql server 2005 버전부터 소개된 apply 는 join 쿼리와 유사하지만 테이블 형식 리턴 함수나 corelated subquery 를 테이블 대신 사용할수 있다. 또한 왼쪽 테이블의 값을 테이블 리턴 함수나 corelated subquery 의 인자로 사용할 수 있어서 왼쪽 테이블 값에 따라 데이터를 처리하여 나온 결과값을 왼쪽 테이블 데이터와의 결합에 사용할 수 있다. 하나의 필드값 만이 필요할 때는 Scala 함수를 이용하여 select 리스트에 함수를 호출하면 되지만 입력 변수에 따라 여러 필드를 동시에 가져와야할 경우 나 내부 함수처리를 통해 데이터를 만들어야 할 경우 apply가 매우 유용한 구문이 될 수 있다.

Apply 실행시 오른쪽 테이블 리턴함수 또는 서브쿼리는 각 레코드에 대해서 각각 평가된다.


Apply 에는 cross apply 와 outer apply 가 있는데

cross apply 는 테이블 리턴함수 또는 corelated subquery를 사용할수 있다는것을 제외하고 inner join 과 같은 결과를 가져온다.

Outer apply 는 테이블 리턴함수 또는 corelated subquery를 사용할 수 있다는 것을 제외하고 left outer join 과 같은결과를 가져온다. 따라서 outer apply 는 오른쪽 테이블 리턴함수 또는 corelated subquery에서 리턴되는 데이터가 없더라도 결과 값을 반환하지만 오른쪽 테이블 리턴함수 또는 corelated subquery의 칼럼들은 null 을 반환한다.


일반적인 join 을 이용하여 쿼리할수 있는데 굳이 apply 를 사용해야 하는 의문이 들수 있다. 하지만 테이블 반환함수를 오른쪽 테이블 반환함수나 서브쿼리에 왼쪽 테이블의 값을 인자로 제공할 수 있다는 특징이 있다. 이것은 join 쿼리로 구현할 수 없는 기능이다.

또한 성능점검을 위한 Dynamic Management Function (DMF) 사용시에도 apply 는 매우 유용한 구문이 된다.


Apply 에는 join 에서와 같은 on 절을 사용하지 않는다. 오른쪽 테이블 반환 함수나 corelated subquery 에 리턴되는 결과값이 있으면 해당 결과값의 레코드 수만큼 결과집합은 수가 늘어나게 된다.


왼쪽 테이블에도 테이블 리턴 함수를 사용할 수 있지만 오른쪽 테이블의 레코드 값을 인자로 사용할 수는 없다.



 
USE master 

GO 

SELECT DB_NAME(r.database_id) database, st.[text] query

FROM sys.dm_exec_requests r 

CROSS APPLY sys.dm_exec_sql_text(r.plan_handle) st 

WHERE r.session_Id > 50 -- 시스템 spid 를 배제하기 위해 

AND r.session_Id NOT IN (@@SPID) -- 현재 spid 를 배제하기 위해
 


모든 데이터 베이스 공통 테이블 존재여부 체크쿼리이다. 

ISO 표준을 따르는 모든 데이터베이스는 information_schema 라는 스키마를 만들어야 하고 그에 따른 VIEW 들을 쿼리할수 있게 만들어야 한다. 

따라서 oracle, mysql 등과 호환되는 스크립트를 만들어야 할 경우 information_schema 소유의 뷰들을 이용하여 존재여부를 체크하여야 한다.

 
IF EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE='BASE TABLE' AND TABLE_NAME='mytablename') 
DROP TABLE mytablename
 

Sql server 에서만 사용할수 있는 테이블 존재여부 체크 쿼리이다. 

SQL Server 버전에 관계없이 사용할수 있는 함수를 이용한 존재 여부 체크


구문

 
IF OBJECT_ID('YourObject', 'ObjectsType') IS NOT NULL 
DROP <ObjectsType> 
 


테이블 존재 체크 예시

 
IF OBJECT_ID (N'mytablename', N'U') IS NOT NULL
DROP TABLE mytablename; 
 


함수 존재 체크 예시

 
IF OBJECT_ID (N'myfunction', N'FN') IS NOT NULL
DROP FUNCTION myfunction; 
 


프로시져 존재 체크 예시

 
IF OBJECT_ID (N'myproc', N'P') IS NOT NULL
DROP PROCEDURE myproc; 
 


Object_id 함수에서 사용할수 있는 SQL Server 버전별 Objectstype

Object type (sys.objects.type)Object Description2000200520082012 이상2016 이상
       
AFAggregate function (CLR) xxxx
CCHECK constraintxxxxx
DDEFAULT (constraint or stand-alone)xxxxx
FFOREIGN KEY constraintxxxxx
FNSQL scalar functionxxxxx
FSAssembly (CLR) scalar function xxxx
FTAssembly (CLR) table-valued function xxxx
IFSQL inline table-valued functionxxxxx
ITInternal table xxxx
PSQL stored procedurexxxxx
PCAssembly (CLR) stored procedure xxxx
PGPlan guide  xxx
PKPRIMARY KEY constraintxxxxx
RRule (old-style, stand-alone) xxxx
RFReplication-filter-procedure xxxx
SSystem base tablexxxxx
SNSynonym xxxx
SOSequence Object   xx
SQService queue  xxx
TAAssembly (CLR) DML trigger xxxx
TFSQL table-valued-functionxxxxx
TRSQL DML triggerxxxxx
TTTable type   xx
UTable (user-defined)xxxxx
UQUNIQUE constraintxxxxx
VViewxxxxx
XExtended stored procedure   xx
ETExternal Table    x


테이블 쿼리를 이용한 존재 여부 체크


SQL Server 2000 버전 이하 

 
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = object_id (N'[dbo].[IsSomething]') AND OBJECTPROPERTY(id, N'IsFunction') = 1)
 


SQL Server 2005 버전 이상

 
IF EXISTS (SELECT * FROM sys.objects WHERE id = object_id (N'[dbo].[IsSomething]') AND OBJECTPROPERTY(id, N'IsFunction') = 1) 
 


Objectproperty 에 사용할 수 있는 property 는 이 링크를 참조 (https://docs.microsoft.com/ko-kr/sql/t-sql/functions/objectproperty-transact-sql?view=sql-server-2017)