사람들이 왜 SQL 커서를 너무 싫어합니까? [닫은]


127

오버 헤드와 불편으로 인해 커서를 사용하지 않으려는 것을 이해할 수는 있지만 사람들이 커서를 사용하지 않기 위해 많은 시간을 할애하는 심각한 커서 공포증이있는 것처럼 보입니다.

예를 들어, 한 질문은 커서로 명백히 사소한 작업을 수행하는 방법을 물었고 재귀 사용자 정의 함수가있는 공통 테이블 표현식 (CTE) 재귀 쿼리를 사용하여 제안 된 대답은 32로 처리 할 수있는 행 수를 제한하지만 (SQL 서버의 재귀 함수 호출 제한으로 인해). 이것은 단순한 커서 사용을 피하기 위해 엄청난 노력을 기울이지 말고 시스템 수명을위한 끔찍한 해결책으로 저를 놀라게합니다.

이 미친 증오의 이유는 무엇입니까? 일부 '공인 기관'은 커서에 대해 fatwa를 발행 했습니까? 말로 표현할 수없는 악이 커서의 중심에 숨어 어린이의 도덕이나 다른 것들의 도덕을 손상 시키는가?

Wiki 질문, 담당자보다 답변에 더 관심이 있습니다.

관련 정보 :

SQL Server 빨리 감기 커서

편집 : 더 정확하게하자 : 나는 일반적인 관계 연산 대신 커서를 사용해서는 안된다는 것을 이해합니다 . 그것은 쉬운 일이 아닙니다. 내가 이해하지 못하는 것은 사람들이 커서가 더 간단하고 효율적인 솔루션 일 때조차도 쿠 티나 무언가를 가진 것처럼 커서를 피하기 위해 길을 가고 있다는 것입니다. 명백한 기술적 효율성이 아니라 나를 괴롭히는 것은 비이성적 인 증오입니다.


1
나는 당신의 편집이 모든 것을 말하고 있다고 생각합니다 ... 거의 모든 상황 (내가봤을 때)에는 더 나은 성능의 세트 기반 상황으로 커서를 대체하는 방법이 있습니다. 당신은 생각할 필요가 없다고 말하지만 차이점을 이해합니다.
StingyJack

7
나는이 질문에 태그를 좋아합니다!
sep332

2
재귀 적 CTE 제한에 대한 부분 32은 말도 안됩니다. 아마도 재귀 트리거와 최대 값 @@NESTLEVEL을 생각하고있을 것 입니다 32. 그것은과 쿼리에서 설정할 수있는 OPTION (MAXRECURSION N)기본으로 100하고 0무제한 의미한다.
Martin Smith

@MartinSmith : 기본 제한은 이제 100이고 최대 값은 32K입니다. sql-server-helper.com/error-messages/msg-310.aspx
Steven A. Lowe

아니, 내 의견을 말할 때와 재귀 적 CTE를 지원하는 모든 버전의 SQL Server에서 여전히 똑같습니다. 링크에 "0을 지정하면 제한이 적용되지 않습니다."라고 표시됩니다.
Martin Smith

답변:


74

커서가있는 "오버 헤드"는 API의 일부일뿐입니다. 커서는 RDBMS의 일부가 후드에서 작동하는 방식입니다. 자주 CREATE TABLEINSERTSELECT문을, 그리고 구현은 분명 내부 커서 구현입니다.

더 높은 수준의 "세트 기반 연산자"를 사용하면 커서 결과가 단일 결과 세트로 묶이므로 API가 줄어 듭니다.

커서는 일류 컬렉션을 제공하는 최신 언어보다 우선합니다. 구 C, COBOL, Fortran 등은 널리 사용될 수있는 "컬렉션"개념이 없기 때문에 한 번에 하나씩 행을 처리해야했습니다. Java, C #, Python 등에는 결과 세트를 포함하는 일류 목록 구조가 있습니다.

느린 문제

일부 서클에서 관계형 조인은 미스터리이며 사람들은 간단한 조인 대신 중첩 커서를 씁니다. 나는 많은 서사시로 작성된 서사시 중첩 루프 연산을 보았습니다. RDBMS 최적화 방지 그리고 정말로 천천히 달리기.

중첩 된 커서 루프를 조인으로 대체하기 위해 간단한 SQL을 다시 작성하며 단일의 평평한 커서 루프는 프로그램을 100 번째로 실행할 수 있습니다. [그들은 내가 최적화의 신이라고 생각했습니다. 내가 한 것은 중첩 루프를 조인으로 바꾸는 것입니다. 여전히 커서를 사용했습니다.]

이 혼란은 종종 커서의 기소로 이어집니다. 그러나 이것은 커서가 아니며 문제가되는 커서의 오용입니다.

크기 문제

서사시 결과 집합 (예 : 테이블을 파일로 덤프)의 경우 커서가 필수적입니다. 집합 기반 작업은 실제로 큰 결과 집합을 메모리의 단일 컬렉션으로 구체화 할 수 없습니다.

대안

ORM 레이어를 가능한 많이 사용하려고합니다. 그러나 그것은 두 가지 목적이 있습니다. 먼저, 커서는 ORM 구성 요소에 의해 관리됩니다. 둘째, SQL이 응용 프로그램에서 구성 파일로 분리됩니다. 커서가 나쁘지는 않습니다. 모든 열기, 닫기 및 가져 오기를 코딩하는 것은 부가 가치 프로그래밍이 아닙니다.


3
"커서는 RDBMS가 작동하는 방식입니다." 구체적으로 SQL Server를 의미한다면 괜찮습니다. 그러나 나는 여러 RDBMS (및 ORDBMS) (Stonebraker 아래)의 내부에서 작업했으며 그중 아무것도하지 않았습니다. 예 : Ingres는 내부적으로 튜플의 "결과 세트"에 필요한 양을 사용합니다.
Richard T

@Richard T : RDBMS 소스에 관한 중고 정보를 처리하고 있습니다. 진술을 수정하겠습니다.
S.Lott

2
"정말로 서사시적인 중첩 루프 연산이 수많은 커서로 작성되는 것을 보았습니다." 나는 그들도 계속보고있다. 믿기 ​​어렵다.
RussellH

41

커서는 사람들이 세트 기반 환경에 절차 적 사고 방식을 과도하게 적용하도록합니다.

그리고 그들은 느리다 !

에서 SQLTeam :

커서는 SQL Server 내부의 데이터에 액세스하는 가장 느린 방법입니다. 한 번에 한 행에 액세스해야하는 경우에만 사용해야합니다. 내가 생각할 수있는 유일한 이유는 각 행에서 저장 프로 시저를 호출하는 것입니다. 에서 커서 성능 기사 나는 커서가있는 것을 발견 이상 서른 배 느린 세트를 기반으로 대안보다 .


6
그 기사는 7 살입니다. 아마도 그 동안 상황이 바뀌었을 것이라고 생각하십니까?
Steven A. Lowe

1
또한 커서가 일반적으로 느리고 피해야한다고 생각합니다. 그러나 OP가 내가 생각한 질문을 언급하고 있다면 커서가 올바른 솔루션이었습니다 (메모리 제약으로 인해 한 번에 하나씩 스트리밍 스트리밍).
rmeador

업데이트 된 기사는 상대 속도 측정을 수정하지는 않지만 몇 가지 좋은 최적화 및 대안을 제공합니다. 원본 기사에 따르면 커서는 while 루프보다 50 배 빠릅니다. 이는 흥미 롭습니다
Steven A. Lowe

6
@BoltBait : 나는 개인적으로 생각하는 당신이 할 경우, 당신이 정말 오래된 P는 사십오년 될 수 없다는 등의 담요 주장
스티븐 A. 로우

4
@ BoltBait : 당신은 내 잔디밭에서 내려요!
Steven A. Lowe

19

위의 답변은 "커서가 SQL Server 내부의 데이터에 액세스하는 가장 느린 방법입니다 ... 커서가 설정된 기반의 대안보다 30 배 이상 느립니다."

이 진술은 많은 상황에서 사실 일 수 있지만, 포괄적 인 진술로서 문제가 있습니다. 예를 들어, 지속적인 프로덕션 읽기를받는 큰 테이블의 많은 행에 영향을주는 업데이트 또는 삭제 작업을 수행하려는 경우 커서를 잘 활용했습니다. 한 번에 한 행씩 이러한 업데이트를 수행하는 저장 프로 시저를 실행하면 세트 기반 작업이 읽기 작업과 충돌하여 끔찍한 잠금 문제가 발생하여 프로덕션 시스템이 완전히 종료 될 수 있기 때문에 세트 기반 작업보다 빠릅니다. 극단적 인 경우).

다른 데이터베이스 활동이 없으면 세트 기반 조작이 보편적으로 더 빠릅니다. 프로덕션 시스템에서는 다릅니다.


1
규칙을 증명하는 예외처럼 들립니다.
Joel Coehoorn

6
@ [Joel Coehoorn] : 나는 그런 말을 이해하지 못했습니다.
Steven A. Lowe

2
@ [Steven A. Lowe] phrases.org.uk/meanings/exception-that-proves-the-rule.html 예외를 "남아있는 것"으로 이해하고 여기에있는 규칙은 "대부분의 상황에서 커서는 나쁜".
David Lay

1
@delm : 링크 주셔서 감사합니다, 지금 나는 문구를 훨씬 적게 이해합니다!
Steven A. Lowe

5
@ [Steven A. Lowe] 기본적으로 하위 사례와 함께 "규칙을 어기"면 규칙을 어 기고 규칙을 어기 게됩니다. 예를 들어 링크에서 : " '일요일에는 입장료가 무료입니다'와 같은 문구가있는 경우 일반적으로 입장료가 청구된다고 합리적으로 추정 할 수 있습니다.")
Fry

9

커서는 집합 기반 작업이 더 나은 곳에서 SQL 개발자를 시작하여 사용하는 경향이 있습니다. 특히 사람들이 전통적인 프로그래밍 언어를 배우고 나서 SQL을 배우면 "이러한 레코드를 반복하는"사고 방식은 사람들이 커서를 부적절하게 사용하게하는 경향이 있습니다.

가장 심각한 SQL 서적에는 커서 사용에 관한 장이 포함되어 있습니다. 잘 작성된 커서는 커서가 그 자리에 있지만 세트 기반 조작에는 사용해서는 안된다는 것을 분명히합니다.

커서가 올바른 선택이거나 적어도 올바른 선택 인 상황이 분명히 있습니다.


9

옵티마이 저는 종종 커서 대}을 사용할 때 관계 대수를 사용하여 문제점을 변환 할 수 없습니다. 커서는 종종 문제를 해결하는 좋은 방법이지만 SQL은 선언적 언어이며, 제약 조건에서 통계 및 색인에 이르기까지 데이터베이스에 많은 정보가 있습니다. 이는 옵티마이 저가 커서가 솔루션을 거의 명시 적으로 지시합니다.


8

Oracle PL / SQL 커서에서는 테이블 잠금이 발생하지 않으며 대량 수집 / 벌크 페칭을 사용할 수 있습니다.

Oracle 10에서 자주 사용되는 암시 적 커서

  for x in (select ....) loop
    --do something 
  end loop;

한 번에 100 개의 행을 가져옵니다. 명시 적 대량 수집 / 대량 반입도 가능합니다.

그러나 PL / SQL 커서는 최후의 수단으로, 세트 기반 SQL의 문제점을 해결할 수 없을 때 사용하십시오.

또 다른 이유는 병렬화인데, 데이터베이스가 행 단위 명령 코드보다 큰 세트 기반 명령문을 병렬화하는 것이 더 쉽습니다. 함수형 프로그래밍이 점점 더 대중화되는 이유 (Haskell, F #, Lisp, C # LINQ, MapReduce ...)와 같은 이유로 함수형 프로그래밍으로 병렬화가 더 쉬워집니다. 컴퓨터 당 CPU 수가 증가함에 따라 병렬화가 점점 더 문제가되고 있습니다.


6

일반적으로 관계형 데이터베이스에서는 커서를 사용한 코드 성능이 세트 기반 작업보다 훨씬 나쁩니다.


이에 대한 벤치 마크 또는 참조가 있습니까? 나는 그러한 급격한 성능 저하를 눈치 채지 못했지만 ... 어쩌면 내 테이블에 충분한 행이 없어서 (일반적으로 백만 이하)?
Steven A. Lowe

하지만 난 커서는 피할 커서를 극단으로하지 않을, 설정 작업의 intead 사용하여 옹호하지 않을 것 - 오, 난 당신이 무슨 뜻인지보고 기다립니다
스티븐 A. 로우를

3
SQL을 처음 수행했을 때 메인 프레임에서 매일 50k 데이터 파일을 SQL Server 데이터베이스로 가져와야한다고 생각했습니다. 커서를 사용하고 커서를 사용하여 가져 오는 데 약 26 시간이 걸렸습니다. 세트 기반 작업으로 변경했을 때 프로세스는 20 분이 걸렸습니다.
찰스

6

위의 답변은 잠금의 중요성을 충분히 강조하지 못했습니다. 나는 종종 테이블 레벨 잠금을 초래하기 때문에 커서를 좋아하지 않습니다.


1
네 감사합니다! 이를 방지하는 옵션 (읽기 전용, 전달 전용 등)이 없으면 여러 행과 여러 페이지의 행을 차지하는 모든 (sql 서버) 작업과 마찬가지로 확실히 작동합니다.
Steven A. Lowe

?? 잠금 전략 NOT 커서에 문제가 있습니다. SELECT 문조차도 읽기 잠금을 추가합니다.
Adam

3

그만한 가치가 있기 때문에 커서가 세트 기반 대응을 수행하는 "한 곳"이 누계에 있다는 것을 읽었습니다. 작은 테이블에서 열을 기준으로 행을 합산하는 속도는 세트 기반 연산을 선호하지만 테이블의 행 크기가 증가함에 따라 커서는 실행 총계 값을 다음 패스로 전달할 수 있기 때문에 커서가 더 빨라집니다. 고리. 이제 누계를 수행 해야하는 은 다른 주장입니다 ...


1
어떤 종류 (최소, 최대, 합)의 집계를 "총계 실행"을 의미하는 경우, 유능한 DBMS는 엔진에서 기능이 수행되기 때문에 클라이언트 측 커서 기반 솔루션의 성능을 능가합니다. 클라이언트 <-> 서버 오버 헤드가 없습니다. 아마도 SQL Server가 유능하지 않습니까?
Richard T

1
@ [Richard T] : 클라이언트 쪽 커서가 아닌 저장 프로 시저 내에서와 같이 서버 쪽 커서에 대해 설명합니다. 혼란을 드려 죄송합니다!
Steven A. Lowe


2

성능 (비) 문제 이외의 커서의 가장 큰 실패는 디버깅하기가 어렵다는 것입니다. 특히 디버깅이 비교적 쉽고 언어 기능이 훨씬 쉬운 대부분의 클라이언트 응용 프로그램의 코드와 비교할 때 특히 그렇습니다. 사실, 커서로 SQL에서 수행하는 거의 모든 작업이 클라이언트 응용 프로그램에서 먼저 발생해야한다고 주장합니다.


2
커서가 없어도 SQL을 디버깅하기가 어렵습니다. Visual Studio의 MS SQL 단계별 도구는 나를 좋아하지 않는 것 같습니다 (많은 중단되거나 중단 점을 전혀 트립하지 않음). 나는 보통 PRINT 문으로 줄었습니다. ;-)
Steven A. Lowe

1

해당 커서 예제를 게시하거나 질문에 링크 할 수 있습니까? 재귀 CTE보다 더 나은 방법이있을 것입니다.

다른 주석 외에도 커서를 잘못 사용하면 (종종) 불필요한 페이지 / 행 잠금이 발생합니다.


1
더 좋은 방법이있다-괴물 같은 커서 ;-)
Steven A. Lowe

1

사람들이 당신과 다른 견해를 가지고 있기 때문에 단순히 사람들을 "미치 광"이라고 부르지 않고 그들이하는 방식을 느끼는 데 충분한 이유가있는 전문가를 조롱하려고하기 때문에 아마도 두 번째 단락 이후에 당신의 질문을 결론 지었을 것입니다.

귀하의 질문에 관해서는, 커서가 필요할 수있는 상황이 있지만, 필자의 경험에 따르면 개발자는 커서가 실제 경우보다 더 자주 FAR을 사용해야한다고 결정합니다. 커서를 너무 많이 사용하여 커서를 잘못 사용하는 것과 비교할 때 커서를 사용하지 않을 가능성은 제 의견으로는 훨씬 높습니다.


8
좀 더 자세히 읽어보십시오. Tom – 정확한 문구는 "미친 증오"였습니다. "증오"는 "사람"이 아니라 형용사 "미친"의 대상이었습니다. 영어는 때때로 조금 어려울 수 있습니다 ;-)
Steven A. Lowe

0

basicaly 동일한 작업을 수행하는 2 개의 코드 블록. 어쩌면 조금 이상한 예이지만 요점을 증명합니다. SQL Server 2005 :

SELECT * INTO #temp FROM master..spt_values
DECLARE @startTime DATETIME

BEGIN TRAN 

SELECT @startTime = GETDATE()
UPDATE #temp
SET number = 0
select DATEDIFF(ms, @startTime, GETDATE())

ROLLBACK 

BEGIN TRAN 
DECLARE @name VARCHAR

DECLARE tempCursor CURSOR
    FOR SELECT name FROM #temp

OPEN tempCursor

FETCH NEXT FROM tempCursor 
INTO @name

SELECT @startTime = GETDATE()
WHILE @@FETCH_STATUS = 0
BEGIN

    UPDATE #temp SET number = 0 WHERE NAME = @name
    FETCH NEXT FROM tempCursor 
    INTO @name

END 
select DATEDIFF(ms, @startTime, GETDATE())
CLOSE tempCursor
DEALLOCATE tempCursor

ROLLBACK 
DROP TABLE #temp

단일 업데이트는 156ms가 걸리고 커서는 2016ms가 걸립니다.


3
네, 이것이 커서를 사용하는 정말 멍청한 방법이라는 점을 증명합니다! 그러나 각 행의 업데이트가 날짜 순서의 이전 행 값에 의존하면 어떻게 될까요?
Steven A. Lowe

소인 DESC INSERT 테이블이 테이블 ORDER FROM TRAN SELECT TOP 1 baseval을 BEGIN (필드) VALUES (종래 레코드로부터 유도 된 값을 포함 발스)를 COMMIT TRAN
dkretz

@doofledorfer : 마지막 행을 기준으로 하나의 행을 삽입하고 모든 행을 날짜 순서의 이전 행 값으로 업데이트하지는 않습니다
.

진정으로 커서를 사용하려면 업데이트 In의 WHERE CURRENT를 사용한다
erikkallen
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.