다음을 통해 공유


명령 트리에서 SQL 생성 - 최선의 방법

출력 쿼리 명령 트리는 SQL로 표현 가능한 쿼리를 유사하게 모델링합니다. 그러나 출력 명령 트리에서 SQL을 생성할 때 공급자 작성기에 대한 특정한 공통적 문제가 있습니다. 이 항목에서는 이러한 문제에 대해 설명하며, 그 다음 항목에서는 동일한 공급자가 이러한 문제를 처리하는 방법을 보여 줍니다.

SQL SELECT 문에서 DbExpression 노드 그룹화

일반적인 SQL 문에는 다음과 같은 모양의 중첩 구조가 있습니다.

SELECT …
FROM …
WHERE …
GROUP BY …
ORDER BY …

하나 이상의 절이 비어 있을 수 있습니다. 중첩된 SELECT 문은 어떤 줄에서든 발생할 수 있습니다.

쿼리 명령 트리를 SQL SELECT 문으로 변환하는 경우 관계형 연산자마다 하위 쿼리가 하나씩 생성됩니다. 그러나 이 경우 읽기 어려운 불필요한 중첩 하위 쿼리가 생성되며, 일부 데이터 저장소에서는 쿼리 성능이 저하될 수 있습니다.

예를 들어, 다음과 같은 쿼리 명령 트리를 살펴보겠습니다.

Project (
a.x,
   a = Filter(
      b.y = 5, 
      b = Scan("TableA")
   )
)

비효율적인 변환에서는 다음을 생성합니다.

SELECT a.x
FROM (   SELECT *
         FROM TableA as b
         WHERE b.y = 5) as a

모든 관계식 노드가 새로운 SQL SELECT 문이 됩니다.

따라서 정확성을 유지하면서 가능한 한 많은 식 노드를 단일 SQL SELECT 문에 집계해야 합니다.

위의 예제에 대한 이러한 집계의 결과는 다음과 같습니다.

SELECT b.x 
FROM TableA as b
WHERE b.y = 5

SQL SELECT 문에서 조인 평면화

여러 노드를 단일 SQL SELECT 문에 집계하는 예로 여러 조인 식을 단일 SQL SELECT 문에 집계하는 경우를 들 수 있습니다. DbJoinExpression은 두 입력 간의 단일 조인을 나타냅니다. 그러나 단일 SQL SELECT 문의 일부로 둘 이상의 조인을 지정할 수 있습니다. 이 경우 지정된 순서대로 조인이 수행됩니다.

왼쪽 편 조인(다른 조인의 왼쪽 자식으로 나타나는 조인)은 단일 SQL SELECT 문으로 보다 쉽게 평면화할 수 있습니다. 예를 들어, 다음과 같은 쿼리 명령 트리를 살펴보겠습니다.

InnerJoin(
   a = LeftOuterJoin(
   b = Extent("TableA")
   c = Extent("TableB")
   ON b.y = c.x ),
   d = Extent("TableC") 
   ON a.b.y = d.z
)

이 트리는 다음으로 올바르게 변환될 수 있습니다.

SELECT *
FROM TableA as b
LEFT OUTER JOIN TableB as c ON b.y = c.x
INNER JOIN TableC as d ON b.y = d.z

그러나 왼쪽 편 조인이 아닌 조인은 쉽게 평면화할 수 없으므로 평면화하려고 하면 안 됩니다. 다음 쿼리 명령 트리의 조인을 예로 들 수 있습니다.

InnerJoin(
   a = Extent("TableA") 
   b = LeftOuterJoin(
   c = Extent("TableB")
   d = Extent("TableC")
   ON c.y = d.x),
   ON a.z = b.c.y
)

이 조인은 하위 쿼리가 포함된 SQL SELECT 문으로 변환됩니다.

SELECT *
FROM TableA as a
INNER JOIN (SELECT * 
   FROM TableB as c 
   LEFT OUTER JOIN TableC as d
   ON c.y = d.x) as b
ON b.y = d.z

입력 별칭 리디렉션

입력 별칭 리디렉션을 설명하기 위해 DbFilterExpression, DbProjectExpression, DbCrossJoinExpression, DbJoinExpression, DbSortExpression, DbGroupByExpression, DbApplyExpression 및 DbSkipExpression과 같은 관계식의 구조를 살펴보겠습니다.

이러한 각 형식에는 입력 컬렉션을 설명하는 하나 이상의 입력 속성이 있으며, 각 입력에 해당하는 바인딩 변수는 컬렉션 순회 중에 해당 입력의 각 요소를 나타내는 데 사용됩니다. 바인딩 변수는 DbFilterExpression의 Predicate 속성이나 DbProjectExpression의 Projection 속성 등에서 입력 요소를 참조할 때 사용됩니다.

더 많은 관계식 노드를 단일 SQL SELECT 문에 집계하고 관계식의 일부(예: DbProjectExpression의 Projection 속성 일부)인 식을 계산하는 경우 여러 식 바인딩이 단일 익스텐트로 리디렉션되어야 하므로 사용하는 바인딩 변수는 입력의 별칭과 동일하지 않을 수 있습니다. 이 문제를 별칭 이름 바꾸기라고 합니다.

이 항목의 첫 번째 예제를 살펴보겠습니다. 기본 변환을 수행하고 Projection a.x (DbPropertyExpression(a, x))를 변환하는 경우 입력의 별칭을 바인딩 변수와 일치하도록 "a"로 지정했으므로 a.x로 변환하는 것이 올바릅니다. 그러나 두 노드를 단일 SQL SELECT 문에 집계하는 경우에는 입력의 별칭이 "b"로 지정되었으므로 동일한 DbPropertyExpression을 b.x로 변환해야 합니다.

조인 별칭 평면화

출력 명령 트리의 다른 모든 관계식과 달리 DbJoinExpression은 각각 입력 중 하나에 해당하는 두 열로 구성된 행인 결과 형식을 출력합니다. DbPropertyExpresssion이 조인에서 제공되는 스칼라 속성에 액세스하기 위해 작성되는 경우 또 다른 DbPropertyExpresssion 위에 있습니다.

예로 예제 2의 "a.b.y"와 예제 3의 "b.c.y"를 들 수 있습니다. 그러나 해당 SQL 문에서 이들은 "b.y"로 참조됩니다. 이렇게 별칭이 다시 지정되는 것을 조인 별칭 평면화라고 합니다.

열 이름 및 익스텐트 별칭 이름 바꾸기

조인이 있는 SQL SELECT 쿼리가 프로젝션을 사용하여 완료되어야 하는 경우 입력에서 참여하는 열을 모두 열거하면 둘 이상의 입력에 동일한 열 이름이 있을 수 있으므로 이름 충돌이 발생할 수 있습니다. 충돌을 방지하려면 열에 서로 다른 이름을 사용합니다.

또한 조인을 평면화할 때 참여하는 테이블(또는 하위 쿼리)에 충돌하는 별칭이 있을 수 있습니다. 이 경우 충돌하는 별칭의 이름을 바꿔야 합니다.

SELECT * 사용 금지

기본 테이블에서 선택하기 위해 **SELECT ***를 사용하지 마십시오. Entity Framework 응용 프로그램의 저장소 모델에는 데이터베이스 테이블에 있는 열의 하위 집합만 포함될 수 있습니다. 이 경우 **SELECT ***는 잘못된 결과를 생성할 수 있습니다. 대신, 참여하는 식의 결과 형식에서 열 이름을 사용하여 참여하는 모든 열을 지정해야 합니다.

식의 재사용

식은 Entity Framework 에서 전달되는 쿼리 명령 트리에서 다시 사용할 수 있습니다. 각 식이 쿼리 명령 트리에서 한 번만 나타난다고 가정하지 마십시오.

기본 형식 매핑

개념적(EDM) 형식을 공급자 형식에 매핑하는 경우 가능한 모든 값이 들어가도록 가장 넓은 형식(Int32)에 매핑해야 합니다. 또한 BLOB 형식과 같이 많은 작업에 사용할 수 없는 형식(예: SQL Server의 ntext)에 매핑하지 마십시오.

참고 항목

개념

SQL 생성