select *를 사용하지 않는 이유는 무엇입니까?


136

많은 사람들이 선택 쿼리에서 원하는 각 열의 이름을 구체적으로 지정해야한다고 주장했습니다.

어쨌든 모든 열을 사용한다고 가정하면 왜 사용하지 SELECT *않습니까?

심지어 질문을 고려 * SQL 쿼리 - 선택 *보기 또는 선택 COL1, COL2에서 ... colN이보기에서 *, 나는 약간 다른 관점에서 문제에 접근하고있어 같은이 정확한 중복 생각하지 않습니다.

우리의 원칙 중 하나는 시간이 지나기 전에 최적화하지 않는 것입니다. 이를 염두에두고 리소스 문제로 입증되거나 스키마가 거의 설정 될 때까지 사용 SELECT *하는 것이 선호되는 방법 인 것처럼 보입니다 . 우리가 알고 있듯이 개발이 완전히 완료 될 때까지 발생하지 않습니다.

즉, 사용하지 않는 가장 중요한 문제가 SELECT *있습니까?

답변:


168

조기에 최적화하지 않는다는 인용의 본질은 간단하고 간단한 코드를 찾은 다음 프로파일 러를 사용하여 핫스팟을 지적하여 효율적으로 최적화 할 수 있다는 것입니다.

select *를 사용하면 프로파일 링이 불가능하므로 명확하고 간단한 코드를 작성하지 않으므로 인용의 정신에 위배됩니다. select *안티 패턴입니다.


따라서 열 선택은 조기 최적화가 아닙니다. 내 머리 꼭대기에서 몇 가지 ....

  1. SQL 문에 열을 지정하면 해당 열이 테이블에서 제거되고 쿼리가 실행되면 SQL 실행 엔진이 오류를 발생시킵니다.
  2. 해당 열이 사용되는 코드를 더 쉽게 스캔 할 수 있습니다.
  3. 최소한의 정보를 다시 가져 오려면 항상 쿼리를 작성해야합니다.
  4. 서수 열 액세스를 사용하는 경우 다른 사람들이 언급했듯이 select *를 사용해서는 안됩니다.
  5. SQL 문이 테이블을 조인하는 경우 select *는 조인에있는 모든 테이블의 모든 열을 제공합니다.

그 결과는 select *...

  1. 응용 프로그램에서 사용하는 열이 불투명합니다
  2. DBA 및 해당 쿼리 프로파일 러가 응용 프로그램의 성능 저하를 도울 수 없습니다
  3. 변경 사항이 발생하면 코드가 더 부서지기 쉽습니다.
  4. 데이터베이스와 네트워크가 너무 많은 데이터를 가져 오기 때문에 어려움을 겪고 있습니다 (I / O)
  5. 데이터베이스 엔진 최적화는 최소 (논리적)에 관계없이 모든 데이터를 다시 가져 오므로 최소화됩니다.

올바른 SQL을 작성하는 것은 작성하는 것만 큼 쉽습니다 Select *. 실제 게으른 사람은 코드를 다시 방문하지 않고 코드를 다시 작성하고 싶지 않았기 때문에 적절한 SQL을 작성합니다. 그들은 모든 코드 비트에 대해 DBA에 설명하고 싶지 않습니다. 그들은 왜 응용 프로그램이 개처럼 실행되는지 고객에게 설명하고 싶지 않습니다.


2
첫 번째 섹션에서 포인트 # 5는 "select *는 조인에있는 모든 테이블의 모든 을 제공합니다"로 표시되어야합니다 . 두 번째 섹션에서 포인트 # 2와 # 5는 반드시 사실 일 필요는 없으며 "select *"를 사용하지 않는 이유로 표시되어서는 안됩니다.
jimmyorr

1
@uglysmurf-수정에 감사하지만 2 & 5와 관련하여 모든 데이터베이스 / dba에 반드시 해당되는 것은 아니지만 대부분의 경우 중요하고 유효하다고 생각합니다. 'select *'를 사용하면 dba의 작업이 쉬워지지 않습니다.
Robert Paulson

11
# 3 (Brittle code)이 실제로는 아니라고 주장합니다. 구현에 따라 Select *는 덜 부서지기 쉽지만 어떻게 그렇게 될 수 있는지 모르겠습니다.
JohnFx

2
@ JohnFx, 취성을 다르게 정의한다고 생각합니다. 취성은 일반적으로 '쉽게 깨짐'으로 정의됩니다. 각 코드가 다른 열을 사용하기 때문에 알 수 없거나 찾기 어려운 종속성이 있으면 전체 회귀없이 데이터 수준에서 아무것도 쉽게 변경할 수 없다는 것을 의미합니다.
Robert Paulson

9
@mavnn, wrt 취성, 이것이 이것이 취성이라는 단어의 선택에 대한 의미 론적 문제로 전개되는 것을 두려워합니다. 마지막 말은 어쨌든 별 차이가 없다고 말하는 것입니다. 유일한 시나리오는 이름이 바뀌거나 제거 된 열입니다. sql이 실행될 때 (명시 적) 중단과 결과가 소비 될 때의 중단을 이동하고 있습니다. 쿼리 결과가 소비되는 방식은 다양 할 수 있으며 코드는 자동으로 실패하거나 실패하지 않지만 SQL 실행 엔진은 유효하지 않은 SQL로 실패합니다. 그래서 *가 도움이 되었습니까? DB 문제의 경우 DB에 가까운 IMO 명시 적 실패가 더 좋습니다. Thx
Robert Paulson

42

코드가 특정 순서의 열에 의존하는 경우 테이블이 변경되면 코드가 중단됩니다. 또한 *를 선택할 때 특히 테이블에 이진 필드가있는 경우 테이블에서 너무 많은 페치가 발생할 수 있습니다.

지금 모든 열을 사용한다고해서 다른 사람이 테이블에 추가 열을 추가하지 않는다는 의미는 아닙니다.

또한 테이블에 대한 메타 데이터를 가져 와서 *에있는 열을 알아야하기 때문에 계획 실행 캐싱에 오버 헤드를 추가합니다.


4
좋은 답변이지만 "코드가 깨질 것"을 "코드가 깨질 수도 있습니다"로 변경하겠습니다. "select *"사용이 항상 중대한 변화를 일으키지는 않습니다. 그리고 중단이 발생하면 일반적으로 사용 중단과 크게 분리됩니다.
BQ.

4
누군가 코드에서 일반적으로 열을 참조하는 경우 SELECT * 사용 여부에 관계없이 문제가 있습니다. 계획 실행 오버 헤드는 사소한 것이며 계획을 캐시 한 후에는 중요하지 않습니다.
MusiGenesis

1
그런 다음 프로그래머 오류는 열 순서에 따라 코드를 작성하는 데 있습니다. 당신은 그렇게 할 필요가 없습니다.
dkretz

1
@doofledorfer-절대 말하지 마십시오. 서수 열에 액세스하는 것이 더 빠르며 때로는 실용적입니다. 서수 액세스를 사용하는 것보다 select *를 사용하는 것이 더 큰 오류입니다.
Robert Paulson

23

주요한 이유 중 하나는 테이블에서 열을 추가 / 제거 할 경우 SELECT * 호출을 수행하는 모든 쿼리 / 프로 시저가 예상보다 많은 데이터 열을 가져 오는 것입니다.


3
어쨌든 반환 된 열 수에 따라 코드를 작성해서는 안됩니다.
dkretz

4
그러나 모든 사람은 프로그래머가 어떤 데이터가 되돌아오고 있는지 알아야하는 코드를 작성하고 있습니다. 열 이름이 SELECT *에 숨겨져 있으면 Ctrl + F 할 수 없습니다.
로터스 노트

17
  1. 로터리 방식으로 가능한 한 엄격한 타이핑을 사용하는 것에 대한 모듈화 규칙을 위반하고 있습니다. 명시 적으로 거의 보편적으로 좋습니다.

  2. 이제 테이블의 모든 열이 필요하더라도 나중에 쿼리를 실행할 때마다 풀다운되어 성능이 저하 될 수있는 추가 열이 추가 될 수 있습니다. 성능이 저하되므로

    • 와이어를 통해 더 많은 데이터를 가져오고 있습니다. 과
    • 테이블 자체에서 조회를 수행하는 대신 인덱스에서 데이터를 바로 가져 오는 옵티마이 저의 기능 (인덱스의 일부인 열에 대한 쿼리의 경우)을 무시할 수 있기 때문입니다.

사용할 때 선택 *

QUERY를 기록 할 당시 존재했던 테이블의 모든 열이 필요하지 않고, 테이블의 모든 열을 명시 적으로 필요로하는 경우. 예를 들어, 테이블의 전체 내용을 표시하는 데 필요한 DB 관리 앱을 작성하는 경우 (어떤 일이 있었 든) 해당 방식을 사용할 수 있습니다.


1
또 다른 시간 SELECT *은 db 클라이언트를 사용하여 테스트 쿼리를 할 때입니다.
cdmckay

질문의 맥락에서 볼 때 이상한 예외처럼 보입니다. 일부 타이핑을 저장하는 것 외에 테스트 쿼리에서이 작업을 수행하면 어떤 이점이 있습니까?
JohnFx

또한 SELECT * FROM (SELECT a, b, c FROM 테이블)은 정상입니다.
kmkaplan

12

몇 가지 이유가 있습니다.

  1. 데이터베이스의 열 수가 변경되고 응용 프로그램에 특정 수가있을 것으로 예상되는 경우 ...
  2. 데이터베이스의 열 순서가 변경되고 응용 프로그램에서 특정 순서를 기대하는 경우 ...
  3. 메모리 오버 헤드. 8 개의 불필요한 INTEGER 열은 32 바이트의 낭비 된 메모리를 추가합니다. 그것은별로 들리지 않지만, 이것은 각 쿼리에 대한 것이고 INTEGER는 작은 열 유형 중 하나입니다 ... 추가 열은 VARCHAR 또는 TEXT 열일 가능성이 더 높으므로 더 빨리 추가됩니다.
  4. 네트워크 오버 헤드. 메모리 오버 헤드와 관련하여 : 30,000 개의 쿼리를 발행하고 8 개의 불필요한 INTEGER 열이 있으면 960kB의 대역폭을 낭비했습니다. VARCHAR 및 TEXT 컬럼이 상당히 클 수 있습니다.

참고 : 위의 예제에서는 고정 크기가 4 바이트이므로 INTEGER를 선택했습니다.


1과 2는 코드 냄새와 조기 최적화와 같은 3과 4 사운드입니다
NikkyD

7

응용 프로그램에서 SELECT *를 사용하여 데이터를 가져오고 데이터베이스의 테이블 구조가 변경되면 (예 : 열 제거) 누락 된 필드를 참조하는 모든 위치에서 응용 프로그램이 실패합니다. 대신 쿼리에 모든 열을 포함 시키면 응용 프로그램이 처음에 데이터를 얻는 위치 (바람직하게)에서 깨져서 수정이 쉬워집니다.

즉, SELECT *가 바람직한 여러 가지 상황이 있습니다. 하나는 항상 전체 테이블을 다른 데이터베이스 (예 : SQL Server에서 DB2로)로 복제해야하는 상황입니다. 다른 하나는 일반적으로 테이블을 표시하도록 작성된 응용 프로그램입니다 (즉, 특정 테이블에 대한 지식이 없음).


이 질문은 '선택적이지 않습니다'이므로 답의 두 번째 부분은 관련이 없습니다. 질문은 'select *'를 사용하는 것이 바람직해야하며, 이는 완전한 볼록입니다.
Robert Paulson

예, 두 번째 부분은 관련이 없습니다. OQ는 질문을 SELECT * 상태로 바 꾸었습니다. 그렇습니다.
MusiGenesis

아 예 죄송합니다-질문은 답변 후 방향이 바뀌 었습니다.
Robert Paulson

그 괜찮아요. 모차르트조차도 편집자였습니다 ( stackoverflow.com/questions/292682/… ). 내 원래 게시물은 SELECT *를 사용하면 식인 풍습으로 이어 졌다고 제안했습니다. :)
MusiGenesis

3

실제로 select *SQL Server 2005의 뷰에서 사용할 때 이상한 동작을 발견했습니다 .

다음 쿼리를 실행하면 무슨 뜻인지 알 수 있습니다.

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[starTest]') AND type in (N'U'))
DROP TABLE [dbo].[starTest]
CREATE TABLE [dbo].[starTest](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [A] [varchar](50) NULL,
    [B] [varchar](50) NULL,
    [C] [varchar](50) NULL
) ON [PRIMARY]

GO

insert into dbo.starTest
select 'a1','b1','c1'
union all select 'a2','b2','c2'
union all select 'a3','b3','c3'

go
IF  EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[vStartest]'))
DROP VIEW [dbo].[vStartest]
go
create view dbo.vStartest as
select * from dbo.starTest
go

go
IF  EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[vExplicittest]'))
DROP VIEW [dbo].[vExplicittest]
go
create view dbo.[vExplicittest] as
select a,b,c from dbo.starTest
go


select a,b,c from dbo.vStartest
select a,b,c from dbo.vExplicitTest

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[starTest]') AND type in (N'U'))
DROP TABLE [dbo].[starTest]
CREATE TABLE [dbo].[starTest](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [A] [varchar](50) NULL,
    [B] [varchar](50) NULL,
    [D] [varchar](50) NULL,
    [C] [varchar](50) NULL
) ON [PRIMARY]

GO

insert into dbo.starTest
select 'a1','b1','d1','c1'
union all select 'a2','b2','d2','c2'
union all select 'a3','b3','d3','c3'

select a,b,c from dbo.vStartest
select a,b,c from dbo.vExplicittest

마지막 2 개의 select 문의 결과를 비교하십시오. 나는 당신이 볼 것의 결과는 이름 대신 인덱스로 열을 참조 Select * .

뷰를 다시 빌드하면 다시 정상적으로 작동합니다.

편집하다

SQL Server 2005의 흥미로운 동작 인“테이블에서 선택 *”과“테이블에서 colA, colB 등 선택”과 같은 별도의 질문을 추가했습니다 .


2

두 테이블을 조인하고 두 번째 테이블의 열 A를 사용할 수 있습니다. 나중에 A 열을 첫 번째 테이블에 추가하면 (같은 이름이지만 다른 의미 일 수 있음) 첫 번째 테이블에서 값을 얻을 수있을 것입니다. 선택하려는 열을 명시 적으로 지정하면 발생하지 않습니다.

물론 모든 select 절에 새 열을 추가하는 것을 잊어 버린 경우 열을 지정하면 종종 버그가 발생합니다. 쿼리가 실행될 때마다 새 열이 필요하지 않으면 버그가 발견되기까지 시간이 걸릴 수 있습니다.


2

조기 최적화와 관련하여 어디로 가고 있는지 이해하지만 실제로는 한 단계에 지나지 않습니다. 의도는 불필요한 것을 피하는 것입니다 처음에 최적화 입니다. 테이블의 색인이 생성되지 않습니까? nvarchar (4000)을 사용하여 우편 번호를 저장 하시겠습니까?

다른 사람들이 지적했듯이 쿼리에 사용하려는 각 열을 지정하면 유지 관리 성과 같은 다른 긍정적 인 요소가 있습니다.


2

열을 지정할 때 특정 열 집합에 자신을 묶고 유연성을 떨어 뜨리므로 Feuerstein이 어디에서든 롤오버 할 수 있습니다. 그냥 생각이야


1
나는 Feuerstein이 누구인지 전혀 모른다. 인터넷 검색을 시도하고 심리학자, 텔레비전 캐릭터 및 블로거를 찾았습니다.
NotMe

PL / SQL에 관한 O'Reilly 책의 저자. "feuerstein"대신 "feuerstein sql"인터넷 검색을 시도하십시오.
orbfish

2

SELECT * 가 항상 악한 것은 아닙니다. 제 생각에는 적어도. 전체 테이블과 일부 계산 필드를 반환하는 동적 쿼리에 자주 사용합니다.

예를 들어, "일반"테이블에서 지오메트리를 계산하려고합니다. 즉, 지오메트리 필드는 없지만 좌표를 포함하는 필드가있는 테이블입니다. postgresql과 공간 확장 postgi를 사용합니다. 그러나 원칙은 다른 많은 경우에도 적용됩니다.

예를 들면 :

  • x, y, z로 표시된 필드에 좌표가 저장된 장소 테이블 :

    CREATE TABLE 자리 (place_id 정수, x 숫자 (10, 3), y 숫자 (10, 3), z 숫자 (10, 3), 설명 varchar);

  • 몇 가지 예제 값으로 공급해 보겠습니다.

    장소에 삽입 (place_id, x, y, z, 설명) 값
    (1, 2.295, 48.863, 64, '파리, Place de l \'Étoile '),
    (2, 2.945, 48.858, 40,'파리, 에펠 투어 '),
    (3, 0.373, 43.958, 90,'콘돔, Cathédrale St-Pierre ');

  • 일부 GIS 클라이언트를 사용하여이 테이블의 내용을 매핑 할 수 있기를 원합니다. 일반적인 방법은 테이블에 형상 필드를 추가하고 좌표를 기반으로 형상을 작성하는 것입니다. 그러나 동적 쿼리를 선호합니다.이 방법으로 좌표 (수정, 정확도 등)를 변경하면 매핑 된 객체가 실제로 동적으로 이동합니다. 다음은 SELECT *를 사용한 쿼리입니다 .

    뷰 생성 또는 교체 place_points AS
    SELECT *,
    GeomFromewkt ( 'SRID = 4326; POINT ('|| x || ''|| y || ''|| z || ')')
    FROM 플레이스;

    GeomFromewkt () 함수 사용에 대해서는 postgis를 참조하십시오.

  • 결과는 다음과 같습니다.

    places_points에서 선택 *;

place_id | x | y | z | 설명 | geomfromewkt                            
---------- + ------- + -------- + ---------- + --------------- ----------------- + -------------------------------- ------------------------------------  
        1 | 2.295 | 48.863 | 64.000 | 파리, Place de l' Étoile | 01010000A0E61000005C8FC2F5285C02405839B4C8766E48400000000000005040  
        2 | 2.945 | 48.858 | 40.000 | 파리, 에펠 탑 | 01010000A0E61000008FC2F5285C8F0740E7FBA9F1D26D48400000000000004440
        3 | 0.373 | 43.958 | 90.000 | 콘돔, Cathédrale St-Pierre | 01010000A0E6100000AC1C5A643BDFD73FB4C876BE9FFA45400000000000805640
(3 리그)

이제 GIS 프로그램에서 가장 오른쪽 열을 사용하여 점을 올바르게 매핑 할 수 있습니다.

  • 앞으로 일부 필드가 테이블에 추가되면 걱정할 필요가 없으며 동일한 VIEW 정의를 다시 실행하면됩니다.

VIEW의 정의가 *와 함께 "있는 그대로"유지 될 수 있기를 바라지 만, 실제로는 그렇지 않습니다. postgresql에 의해 내부적으로 저장되는 방식입니다.

places.place_id, places.x, places.y, places.z, places.description, geomfromewkt (((((((((((((( '(:( text : || 텍스트 || places.x) || places.x) ||') :) '': : text) || places.y) || '':: text) || places.z) || ')':: text) AS geomfromewkt FROM 플레이스;


1

모든 열을 사용하지만 숫자 인덱스로 행 배열을 지정하더라도 나중에 다른 행을 추가하면 문제가 발생합니다.

따라서 기본적으로 유지 관리의 문제입니다! * 선택기를 사용하지 않으면 쿼리에 대해 걱정할 필요가 없습니다.


1

필요한 열만 선택하면 메모리의 데이터 집합이 더 작게 유지되므로 응용 프로그램의 속도가 빨라집니다.

또한 많은 도구 (예 : 저장 프로 시저)도 쿼리 실행 계획을 캐시합니다. 나중에 열을 추가하거나 제거하면 (특히보기에서 선택하는 경우 특히 쉬움) 도구가 예상 한 결과를 다시 얻지 못할 때 종종 오류가 발생합니다.


1

코드를 더 모호하고 유지하기가 더 어려워집니다. 사용하지 않는 데이터를 도메인에 추가하고 있기 때문에 의도 한 것과 그렇지 않은 데이터가 명확하지 않기 때문입니다. (또한 알지 못하거나 돌보지 않을 수도 있음을 제안합니다.)


1

질문에 직접 대답하려면 다음을 수행하십시오. "SELECT *"를 사용하면 코드가 기본 테이블의 변경 사항을보다 복잡하게 만들 수 있습니다. 프로그램의 요구 사항에 직접 영향을주는 테이블을 변경 한 경우에만 코드가 깨져야합니다.

애플리케이션은 관계형 액세스가 제공하는 추상화 계층을 활용해야합니다.


1

나는 어떤 필드를 검색하고 있는지 알고 알고 있기 때문에 단순히 SELECT *를 사용하지 않습니다.


1

테이블 열 변경시 뷰를 다시 컴파일해야하므로 일반적으로 뷰 내에서 'select *'를 사용하는 것은 좋지 않습니다. 뷰의 기본 테이블 열을 변경하면 돌아가서 다시 컴파일 할 때까지 존재하지 않는 열에 오류가 발생합니다.


1

exists(select * ...)결코 확장되지 않기 때문에 할 때 괜찮 습니다. 그렇지 않으면 임시 select 문이있는 테이블을 탐색하거나 위에서 정의한 CTE가 있고 모든 열을 다시 입력하지 않고 모든 열을 원하는 경우에만 유용합니다.


1

아무도 언급하지 않은 것을 추가하십시오. Select *모든 열을 반환합니다. 누군가 열을 추가하면 나중에 사용자가 데이터를 마지막으로 업데이트 한 사람이나 타임 스탬프 또는 관리자 만 모든 사용자를 볼 수 없다는 메모 등 사용자가 볼 수있는 것을 원하지 않을 수 있습니다.

또한 열을 추가 할 때 기존 코드에 미치는 영향을 검토하고 열에 저장된 정보에 따라 변경이 필요한지 검토해야합니다. 를 사용 select *하면 개발자가 아무것도 깨지지 않을 것이라고 가정하기 때문에 해당 검토를 종종 건너 뜁니다. 실제로 실제로는 아무것도 깨지는 것처럼 보이지 않지만 쿼리는 이제 잘못된 것을 반환하기 시작할 수 있습니다. 명시 적으로 중단되는 것이 없다고해서 쿼리가 변경되어서는 안된다는 의미는 아닙니다.


0

"select *"는 모든 필드가 필요하지 않을 때 메모리를 낭비하기 때문입니다. 그러나 SQL Server의 경우 성능이 동일합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.