FROM 절이 누락 된이 쿼리는 왜 오류가 발생하지 않습니까?


9

그래서 우리는 오타가있는 하위 쿼리가있는 쿼리가 있습니다. FROM 절이 없습니다. 그러나 실행할 때 오류가 발생하지 않습니다! 왜!?


SELECT

    1
   ,r.id
   ,'0D4133BE-C1B5-4141-AFAD-B171A2CCCE56'
   ,GETDATE()
   ,1
   ,'Y'
   ,'N'
   ,oldItem.can_view
   ,oldItem.can_update

FROM Role r

JOIN RoleObject oldReport
    ON r.customer_id = oldReport.customer_id

JOIN RoleItem oldItem
    ON oldReport.id = oldItem.role_object_id
        AND r.id = oldItem.role_id

WHERE r.id NOT IN (SELECT
        role_id
    WHERE role_object_id = '0D4133BE-C1B5-4141-AFAD-B171A2CCCE56')

AND oldReport.id = '169BA22F-1614-4EBA-AF45-18E333C54C6C'

답변:


21

이 진술은 합법적입니다 (즉, FROM필요 하지 않습니다).

SELECT x = 1;
SELECT x = 1 WHERE 1 = 1; -- also try WHERE 1 = 0;

요점은 명확하게 존재할 수없는 열 이름을 도입 할 때입니다. 그래서 이것들은 실패합니다 :

SELECT name WHERE 1 = 1;

SELECT x = 1 WHERE id > 0;

메시지 207, 수준 16, 상태 1
잘못된 열 이름 'name'입니다.
메시지 207, 수준 16, 상태 1
잘못된 열 이름 'id'입니다.

그러나 잘못된 쿼리가 하위 쿼리와 같은 방식으로 도입되면 하위 쿼리의 내부 범위에서 해당 열을 찾을 수 없을 때 SQL Server가 수행하는 작업은 외부 범위로 이동하고 하위 쿼리가 해당 외부 범위와 상관됩니다. 예를 들어 모든 행을 반환합니다.

SELECT * FROM sys.columns WHERE name IN (SELECT name WHERE 1 = 1);

본질적으로 다음과 같이 말합니다.

SELECT * FROM sys.columns WHERE name IN (SELECT sys.columns.name WHERE 1 = 1); /*
              ^^^^^^^^^^^                       -----------
                   |                                 |
                   -----------------------------------    */

WHERE하위 쿼리 에는 절이 필요하지 않습니다 .

SELECT * FROM sys.columns WHERE name IN (SELECT name);

외부 범위 테이블을 실제로보고 있음을 알 수 있습니다.

SELECT * FROM sys.columns WHERE name IN (SELECT name WHERE name > N'x');

훨씬 적은 수의 행을 반환합니다 (시스템에서 11 개).

여기에는 범위 지정에 대한 표준을 준수하는 것이 포함됩니다. 두 개의 #temp 테이블이 있으면 비슷한 것을 볼 수 있습니다.

CREATE TABLE #foo(foo int);
CREATE TABLE #bar(bar int);

SELECT foo FROM #foo WHERE foo IN (SELECT foo FROM #bar);

더 있기 때문에 분명히,이, 우, 오류가 없어야 foo에서 #bar? 아니. 무슨 일이 일어나는지 SQL Server는 "오, 나는 foo여기를 찾지 못했습니다 . 당신은 다른 것을 의미했을 것입니다."라고 말합니다.

또한 일반적으로 피할 것 NOT IN입니다. NOT EXISTS일부 시나리오에서는 더 효율적일 가능성이 있지만 더 중요하게는 대상 컬럼이 될 수있을 때 동작이 변경되지 않습니다 NULL. 자세한 내용은이 게시물을 참조하십시오 .


나는 스택 오버플로에 대한 답변을 본질적으로 이것과 동일하게 질문했습니다 (더 철저하지만). 쿼리중인 테이블의 일부가 아닌 열 (왼쪽 피연산자)을 참조하는 것이 왜 EXISTS 연산자에서 오류가 아닌가?
Marc.2377

2

나는 2016 년에 이것을 간단한 예와 함께 재현했습니다.

declare @t1 table (c1 int, c2 int, c3 int)
insert into @t1 values (1,2,3), (2,3,4), (3,4,5)

select * from @t1
where
    c1 not in 
    (select c2 where c3 = 3)

c2 및 c3이 각 행에 대해 평가되는 것으로 보입니다.


1

SQL Server SELECT 구문에서는 FROM 섹션이 필요하지 않습니다. FROM을 생략하면 select 문은 하나의 행이 있고 열이없는 "더미"테이블을 사용합니다. 그래서

select 'x' as c where ...

식이 true이면 하나의 행을 반환하고 false이면 행을 반환하지 않습니다.


그러나 그것은 그냥 말하는 경우에 작동하지 않습니다 select cc일부 외부 객체에 존재하지 않습니다. 나는 동의 FROM필요하지 않습니다,하지만 당신은 명시 적으로 열 이름을 지정할 때 여기 플레이의 역학 않습니다 당신이하지 않는 컬럼에 대한 상수를 제공하지 않는 경우 외부 범위에 존재는 더미 테이블에 비해 확실히 다르며, 존재하면 런타임 오류가 발생하므로 더미 테이블도 없습니다. 더미 테이블은 다른 시나리오에서 작동 할 수 있지만 참조가 하위 쿼리 / 파생 테이블에있는 경우에는 불가능합니다.
Aaron Bertrand

예에서 상관 상관 하위 선택이며 role_id 및 role_object_id는 외부 선택의 테이블 중 하나에 속합니다.
Piotr

그렇다고 말하는 SELECT 'x' AS c것은 방금 말한 OP의 시나리오와 완전히 다른 시나리오 SELECT c입니다. 하위 쿼리 / 파생 테이블에서.
Aaron Bertrand
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.