어제 나는 "취미"프로그래머 (나 자신은 전문 프로그래머이다)와 논의하고 있었다. 우리는 그의 작업 중 일부를 발견했으며, 그는 항상 데이터베이스의 모든 열 (운영 서버 / 코드의 / 서버)을 쿼리한다고 말했습니다.
나는 그렇게하지 말라고 설득했지만 아직 성공하지는 못했다. 제 생각에는 프로그래머는 "예쁜", 효율성 및 트래픽을 위해 실제로 필요한 것을 쿼리해야합니다. 내 견해에 착각하고 있습니까?
어제 나는 "취미"프로그래머 (나 자신은 전문 프로그래머이다)와 논의하고 있었다. 우리는 그의 작업 중 일부를 발견했으며, 그는 항상 데이터베이스의 모든 열 (운영 서버 / 코드의 / 서버)을 쿼리한다고 말했습니다.
나는 그렇게하지 말라고 설득했지만 아직 성공하지는 못했다. 제 생각에는 프로그래머는 "예쁜", 효율성 및 트래픽을 위해 실제로 필요한 것을 쿼리해야합니다. 내 견해에 착각하고 있습니까?
답변:
돌아 오는 내용과 코드의 변수에 어떻게 바인딩하는지 생각해보십시오.
이제 누군가가 직접 사용하지 않는 열을 추가하거나 제거하기 위해 테이블 스키마를 업데이트하면 어떻게 될지 생각하십시오.
코드 쿼리를 작성할 때가 아니라 직접 쿼리를 입력 할 때 select *를 사용하는 것이 좋습니다.
foo
있고 쿼리의 다른 테이블이 열을 추가 foo
하면 올바른 foo
열 을 가져 오려고 할 때 문제가 발생할 수 있습니다 .어느 쪽이든 스키마 변경으로 인해 데이터 추출에 문제가 발생할 수 있습니다.
사용중인 컬럼이 테이블에서 제거 되는지 추가로 고려 하십시오. 는 select * from ...
결과 집합에서 데이터를 끌어하려고 할 때 여전히 오류가 밖으로 제외한 작동합니다. 열이 쿼리에 지정되면 쿼리 는 오류가 발생하고 대신 문제의 위치와 위치를 명확하게 표시합니다.
일부 열에는 많은 양의 데이터가 연결될 수 있습니다. 다시 선택하면 모든 데이터 *
를 가져옵니다 . 그렇습니다. 여기서 선택한 1000 행의 데이터는 필요하지 않은 추가 4 메가 바이트의 데이터를 제공하지만 어쨌든 와이어를 통해 전송됩니다.varchar(4096)
스키마 변경과 관련하여 테이블을 처음 만들 때 해당 varchar가 존재하지 않을 수 있습니다.
다시 선택하여 *
20 개의 열을 가져 오지만 그 중 2 개만 필요한 경우 코드의 의도를 전달하지 않습니다. 쿼리를 수행하는 쿼리를 볼 때 쿼리 select *
의 중요한 부분이 무엇인지 모릅니다. 이 열을 포함시키지 않으면 서 다른 계획을 대신 사용하도록 쿼리를 변경할 수 있습니까? 쿼리가 반환하는 의도가 명확하지 않기 때문에 모르겠습니다.
이러한 스키마 변경 사항 을 조금 더 살펴 보는 일부 SQL 바이올린을 살펴 보겠습니다 .
먼저 초기 데이터베이스 : http://sqlfiddle.com/#!2/a67dd/1
DDL :
create table one (oneid int, data int, twoid int);
create table two (twoid int, other int);
insert into one values (1, 42, 2);
insert into two values (2, 43);
SQL :
select * from one join two on (one.twoid = two.twoid);
그리고 다시 얻을 열은 oneid=1
, data=42
, twoid=2
,와 other=43
.
이제 테이블 1에 열을 추가하면 어떻게됩니까? http://sqlfiddle.com/#!2/cd0b0/1
alter table one add column other text;
update one set other = 'foo';
그리고 같은 쿼리 내 결과는 이전이다 oneid=1
, data=42
, twoid=2
,와 other=foo
.
테이블 중 하나를 변경하면 a 값이 중단 select *
되고 갑자기 'other'를 int에 바인딩하면 오류가 발생하고 이유를 알 수 없습니다.
대신 SQL 문이
select
one.oneid, one.data, two.twoid, two.other
from one join two on (one.twoid = two.twoid);
표 1로 변경해도 데이터가 중단되지 않았습니다. 해당 쿼리는 변경 전과 변경 후 동일하게 실행됩니다.
당신이 할 때 조건과 일치하는 모든 테이블을 형성하는 모든 행을 select * from
당기는 것 입니다. 당신이 정말로 신경 쓰지 않는 테이블조차도. 이것은 더 많은 데이터가 전송됨을 의미하지만 스택 아래로 더 많은 성능 문제가 있습니다.
인덱스. (SO 관련 : select 문에서 인덱스를 사용하는 방법은 무엇입니까? )
많은 열을 철회하는 경우 데이터베이스 계획 최적화 프로그램 은 여전히 모든 열을 가져와야하고 인덱스를 사용한 다음 쿼리의 모든 열을 가져 오는 데 더 많은 시간이 걸리므로 인덱스 사용을 무시할 수 있습니다. 완전한 테이블 스캔을 수행하는 것보다.
예를 들어 사용자의 성 (많은 일을하고 색인을 가지고있는)을 선택하는 경우 데이터베이스는 색인 만 스캔 ( postgres wiki index only scan , mysql full table scan vs full)을 수행 할 수 있습니다 인덱스 스캔 , 인덱스 전용 스캔 : 테이블 액세스 방지 ).
가능한 경우 인덱스에서만 읽기에 대한 약간의 최적화가 있습니다. 각 색인 페이지에서 정보를 더 빨리 가져올 수 있습니다. 정보를 덜 가져 오기 때문 select *
입니다.에 대한 다른 모든 열을 가져 오지는 않습니다 . 인덱스 만 스캔하면 100 배 빠른 결과를 반환 할 수 있습니다 (출처 : Select *가 잘못되었습니다 ).
이것은 전체 인덱스 스캔이 훌륭하지만 여전히 전체 스캔이지만 전체 테이블 스캔보다 낫다는 것을 말하는 것은 아닙니다. select *
성능을 저하 시키는 모든 방법을 쫓기 시작하면 새로운 것을 계속 찾으십시오.
또 다른 관심사 : JOIN
쿼리 인 경우 쿼리 결과를 연관 배열로 검색하는 경우 (PHP의 경우와 같이) 버그가 발생하기 쉽습니다.
문제는
foo
에 열이 id
있고name
bar
에 열이 id
있고 address
,SELECT * FROM foo
JOIN bar ON foo.id = bar.id
누군가가 테이블에 열 name
을 추가하면 어떻게 될지 추측 bar
하십시오.
이제 때문에 코드가 갑자기 제대로 작동이 중지됩니다 name
열은 결과에 표시 두 번 하고 배열로 결과를 저장하는 경우, 두 번째 데이터는 name
( bar.name
) 첫 덮어 쓰게됩니다 name
( foo.name
)!
그것은 명백하지 않기 때문에 상당히 불쾌한 버그입니다. 알아내는 데 시간이 걸릴 수 있으며 테이블에 다른 열을 추가하는 사람이 바람직하지 않은 부작용을 예상 할 수있는 방법이 없습니다.
(진실한 이야기).
따라서을 사용하지 말고 *
검색중인 열을 제어하고 적절한 경우 별명을 사용하십시오.
SELECT
절에 다른 열을 추가하기 위해 쿼리로 이동해야합니다 . 이름이 고유하지 않다는 것을 알 수 있습니다. BTW 데이터베이스가 큰 시스템에서는 그렇게 드물지 않다고 생각합니다. 내가 말했듯이, 한 번 PHP 코드의 큰 머드 볼 에서이 버그를 사냥하는 데 두 시간을 보냈습니다. 그리고 지금 또 다른 사례를 발견했습니다. stackoverflow.com/q/17715049/168719
대부분의 경우 모든 열을 쿼리하는 것이 완벽 할 수 있습니다.
항상 모든 열을 쿼리하는 것은 아닙니다.
실제로 데이터를 가져 와서 다시 전송하는 실제 비즈니스에 착수하기 전에 처리해야하는 열을 해결하기 위해 내부 메타 데이터를 둘러보아야합니다. 그것은 세계에서 가장 큰 오버 헤드는 아니지만 시스템 카탈로그는 병목 현상이 될 수 있습니다.
하나 또는 두 개만 원할 경우 여러 필드 를 철회하기 때문에 네트워크에 더 효과적입니다 . 누군가 [else]가 많은 텍스트 필드를 포함하는 수십 개의 추가 필드를 추가하면 처리량이 갑자기 바닥을 통과합니다. 명백한 이유는 없습니다. "where"절이 특히 좋지 않고 많은 행을 철회하는 경우 상황이 더 나빠질 수 있습니다. 이는 잠재적으로 네트워크를 가로 질러 길을 가로막는 많은 데이터입니다 (즉, 느려질 것입니다).
응용 프로그램에서 더 많은 작업을 수행하므로 관심이없는이 추가 데이터를 모두 가져 와서 저장해야합니다.
열 순서가 변경 될 위험이 있습니다. 좋아, 당신은 이것에 대해 걱정할 필요 가 없습니다 (그리고 당신 이 필요한 열만 선택해도 안됩니다 ). 그러나 당신이 한꺼번에 가져 가면 누군가 [else]는 테이블 내에서 열 순서를 재 배열하기로 결정합니다. , 신중하게 제작 된 CSV 내보내기는 복도로 내려가는 계정에 갑자기 명백한 이유없이 갑자기 다시 사라집니다.
BTW, 나는 위에서 "누군가 [다른 사람]"라고 말했습니다. 데이터베이스는 본질적으로 다중 사용자라는 것을 기억하십시오. 당신이 생각하는 그들에 대한 통제권이 없을 수도 있습니다.
TOP
제한 을 추가했습니다 . 코드를 표시하고 쿼리를 처리하는 데 필요한만큼 코드를 읽는 것이 얼마나 중요한지 잘 모르겠습니다. 세부 사항을 모르지만 쿼리 응답이 다소 느리게 처리됩니다. 어쨌든, 나는 그것이 "적법하지 않다"고 말하는 것이 아니라 "... 훨씬 더 적법하다"고 말하는 것이 낫다고 생각합니다 . 기본적으로, 나는 합법적 인 사례를 사용자가 프로그래머보다 의미있는 것이 더 나은 아이디어를 갖는 사례로 요약했습니다.
짧은 대답은 사용하는 데이터베이스에 따라 다릅니다. 관계형 데이터베이스는 필요한 데이터를 빠르고 안정적이며 원자 적 으로 추출 할 수 있도록 최적화되었습니다 . 큰 데이터 세트와 복잡한 쿼리에서는 SELECTing *보다 훨씬 빠르고 안전하며 '코드'측에서 조인과 동일합니다. 키-값 저장소에는 이러한 기능이 구현되지 않았거나 프로덕션에 사용할만큼 성숙하지 않을 수 있습니다.
즉, SELECT *로 사용중인 데이터 구조를 채우고 나머지 코드를 해결할 수는 있지만 확장하려는 경우 성능 병목 현상이 발생합니다.
가장 가까운 비교는 데이터 정렬입니다. quicksort 또는 bubblesort를 사용할 수 있으며 결과는 정확합니다. 그러나 최적화되지는 않으며 동시성을 도입하고 원자 적으로 정렬해야 할 때 문제가 발생합니다.
물론 SQL 쿼리를 수행 할 수 있고 JOIN이 무엇인지 모호한 프로그래머에게 투자하는 것보다 RAM과 CPU를 추가하는 것이 더 저렴합니다.
Customer customer = this._db.Customers.Where( “it.ID = @ID”, new ObjectParameter( “ID”, id ) ).First();
대해서는 where 절에 대해 이것이 합리적이거나 우아하다고 확신 할 수 없습니다 . 2 페이지의 위반 시간 참조
var cmd = db.CreateCommand(); cmd.CommandText = "SELECT TOP 1 * FROM Customers WHERE ID = @ID"; cmd.Parameters.AddWithValue("@ID", id); var result = cmd.ExecuteReader();
.... 그런 다음 각 행에서 고객을 작성하는 것입니다. LINQ가 바지를 이겼습니다.
var customer = _db.Customers.Where(it => it.id == id).First();
있습니다.
IMO는 명시 적이거나 암시 적입니다. 코드를 작성할 때 모든 부분이 거기에 있기 때문에 코드가 작동하기 때문에 코드가 작동하기를 원합니다. 모든 레코드를 쿼리하고 코드가 작동하면 계속 진행하는 경향이 있습니다. 나중에 무언가가 변경되고 코드가 작동하지 않으면 거기에 있어야 할 값을 찾는 많은 쿼리와 함수를 디버깅하는 데 큰 어려움이 있으며 유일한 값 참조는 *입니다.
또한 N 계층 접근 방식에서는 데이터베이스 스키마 중단을 데이터 계층으로 격리하는 것이 가장 좋습니다. 데이터 계층이 비즈니스 로직으로 전달되고 프레젠테이션 계층에서 가장 많이 전달되는 경우 디버깅 범위가 기하 급수적으로 확장됩니다.
select *
이 훨씬 나쁩니다!
오버 헤드와는 별도로, 우선 피하고 싶은 것은 프로그래머로서 데이터베이스 관리자가 정의한 열 순서에 의존하지 않는다고 말할 수 있습니다. 필요한 경우에도 각 열을 선택합니다.
나는 당신이 그것을 빌드 목적으로 사용해서는 안되는 이유를 보지 못합니다-데이터베이스에서 모든 열을 검색하십시오. 세 가지 경우가 있습니다.
데이터베이스에 열이 추가되고 코드에서도 열이 필요합니다. a) *를 사용하면 적절한 메시지가 표시되지 않습니다. b) *가 없으면 작동하지만 예상 한 것을 수행하지 않습니다.
열은 데이터베이스에 추가되며 코드에서는 원하지 않습니다. a) *로 실패합니다. 이는 의미론이 "모두 검색"을 의미하므로 *가 더 이상 적용되지 않음을 의미합니다. b) *가 없으면 작동합니다.
열이 제거되었습니다. 코드는 어느 쪽이든 실패합니다.
이제 가장 일반적인 경우는 case 1입니다 (*를 사용했기 때문에 모든 것을 원할 것입니다). * 없이는 잘 작동하는 코드를 가질 수 있지만 적절한 오류 메시지와 함께 실패하는 코드보다 훨씬 최악의 코드는 예상대로 수행하지 않습니다 .
내 생각에 오류가 발생하기 쉬운 열 인덱스를 기반으로 열 데이터를 검색하는 코드는 고려하지 않습니다. 열 이름을 기준으로 검색하는 것이 훨씬 더 논리입니다.
Select *
응용 프로그램 개발 목적이 아닌 임시 쿼리의 편의를 위해 더욱 의도되었습니다. 또는 select count(*)
쿼리 엔진이 인덱스 사용 여부, 사용할 인덱스 등을 결정하고 실제 열 데이터를 반환하지 않는 것과 같은 통계적 구성 에 사용합니다. 또는 같은 절에서 사용하기 위해 where exists( select * from other_table where ... )
다시 쿼리 엔진에 가장 효율적인 경로를 선택하도록 초대하고 하위 쿼리는 기본 쿼리의 결과를 제한하는 데만 사용됩니다. 기타
select *
모든 열을 검색하는 의미가 있다고 생각 합니다. 귀하의 응용 프로그램에 실제로 이것이 필요한 경우 사용하지 않는 이유가 없습니다. select *
빌드 의 목적 이 모든 열을 검색하는 것이 아니라고 언급 한 일부 참조 (Oracle, IBM, Microsoft 등)를 가리킬 수 있습니까 ?
select *
모든 열을 검색하는 것이 편리합니다. 편의 쿼리는 임시 쿼리를위한 것입니다. 프로덕션 소프트웨어에서 좋은 아이디어가 아니기 때문입니다. 그 이유는이 페이지의 답변에서 이미 잘 설명되어 있습니다. 따라서 제가 직접 답을 만들지 않은 이유는 다음과 같습니다. •) 성능 문제, 사용하지 않는 네트워크를 통해 데이터를 반복적으로 마샬링 •) 쿼리 계획 최적화 실패 (일부 경우 인덱스 사용 실패), •) 제한된 선택 만 인덱스 등을 사용할 수있는 경우 비효율적 인 서버 I / O
select *
실제 생산 애플리케이션에서 의 사용을 정당화하는 엣지 케이스가있을 수도 있지만 엣지 케이스 의 특성은 일반적인 경우 가 아닙니다 . :-)
select *
. 당신이 정말로 모든 컬럼을 필요로한다면 내가 말한 것, 왜 당신이 사용하지 말아야 할 이유가 없습니다 select *
. 모든 열이 필요한 시나리오는 거의 없지만.
이 방법으로 생각하십시오 ... 작은 문자열이나 숫자 필드가 몇 개있는 테이블에서 총 100k 데이터의 모든 열을 쿼리하는 경우. 나쁜 연습이지만 수행합니다. 이제 이미지 또는 10MB 단어 문서를 포함하는 단일 필드를 추가하십시오. 이제 필드가 테이블에 추가 되었기 때문에 빠른 수행 쿼리가 즉시 신비롭게 수행되기 시작합니다. 거대한 데이터 요소가 필요하지 않을 수도 있지만 Select * from Table
어쨌든 가져 왔기 때문 입니다.