SQL Server 2012에서 select *가 여전히 큰 인기가 있습니까?


41

위로 작년의 일, 그것은 큰 더 - 더 할 수없는 것으로 간주되지 않았다 select * from table또는 select count(*) from table때문에 성능 저하의.

이후 버전의 SQL Server에서도 여전히 그렇습니까? (2012를 사용하고 있지만 질문이 2008-2014에 적용되는 것 같습니다)?

편집 : 사람들이 나를 약간 여기에서 슬쩍하는 것처럼 보이기 때문에 나는 이것이 옳은 일인지 아닌지 벤치 마크 / 필수 관점에서 이것을보고 있습니다 (물론 그렇지 않습니다)

답변:


50

SELECT COUNT(*) FROM TABLE하나의 행 (카운트) 만 반환하는 경우 상대적으로 가볍고 해당 데이텀을 얻는 방법입니다.

그리고 SELECT *합법적이고 허용된다는 점에서 물리적 인 제한이 없습니다.

그러나 문제 SELECT *는 더 많은 데이터 이동을 유발할 수 있다는 것입니다. 테이블의 모든 열에서 작업합니다. 당신이 경우 SELECT몇 열을 포함, 당신은 I / O 또한 서버 캐시에 미치는 영향을 감소 인덱스 또는 인덱스에서 답을 얻을 수 있습니다.

그래서, 는 자원의 낭비이기 때문에 그것은 일반적인 관행으로 반대하는 것이 좋습니다.

유일한 장점은 SELECT *모든 열 이름을 입력하지 않는 것입니다 . 그러나 SSMS에서 끌어서 놓기를 사용하여 쿼리에서 열 이름을 가져오고 필요하지 않은 열 이름을 삭제할 수 있습니다.

비유 : 누군가가 사용하는 경우, SELECT *그들은 모든 열을 필요로하지 않을 때, 그들은 것 또한 사용 SELECT없이 WHERE그들은 모든 행을 필요로하지 않는 경우 (또는 다른 제한 절)?


24

이미 제공 한 답변 외에도 개발자가 Entity Framework와 같은 최신 ORM을 사용할 때 너무 게으르다는 점을 지적 할 가치가 있다고 생각합니다. DBA는 피하기 위해 최선을 다 SELECT *하지만 개발자는 종종 C # Linq와 같은 의미 적으로 동등한 내용을 작성합니다.

var someVariable = db.MyTable.Where(entity => entity.FirstName == "User").ToList();

본질적으로 다음과 같은 결과가 발생합니다.

SELECT * FROM MyTable WHERE FirstName = 'User'

아직 다루지 않은 추가 오버 헤드가 있습니다. 즉, 각 행의 각 열을 관련 개체로 처리하는 데 필요한 리소스입니다. 또한 메모리에 보관 된 모든 개체에 대해 해당 개체를 정리해야합니다. 필요한 컬럼 만 선택한 경우 100mb를 초과하는 램을 쉽게 절약 할 수 있습니다. 자체적으로 대량은 아니지만 가비지 수집 등의 누적 효과는 비용 클라이언트 측입니다.

그렇습니다. 최소한의 저에게는 항상 그렇습니다. 또한이 작업을 수행하는 "숨겨진"비용에 대해 교육해야합니다.

추가

다음은 주석에서 요청한대로 필요한 데이터 만 가져 오는 샘플입니다.

var someVariable = db.MyTable.Where(entity => entity.FirstName == "User")
                             .Select(entity => new { entity.FirstName, entity.LastNight });

13

성능 : SELECT *가 포함 된 쿼리는 절대 쿼리가 아닐 수 있습니다 ( 간단한 대화 설명 , 스택 오버플로 설명 ).

미래 보장 : 오늘 쿼리에서 7 개의 열을 모두 반환 할 수 있지만 내년에 누군가가 5 개의 열을 추가하면 1 년 안에 쿼리에서 12 개의 열을 반환하여 IO와 CPU를 낭비하게됩니다.

인덱싱 : 뷰와 테이블 반환 함수가 SQL Server의 인덱싱에 참여하려면 해당 뷰와 함수를 스키마 바인딩으로 만들어야하므로 SELECT *를 사용할 수 없습니다.

모범 사례 : SELECT *프로덕션 코드에서 사용하지 마십시오 .

하위 쿼리의 경우을 선호합니다 WHERE EXISTS ( SELECT 1 FROM … ).

편집 : 하위 쿼리에서 "SELECT 1"을 사용하는 것은 아래의 Craig Young의 의견을 다루는 것이 " '최적화"가 아닙니다. 따라서 클래스 앞에 서서 "SELECT *를 사용하지 마십시오. 예외는 없습니다!" "

내가 생각할 수있는 유일한 예외는 클라이언트가 일종의 피벗 테이블 작업을 수행하고 현재 및 미래의 모든 열이 필요한 곳입니다.

CTE 및 파생 테이블과 관련된 예외를 수락 할 수는 있지만 실행 계획을보고 싶습니다.

COUNT(*)구문 적으로 "*"를 사용하기 때문에 이에 대한 예외를 고려 합니다.


10

SQL Server 2012 (또는 2005 이상 버전)에서는 SELECT *...쿼리의 최상위 SELECT 문에서 성능 문제가 발생할 수 있습니다.

그것은 CTE는 존재 절에서 하위 쿼리에서 뷰 (*),,,의 문제가 아니며, 그렇게 SELECT COUNT(*)..등, 등을 참고하는 것이이 오라클에 대해서도 아마 사실이며, DB2, 그리고 어쩌면 포스트 그레스 (하지 않도록) 그러나 MySql의 경우 많은 경우 여전히 문제가 될 가능성이 큽니다.

왜 (그리고 최상위 SELECT에서 여전히 문제가 될 수 있는지) 이해하려면 문제가 발생한 이유를 이해하는 것이 도움이됩니다. 이는 SELECT *.." 모든 열 반환 " 을 의미 하기 때문 입니다. 일반적으로 이것은 실제로 원하는 것보다 훨씬 많은 데이터를 반환 하므로 디스크와 네트워크 모두에서 더 많은 IO를 얻을 수 있습니다.

덜 명확한 것은 SQL 옵티마이 저가 사용할 수있는 인덱스 및 쿼리 계획도 제한한다는 것입니다. 궁극적으로 모든 데이터 열을 반환해야한다는 것을 알고 있기 때문입니다. 특정 열만 원한다는 것을 미리 알 수 있다면 해당 열만있는 인덱스를 활용하여보다 효율적인 쿼리 계획을 사용할 수 있습니다. 운 좋게도이를 미리 알 수있는 방법이 있습니다.이를 통해 열 목록에서 원하는 열을 명시 적으로 지정할 수 있습니다. 그러나 "*"를 사용할 때 "단지 모든 것을 주면 내가 필요한 것을 알아낼 것"이라는 찬성으로 이것을 잊어 버립니다.

예, 모든 열을 처리하는 데 추가 CPU 및 메모리 사용이 있지만이 두 가지에 비해 거의 항상 미미합니다. 필요하지 않은 열에 필요한 추가 디스크 및 네트워크 대역폭이 적고 사용량이 적어야합니다. 모든 열을 포함해야하므로 최적화 된 쿼리 계획

그래서 무엇이 바뀌 었습니까? 기본적으로 SQL Optimizers는 "열 최적화"라는 기능을 성공적으로 통합했습니다. 즉, 쿼리의 상위 수준에서 실제로 열을 사용하려는 경우 하위 수준의 하위 쿼리에서이를 파악할 수 있습니다.

이것의 결론은 쿼리의 낮은 / 내부 레벨에서 'SELECT * ..'를 사용하면 더 이상 중요하지 않다는 것입니다. 대신 실제로 중요한 것은 최상위 SELECT의 열 목록에있는 것입니다. SELECT *..맨 위에서 사용하지 않으면 다시 한 번 모든 열 을 원한다고 가정해야 하므로 열 최적화를 효과적으로 사용할 수 없습니다.

(*- *"*"를 사용할 때 열 목록의 변경 사항을 항상 등록하지 않는 뷰에는 다른 사소한 바인딩 문제 가 있습니다.이를 해결하는 다른 방법이 있으며 성능에는 영향을 미치지 않습니다.


5

사용하지 않는 한 가지 작은 이유가 있습니다 SELECT *. 열의 순서가 변경되면 응용 프로그램이 중단됩니다 ... 행운이 있다면. 그렇지 않은 경우, 오랫동안 감지되지 않을 수있는 미묘한 버그가 있습니다. 테이블의 필드 순서는 구현 세부 사항이며 응용 프로그램에서는 절대로 고려해서는 안됩니다 SELECT *.


4
이것은 관련이 없습니다. 응용 프로그램 코드에서 열 인덱스별로 열에 액세스하는 경우 응용 프로그램이 손상되었을 수 있습니다. 이름으로 열에 액세스하면 항상 훨씬 더 읽기 쉬운 응용 프로그램 코드가 생성되며 성능 병목 현상이 거의 없습니다.
Lie Ryan

3

물리적으로 그리고 문제 적으로 사용할 수 select * from table는 있지만 나쁜 생각입니다. 왜?

우선, 필요하지 않은 열을 반환한다는 것을 알게 될 것입니다 (자원이 무겁습니다).

둘째, *를 선택하면 실제로 데이터베이스에서 열 이름을 선택하고 "다른 목록에 이름이있는 열과 관련된 데이터를 제공하십시오"라고 말하므로 열 이름을 지정하는 것보다 큰 테이블에서 시간이 오래 걸립니다. " 이것은 프로그래머에게는 빠르지 만 문자 그대로 수십만 건의 조회가있는 은행 컴퓨터에서이 조회를 수행한다고 상상해보십시오.

셋째,이 작업을 수행하면 실제로 개발자가 더 어려워집니다. 열 이름을 모두 얻기 위해 얼마나 자주 SSMS에서 VS로 앞뒤로 전환해야합니까?

넷째, 게으른 프로그래밍의 징조이며 어떤 개발자도 그 명성을 원한다고 생각하지 않습니다.


이 현재 형태의 두 번째 주장에는 약간의 실수가 있습니다. 첫째, 모든 RDBMS는 테이블의 스키마를 캐시합니다. 주로 스키마가 쿼리 구문 분석 단계에서로드되어 쿼리에서 테이블에 존재하거나 누락 된 열을 결정하기 때문입니다. 따라서 쿼리 파서는 이미 열 이름 목록을 쿼리하여 *를 열 목록으로 즉시 바꿉니다. 그런 다음 대부분의 RDBMS 엔진은 가능한 모든 것을 캐시하려고 시도하므로 SELECT * FROM 테이블을 발행하면 컴파일 된 쿼리가 캐시되므로 구문 분석이 매번 발생하지 않습니다. 그리고 개발자는 게으르다 :-)
Gabor Garami

두 번째 인수와 관련하여 이것은 일반적인 오해입니다. SELECT *의 문제는 메타 데이터 조회가 아닙니다. 열 이름을 지정해도 SQL Server는 이름을 확인하고 데이터 형식을 확인해야합니다.
Aaron Bertrand

@Gabor SELECT *와 관련된 문제 중 하나는 뷰에 넣을 때 발생합니다. 기본 스키마를 변경하면 뷰가 혼동 될 수 있습니다. 이제 테이블 자체와는 다른 테이블 스키마 (자체) 개념이 있습니다. 나는 이것에 대해 여기서 이야기한다 .
Aaron Bertrand

3

Select * ...앞에서 지적했듯이 데이터베이스는 시간이 지남에 따라 변경되고 쿼리를 작성할 때 예상 한 것보다 많은 열이있을 수 있으므로 프로그램에 코드 를 넣는 경우 문제가 될 수 있습니다 . 이로 인해 프로그램 오류 (가장 좋은 경우)가 발생하거나 프로그램이 처리하기 위해 작성되지 않은 필드 값을보고 있기 때문에 프로그램이 문제가되어 일부 데이터가 손상 될 수 있습니다. 즉, 프로덕션 코드는 항상에서 반환 될 필드를 지정해야합니다 SELECT.

프로그램에 반환 될 모든 것이 선택의 성공 또는 실패를 나타내는 부울이기 때문에 절의 Select *일부 일 때 문제가 적습니다 EXISTS. 다른 사람들은이 입장에 동의하지 않을 수 있으며 이에 대한 의견을 존중합니다. 조항 Select *에서 'Select 1' 을 코딩하는 것보다 코딩하는 것이 약간 덜 효율적일 수 EXISTS있지만 데이터 손상의 위험이 있다고 생각하지 않습니다.


사실, 나는 EXISTS 절을 참조하려고했습니다. 내 실수.
Mark Ross

2

왜 틀린지에 대한 답 select *이 많으므로 그것이 옳거나 이상하다고 느낄 때 다루겠습니다.

1) EXISTS에서는 쿼리의 SELECT 부분 내용이 무시되므로 쓰기도 가능 SELECT 1/0하고 오류가 발생하지 않습니다. EXISTS일부 데이터가 반환되는지 확인하고이를 기반으로 부울을 반환합니다.

IF EXISTS(
    SELECT * FROM Table WHERE X=@Y
)

2) 이로 인해 화재가 발생할 수 있지만 select *히스토리 테이블 트리거에서 사용하는 것이 좋습니다. 에 의해 select *, 기본 테이블에 삽입 / 업데이트 / 삭제 될 때 즉시 오류가 발생하여 기록 테이블에 열을 추가하지 않고 기본 테이블이 새 열을 가져 오는 것을 방지합니다. 이로 인해 개발자가 열을 추가하고 히스토리 테이블에 열을 추가하는 것을 잊어 버린 경우가 여러 번 방지되었습니다.


3
SELECT 1미래 코드 관리자에게 의도를 가장 명확하게 알리기 때문에 여전히 선호 합니다. 그것은 요구 사항 은 아니지만, 필자가 ... WHERE EXISTS (SELECT 1 ...)분명히 그것을 보면 진실 테스트라고 발표합니다.
swasheck

1
많은 사람들 SELECT 1이 성능이 더 나을 것이라는 신화를 기반으로 사용 SELECT *합니다. 그러나 두 옵션 모두 완벽하게 허용됩니다. 옵티마이 저가 EXISTS를 처리하는 방식으로 인해 성능에 차이가 없습니다. 진실 테스트를 명확하게 알리는 단어 "EXISTS"때문에 가독성의 차이도 없습니다.
환멸

2 번 지점에서, 나는 당신의 추론을 이해하지만 여전히 위험이 있습니다. '너희를 위해 시나리오를 그리자'... 개발자가 Column8기록 테이블을 잊어 버린 기본 테이블에 추가 합니다. 개발자는 열 8에 실현 된 많은 코드를 작성합니다. 그런 다음 Column9기본 테이블에 추가 합니다. 이번에는 역사에 추가하는 것을 기억하십시오. 나중에 테스트 할 때, 그는 Column9히스토리 에 추가 하는 것을 잊었 음을 깨닫고 (오류 감지 기술에 감사함) 즉시 추가합니다. 이제 트리거 가 작동하는 것처럼 보이지만 열 8 및 9의 데이터는 히스토리에서 혼합됩니다. : S
Disillusioned

cont ... 요점은 위의 '협의 된'시나리오는 오류 감지 트릭으로 인해 실패하고 실제로 상황을 악화시킬 수있는 많은 시나리오 중 하나 일뿐입니다. 기본적으로 더 나은 기술이 필요합니다. 선택한 테이블의 열 순서에 대해 가정하여 트리거에 의존하지 않는 것입니다. 제안 :-일반적인 실수 점검표가 포함 된 개인 코드 검토. -동료 코드 검토. -히스토리 추적을위한 대체 기술
환멸

@CraigYoung 가능성이 있습니다. 하지만 그렇게하면 누군가를 조절할 것입니다. 그것은 당신이 쉽게 만들 수있는 실수가 아닙니다
UnhandledExcepSean
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.