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.