SELECT 문을 살펴봅니다.
Transact-SQL 또는 T-SQL은 Microsoft SQL 제품 및 서비스에서 사용하는 ANSI 표준 SQL 언어입니다. T-SQL은 표준 SQL과 비슷합니다. DML 문 중 옵션과 변형이 가장 많은 SELECT 문을 중점적으로 다룰 것입니다.
먼저 SELECT 문이 처리되는 방법을 좀 더 깊게 살펴봅시다. SELECT 문이 작성되는 순서는 SQL Server 데이터베이스 엔진에서 계산하고 처리하는 순서와 다릅니다.
다음과 같은 쿼리를 고려해 보세요.
SELECT OrderDate, COUNT(OrderID) AS Orders
FROM Sales.SalesOrder
WHERE Status = 'Shipped'
GROUP BY OrderDate
HAVING COUNT(OrderID) > 1
ORDER BY OrderDate DESC;
쿼리는 여러 ‘절’로 구성된 SELECT 문으로 구성되며 각 절은 검색되는 데이터에 적용해야 하는 특정 작업을 정의합니다. 작업의 런타임 순서를 살펴보기 전에, 이 모듈에서 다양한 절의 세부 정보를 다루지 않지만 이 쿼리가 수행하는 작업을 간략하게 살펴보겠습니다.
SELECT 절은 OrderDate 열과 OrderID 값의 개수를 반환합니다. 이 값에는 Orders라는 이름(또는 ‘별칭’)이 할당됩니다.
SELECT OrderDate, COUNT(OrderID) AS Orders
FROM 절은 쿼리의 행의 원본인 테이블을 식별합니다. 이 경우 Sales.SalesOrder 테이블입니다.
FROM Sales.SalesOrder
WHERE 절은 결과에서 행을 필터링하여 지정된 조건을 충족하는 행만 유지합니다. 이 경우 상태가 “shipped”인 주문입니다.
WHERE Status = 'Shipped'
GROUP BY 절은 필터 조건을 충족하는 행을 가져와 OrderDate별로 그룹화하므로 OrderDate가 동일한 모든 행은 단일 그룹으로 간주되고 각 그룹에 하나의 행이 반환됩니다.
GROUP BY OrderDate
그룹을 구성한 후 HAVING 절은 고유의 조건자에 따라 그룹을 필터링합니다. 두 개 이상의 주문이 있는 날짜만 결과에 포함됩니다.
HAVING COUNT(OrderID) > 1
이 쿼리를 미리 보기 위해 최종 절은 출력을 OrderDate 기준 내림차순으로 정렬하는 ORDER BY입니다.
ORDER BY OrderDate DESC;
지금까지 각 절이 수행하는 작업을 살펴보았습니다. 이제 SQL Server가 실제로 계산하는 순서를 살펴보겠습니다.
- 문의 나머지 부분에 대한 원본 행을 제공하기 위해 FROM 절이 먼저 계산됩니다. 가상 테이블이 만들어지고 다음 단계로 전달됩니다.
- WHERE 절을 계산하여 조건자와 일치하는 원본 테이블에서 해당하는 행을 필터링합니다. 필터링된 가상 테이블이 다음 단계로 전달됩니다.
- Group BY는 다음으로 GROUP BY 목록에 있는 고유 값에 따라 가상 테이블의 행을 구성합니다. 그룹 목록이 포함된 새 가상 테이블이 만들어지고 다음 단계로 전달됩니다. 작업 흐름에서 이 시점부터 다른 요소에서 GROUP BY 목록 또는 집계 함수의 열만 참조할 수 있습니다.
- HAVING 절은 조건자를 기준으로 전체 그룹을 필터링하여 다음에 계산됩니다. 3단계에서 만든 가상 테이블이 필터링되어 다음 단계로 전달됩니다.
- SELECT 절은 마지막으로 실행되어 쿼리 결과에 표시될 열을 결정합니다. SELECT 절은 다른 단계 이후에 계산되므로 SELECT 절에서 만든 열 별칭(이 예제에서는 Orders)은 GROUP BY 또는 HAVING 절에서 사용할 수 없습니다.
- ORDER BY 절은 마지막으로 실행되어 열 목록에 의해 결정된 대로 행을 정렬합니다.
예제 쿼리에 이해한 내용을 적용하기 위해 위의 SELECT 문 런타임의 논리적 순서는 다음과 같습니다.
FROM Sales.SalesOrder
WHERE Status = 'Shipped'
GROUP BY OrderDate
HAVING COUNT(OrderID) > 1
SELECT OrderDate, COUNT(OrderID) AS Orders
ORDER BY OrderDate DESC;
작성하는 모든 SELECT 문에 가능한 모든 절이 필요한 것은 아닙니다. 유일한 필수 절은 SELECT 절이며 경우에 따라 자체적으로 사용할 수 있습니다. 일반적으로 쿼리 중인 테이블을 식별하기 위해 FROM 절이 포함되어 있습니다. 또한 Transact-SQL에는 추가될 수 있는 다른 절이 있습니다.
앞서 살펴봤듯이 논리적으로 계산되는 순서와 동일한 순서로 T-SQL 쿼리를 작성하지 않습니다. 절은 이미 처리된 절에서 사용 가능하게 설정된 정보에만 액세스할 수 있기 때문에 계산의 런타임 순서에 따라 어떤 데이터가 어떤 절에서 사용 가능할지가 결정됩니다. 따라서 쿼리를 작성할 때 진정한 논리적 처리 순서를 이해하는 것이 중요합니다.
모든 열 선택
SELECT 절은 쿼리의 결과에서 반환될 값을 나열하므로 SELECT ‘목록’이라고 합니다.
SELECT 절의 가장 간단한 형태는 별표 문자(*)를 사용하여 모든 열을 반환하는 것입니다. T-SQL 쿼리에서 사용되는 경우 이를 ‘별표’라고 합니다. SELECT *는 빠른 테스트에 적합하지만, 다음과 같은 이유로 프로덕션 작업에 사용하지 않는 것이 좋습니다.
- 테이블에 열을 추가하거나 다시 정렬하는 변경 내용은 쿼리 결과에 반영되며 이로 인해 쿼리를 사용하는 애플리케이션 또는 보고서에 대한 예기치 않은 출력이 발생할 수 있습니다.
- 필요하지 않은 데이터를 반환하면 쿼리 속도가 느려질 수 있으며 원본 테이블에 많은 행이 포함된 경우 성능 문제가 발생할 수 있습니다.
예를 들어 다음 예에서는 (가상) Production.Product 테이블에서 모든 열을 검색합니다.
SELECT * FROM Production.Product;
이 쿼리의 결과는 테이블의 모든 행에 대한 모든 열을 포함하는 행 집합이며 다음과 같을 수 있습니다.
ProductID
Name
ProductNum
색
StandardCost
ListPrice
크기
무게
ProductCatID
680
HL Road Frame - Black, 58
FR-R92B-58
흑인
1059.31
1431.5
58
1016.04
18
706
HL Road Frame - Red, 58
FR-R92R-58
빨간색
1059.31
1431.5
58
1016.04
18
707
Sport-100 Helmet, Red
HL-U509-R
빨간색
13.0863
34.99
35
708
Sport-100 Helmet, Black
HL-U509
흑인
13.0863
34.99
35
...
...
...
...
...
...
...
...
...
특정 열 선택
명시적 열 목록을 사용하면 반환되는 열과 순서를 정확하게 컨트롤할 수 있습니다. 결과의 각 열에는 열 이름이 헤더로 포함됩니다.
예를 들어 가상의 Production.Product 테이블을 다시 사용하는 다음 쿼리를 살펴봅시다.
SELECT ProductID, Name, ListPrice, StandardCost
FROM Production.Product;
이번에는 지정된 열만 결과에 포함됩니다.
ProductID
Name
ListPrice
StandardCost
680
HL Road Frame - Black, 58
1431.5
1059.31
706
HL Road Frame - Red, 58
1431.5
1059.31
707
Sport-100 Helmet, Red
34.99
13.0863
708
Sport-100 Helmet, Black
34.99
13.0863
...
...
...
...
식 선택
SELECT 절은 지정된 테이블에 저장된 열을 검색하는 것 외에도 연산자를 사용하여 열과 값 또는 여러 열을 결합하는 계산 및 조작을 수행할 수 있습니다. 계산 또는 조작의 결과는 결과에 별도의 열로 표시되는 단일 값(스칼라) 결과여야 합니다.
예를 들어 다음 쿼리에는 두 개의 식이 포함됩니다.
SELECT ProductID,
Name + '(' + ProductNumber + ')',
ListPrice - StandardCost
FROM Production.Product;
이 쿼리의 결과는 다음과 같을 수 있습니다.
ProductID
680
HL Road Frame - Black, 58(FR-R92B-58)
372.19
706
HL Road Frame - Red, 58(FR-R92R-58)
372.19
707
Sport-100 Helmet, Red(HL-U509-R)
21.9037
708
Sport-100 Helmet, Black(HL-U509)
21.9037
...
...
...
결과에서 다음과 같은 몇 가지 흥미로운 사실을 확인할 수 있습니다.
- 두 식에서 반환된 열에는 열 이름이 없습니다. 쿼리를 제출하는 데 사용하는 도구에 따라 누락된 열 이름은 빈 열 머리글, “no column name” 리터럴 지표 또는 column1과 같은 기본 이름으로 표시될 수 있습니다. 이 섹션의 후반부에 있는 쿼리에서 열 이름의 ‘별칭’을 지정하는 방법을 살펴보겠습니다.
- 첫 번째 식은 + 연산자를 사용하여 문자열(문자 기반) 값을 연결하는 반면 두 번째 식은 - 연산자를 사용하여 한 숫자 값을 다른 숫자 값에서 뺍니다. 숫자 값과 함께 사용하는 경우 + 연산자는 더하기를 수행합니다. 식에 포함하는 열의 ‘데이터 형식’을 이해하는 것이 분명히 중요합니다. 다음 섹션에서는 데이터 형식에 대해 설명합니다.
열 별칭 지정
SELECT 쿼리에서 반환하는 각 열에 원본 열 이름 대신 ‘별칭’을 지정하거나 식의 출력에 이름을 할당할 수 있습니다.
예를 들어 다음 쿼리는 앞의 쿼리와 동일하지만 각 열에 대해 지정된 별칭을 사용합니다.
SELECT ProductID AS ID,
Name + '(' + ProductNumber + ')' AS ProductName,
ListPrice - StandardCost AS Markup
FROM Production.Product;
이 쿼리의 결과에는 지정된 열 이름이 포함됩니다.
ID
ProductName
태그
680
HL Road Frame - Black, 58(FR-R92B-58)
372.19
706
HL Road Frame - Red, 58(FR-R92R-58)
372.19
707
Sport-100 Helmet, Red(HL-U509-R)
21.9037
708
Sport-100 Helmet, Black(HL-U509)
21.9037
...
...
...
참고
AS 키워드는 별칭을 지정할 때 선택 사항이지만 설명을 위해 포함하는 것이 좋습니다.
서식 지정 쿼리
이 섹션의 예제를 통해 쿼리 코드의 형식을 유연하게 지정할 수 있다는 것을 확인할 수 있습니다. 예를 들어 각 절(또는 전체 쿼리)을 한 줄에 작성하거나 여러 줄로 나눌 수 있습니다. 대부분의 데이터베이스 시스템에서 코드는 대/소문자를 구분하지 않으며 T-SQL 언어의 일부 요소는 선택 사항입니다(앞에서 설명한 대로 AS 키워드가 포함되며 문 끝의 세미콜론도 포함).
T-SQL 코드를 쉽게 읽을 수 있도록(따라서 더 쉽게 이해하고 디버그할 수 있도록) 다음 지침을 고려합니다.
- SELECT, FROM, AS와 같은 T-SQL 키워드를 대문자로 표시합니다. 키워드를 대문자로 표시하는 것은 복잡한 문에서 각 절을 더 쉽게 찾을 수 있도록 일반적으로 사용되는 규칙입니다.
- 명령문의 각 주 절에서 새 줄을 시작합니다.
- SELECT 목록에 적지 않은 열, 식 또는 별칭이 포함된 경우 각 열을 각각의 줄에 나열하는 것이 좋습니다.
- 하위 절 또는 열이 포함된 줄을 들여쓰기하여 각 주 절에 속하는 코드를 명확하게 합니다.