Tại sao mọi người ghét con trỏ SQL rất nhiều? [đóng cửa]


127

Tôi có thể hiểu rằng muốn tránh phải sử dụng một con trỏ do chi phí quá cao và sự bất tiện, nhưng có vẻ như có một số ám ảnh nghiêm trọng về con trỏ đang diễn ra trong đó mọi người sẽ cố gắng hết sức để tránh phải sử dụng một con trỏ.

Ví dụ: một câu hỏi hỏi cách thực hiện một cái gì đó rõ ràng tầm thường với một con trỏ và câu trả lời được chấp nhận được đề xuất bằng cách sử dụng truy vấn đệ quy biểu thức bảng chung (CTE) với hàm tùy chỉnh đệ quy, mặc dù điều này giới hạn số lượng hàng có thể được xử lý là 32 (do giới hạn gọi hàm đệ quy trong máy chủ sql). Đây là một giải pháp khủng khiếp cho tuổi thọ hệ thống, chưa kể đến một nỗ lực to lớn chỉ để tránh sử dụng một con trỏ đơn giản.

Lý do cho mức độ thù hận điên rồ này là gì? Có một số 'cơ quan có thẩm quyền' đã ban hành một fatwa chống lại con trỏ? Có một số điều xấu xa không thể nói được ẩn giấu trong trái tim của những kẻ nguyền rủa làm hỏng đạo đức của trẻ em hay cái gì đó?

Wiki câu hỏi, quan tâm đến câu trả lời hơn các đại diện.

Thông tin liên quan:

Máy chủ chuyển tiếp nhanh SQL Server

EDIT: hãy để tôi chính xác hơn: Tôi hiểu rằng không nên sử dụng các con trỏ thay vì các hoạt động quan hệ thông thường ; đó là một người không có trí tuệ Điều tôi không hiểu là mọi người sẽ cố gắng tránh những con trỏ như họ có coot hay gì đó, ngay cả khi con trỏ là một giải pháp đơn giản và hiệu quả hơn. Đó là sự thù hận phi lý gây trở ngại cho tôi, không phải là hiệu quả kỹ thuật rõ ràng.


1
Tôi nghĩ rằng Chỉnh sửa của bạn nói lên tất cả ... Trong hầu hết tất cả các tình huống (mà tôi đã gặp) có một cách để thay thế một con trỏ bằng một tình huống dựa trên thiết lập hiệu suất tốt hơn. Bạn nói không có trí tuệ, nhưng bạn hiểu khác.
StingyJack

7
Tôi thích các thẻ về câu hỏi này!
sep32

2
Phần về giới hạn CTE đệ quy 32là vô nghĩa. Có lẽ bạn đang nghĩ đến trigger đệ quy và tối đa @@NESTLEVELcủa 32. Nó có thể được đặt trong truy vấn OPTION (MAXRECURSION N)với mặc định 1000có nghĩa là không giới hạn.
Martin Smith

@MartinSmith: giới hạn mặc định là 100 và tối đa là 32K sql-server-helper.com/error-messages/msg-310.aspx
Steven A. Lowe

Không, nó vẫn giống hệt như khi tôi đưa ra nhận xét của mình và trong tất cả các phiên bản SQL Server hỗ trợ CTE đệ quy. Như liên kết của bạn nói "Khi 0 được chỉ định, không có giới hạn nào được áp dụng."
Martin Smith

Câu trả lời:


74

"Chi phí" với các con trỏ chỉ là một phần của API. Các con trỏ là cách các phần của RDBMS hoạt động dưới mui xe. Thường CREATE TABLEINSERTcó các SELECTcâu lệnh, và việc thực hiện là triển khai con trỏ bên trong rõ ràng.

Sử dụng "toán tử dựa trên tập hợp" cấp cao hơn sẽ kết hợp các kết quả con trỏ thành một tập kết quả duy nhất, nghĩa là ít qua lại API hơn.

Con trỏ có trước các ngôn ngữ hiện đại cung cấp các bộ sưu tập hạng nhất. Old C, COBOL, Fortran, v.v., đã phải xử lý từng hàng một vì không có khái niệm "bộ sưu tập" nào có thể được sử dụng rộng rãi. Java, C #, Python, v.v., có cấu trúc danh sách hạng nhất để chứa các tập kết quả.

Vấn đề chậm

Trong một số vòng tròn, các phép nối quan hệ là một bí ẩn và mọi người sẽ viết các con trỏ lồng nhau chứ không phải là một phép nối đơn giản. Tôi đã thấy các hoạt động vòng lặp lồng nhau thực sự hoành tráng được viết ra rất nhiều và nhiều con trỏ. Đánh bại tối ưu hóa RDBMS. Và chạy rất chậm.

SQL đơn giản viết lại để thay thế các vòng lặp con trỏ lồng nhau bằng các phép nối và một vòng lặp con trỏ đơn, phẳng có thể làm cho các chương trình chạy trong 100 lần. [Họ nghĩ tôi là thần tối ưu. Tất cả tôi đã làm là thay thế các vòng lặp lồng nhau bằng các phép nối. Vẫn sử dụng con trỏ.]

Sự nhầm lẫn này thường dẫn đến một bản cáo trạng của con trỏ. Tuy nhiên, đó không phải là con trỏ, đó là sự lạm dụng của con trỏ là vấn đề.

Vấn đề kích thước

Đối với các tập kết quả thực sự hoành tráng (nghĩa là bỏ bảng vào một tệp), con trỏ là rất cần thiết. Các hoạt động dựa trên tập hợp không thể cụ thể hóa các tập kết quả thực sự lớn thành một bộ sưu tập duy nhất trong bộ nhớ.

Lựa chọn thay thế

Tôi cố gắng sử dụng một lớp ORM càng nhiều càng tốt. Nhưng điều đó có hai mục đích. Đầu tiên, các con trỏ được quản lý bởi thành phần ORM. Thứ hai, SQL được tách ra khỏi ứng dụng thành một tệp cấu hình. Không phải là những con trỏ xấu. Đó là mã hóa tất cả những gì mở, đóng và tìm nạp không phải là lập trình giá trị gia tăng.


3
"Các con trỏ là cách RDBMS hoạt động dưới mui xe." Nếu bạn có nghĩa cụ thể là SQL Server, OK, tốt, tôi không biết gì về điều đó. Nhưng tôi đã làm việc trên các phần bên trong của nhiều RDBMS (và ORDBMS) (dưới Stonebraker) và không ai trong số họ làm điều đó. Ví dụ: Ingres sử dụng số tiền cho "tập kết quả" của bộ dữ liệu trong nội bộ.
Richard T

@Richard T: Tôi đang xử lý thông tin cũ về nguồn RDBMS; Tôi sẽ sửa đổi tuyên bố.
S.Lott

2
"Tôi đã thấy các hoạt động vòng lặp lồng nhau thực sự hoành tráng được viết ra rất nhiều và rất nhiều con trỏ." Tôi tiếp tục nhìn thấy chúng quá. Thật khó có thể tin.
RussellH

41

Con trỏ làm cho mọi người áp dụng quá mức một tư duy thủ tục vào một môi trường dựa trên tập hợp.

Và họ đang CHẬM !!!

Từ SQLTeam :

Xin lưu ý rằng các con trỏ là cách TUYỆT VỜI nhất để truy cập dữ liệu trong SQL Server. Chỉ nên được sử dụng khi bạn thực sự cần truy cập một hàng tại một thời điểm. Lý do duy nhất tôi có thể nghĩ đến đó là gọi một thủ tục được lưu trữ trên mỗi hàng. Trong bài viết Hiệu suất con trỏ, tôi phát hiện ra rằng các con trỏ chậm hơn ba mươi lần so với các lựa chọn thay thế dựa trên thiết lập .


6
Bài báo đó đã 7 tuổi, bạn có nghĩ rằng có lẽ mọi thứ đã thay đổi trong lúc này không?
Steven A. Lowe

1
Tôi cũng nghĩ rằng con trỏ rất chậm và nói chung là nên tránh. Tuy nhiên, nếu OP đang đề cập đến câu hỏi mà tôi nghĩ là anh ta, thì một con trỏ là giải pháp chính xác ở đó (phát trực tiếp bản ghi một lần do hạn chế về bộ nhớ).
rmeador

bài viết cập nhật không sửa các phép đo tốc độ tương đối, nhưng nó cung cấp một số tối ưu hóa và lựa chọn thay thế tốt. Lưu ý rằng bài báo gốc nói rằng con trỏ nhanh hơn 50 lần so với vòng lặp, điều này rất thú vị
Steven A. Lowe

6
@BoltBait: Cá nhân tôi nghĩ rằng nếu bạn đưa ra những khẳng định về chăn như thế thì bạn thực sự không thể 45 tuổi :-P
Steven A. Lowe

4
@BoltBait: Các em xuống bãi cỏ của tôi!
Steven A. Lowe

19

Có một câu trả lời ở trên có nội dung "con trỏ là cách TUYỆT VỜI nhất để truy cập dữ liệu bên trong SQL Server ... con trỏ chậm hơn ba mươi lần so với các lựa chọn thay thế dựa trên thiết lập."

Tuyên bố này có thể đúng trong nhiều trường hợp, nhưng như một tuyên bố về chăn thì nó có vấn đề. Ví dụ: tôi đã sử dụng tốt các con trỏ trong các tình huống tôi muốn thực hiện cập nhật hoặc xóa hoạt động ảnh hưởng đến nhiều hàng của một bảng lớn đang nhận được số lần đọc sản xuất liên tục. Chạy một thủ tục được lưu trữ, những cập nhật này mỗi lần cập nhật sẽ nhanh hơn các hoạt động dựa trên tập hợp, bởi vì hoạt động dựa trên tập hợp mâu thuẫn với hoạt động đọc và kết thúc gây ra sự cố khóa khủng khiếp (và có thể giết chết hoàn toàn hệ thống sản xuất, trong trường hợp cực đoan).

Trong trường hợp không có hoạt động cơ sở dữ liệu khác, các hoạt động dựa trên tập hợp sẽ nhanh hơn. Trong các hệ thống sản xuất, nó phụ thuộc.


1
Âm thanh như ngoại lệ chứng minh quy tắc.
Joel Coehoorn

6
@ [Joel Coehoorn]: Tôi chưa bao giờ hiểu câu nói đó.
Steven A. Lowe

2
@ [Steven A. Lowe] phrases.org.uk/meanings/exception-that-proves-the-rule.html hiểu ngoại lệ như "những gì còn lại out" và lưu ý rằng các quy tắc ở đây là cái gì đó như "trong hầu hết các con trỏ tình hình là xấu".
David Lay

1
@delm: cảm ơn vì đường link, giờ tôi hiểu cụm từ còn ít hơn!
Steven A. Lowe

5
@ [Steven A. Lowe] Về cơ bản, nó nói rằng nếu bạn "phá vỡ một quy tắc" bằng một chữ con, thì phải có một quy tắc chung để phá vỡ, tồn tại một quy tắc tồn tại. ví dụ như Từ Link: ( "Nếu chúng ta có một tuyên bố như" nhập hoàn toàn miễn phí vào ngày chủ nhật, chúng tôi hợp lý có thể cho rằng, như một quy luật chung, nhập cảnh được sạc cho.")
Fry

9

Các con trỏ có xu hướng được sử dụng bởi các nhà phát triển SQL bắt đầu ở những nơi mà các hoạt động dựa trên tập hợp sẽ tốt hơn. Đặc biệt khi mọi người học SQL sau khi học một ngôn ngữ lập trình truyền thống, tâm lý "lặp đi lặp lại những hồ sơ này" có xu hướng khiến mọi người sử dụng con trỏ không phù hợp.

Hầu hết các sách SQL nghiêm trọng bao gồm một chương liên quan đến việc sử dụng các con trỏ; những cái được viết tốt cho thấy rõ rằng con trỏ có vị trí của chúng nhưng không nên được sử dụng cho các hoạt động dựa trên tập hợp.

Rõ ràng có những tình huống mà con trỏ là lựa chọn chính xác, hoặc ít nhất là một lựa chọn chính xác.


9

Trình tối ưu hóa thường không thể sử dụng đại số quan hệ để biến đổi vấn đề khi sử dụng phương pháp con trỏ. Thông thường, một con trỏ là một cách tuyệt vời để giải quyết vấn đề, nhưng SQL là ngôn ngữ khai báo và có rất nhiều thông tin trong cơ sở dữ liệu, từ các ràng buộc, đến các thống kê và chỉ mục có nghĩa là trình tối ưu hóa có rất nhiều tùy chọn để giải quyết vấn đề, trong khi một con trỏ khá rõ ràng chỉ đạo giải pháp.


8

Trong các con trỏ Oracle PL / SQL sẽ không dẫn đến khóa bảng và có thể sử dụng thu thập hàng loạt / tìm nạp hàng loạt.

Trong Oracle 10, con trỏ ẩn thường được sử dụng

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

tìm nạp 100 hàng một lúc. Cũng có thể thu thập số lượng lớn / tìm nạp số lượng lớn.

Tuy nhiên, các con trỏ PL / SQL là một cái gì đó cuối cùng, hãy sử dụng chúng khi bạn không thể giải quyết vấn đề với SQL dựa trên tập hợp.

Một lý do khác là song song hóa, cơ sở dữ liệu sẽ dễ dàng song song hóa các câu lệnh dựa trên tập lớn hơn so với mã mệnh lệnh theo từng hàng. Đó là cùng một lý do tại sao lập trình chức năng ngày càng trở nên phổ biến (Haskell, F #, Lisp, C # LINQ, MapReduce ...), lập trình chức năng giúp việc song song hóa dễ dàng hơn. Số lượng CPU trên mỗi máy tính đang tăng lên nên việc song song hóa ngày càng trở thành một vấn đề.


6

Nói chung, vì trên cơ sở dữ liệu quan hệ, hiệu năng của mã sử dụng con trỏ là một thứ tự có độ lớn kém hơn các hoạt động dựa trên tập hợp.


Bạn có một điểm chuẩn hoặc tài liệu tham khảo cho điều này? tôi đã không nhận thấy bất kỳ sự suy giảm hiệu suất mạnh mẽ như vậy ... nhưng có lẽ các bảng của tôi không có đủ hàng cho vấn đề đó (thường là một triệu hoặc ít hơn)?
Steven A. Lowe

oh chờ đợi tôi xem những gì bạn có nghĩa là - nhưng tôi sẽ không bao giờ chủ trương dùng con trỏ intead của hoạt động thiết lập, chỉ không đi đến thái cực để con trỏ tránh
Steven A. Lowe

3
Tôi nhớ lần đầu tiên tôi làm SQL, chúng tôi phải nhập tệp dữ liệu 50 nghìn hàng ngày từ máy tính lớn vào cơ sở dữ liệu SQL Server ... Tôi đã sử dụng một con trỏ và phát hiện ra rằng quá trình nhập mất khoảng 26 giờ bằng cách sử dụng con trỏ .. Khi tôi thay đổi thành các hoạt động dựa trên thiết lập, quá trình này mất 20 phút.
Charles Bretana

6

Các câu trả lời ở trên chưa nhấn mạnh đủ tầm quan trọng của việc khóa. Tôi không phải là một fan hâm mộ lớn của các con trỏ bởi vì chúng thường dẫn đến khóa cấp bảng.


1
Vâng, cảm ơn! Không có tùy chọn để ngăn chặn nó (chỉ đọc, chỉ chuyển tiếp, v.v.), chắc chắn họ sẽ, cũng như mọi hoạt động (máy chủ sql) sẽ tiến hành chiếm vài hàng và sau đó vài trang hàng.
Steven A. Lowe

?? Đó là một vấn đề với chiến lược khóa của bạn KHÔNG phải là con trỏ. Ngay cả một câu lệnh CHỌN sẽ thêm khóa đọc.
Adam

3

Đối với những gì đáng giá tôi đã đọc rằng "một" nơi con trỏ sẽ thực hiện đối tác dựa trên tập hợp của nó trong tổng số đang chạy. Trên một bảng nhỏ, tốc độ tính tổng các hàng theo thứ tự theo các cột ủng hộ hoạt động dựa trên tập hợp nhưng khi bảng tăng kích thước hàng, con trỏ sẽ trở nên nhanh hơn vì đơn giản là nó có thể mang tổng giá trị đang chạy đến lần truyền tiếp theo của vòng. Bây giờ , nơi bạn nên thực hiện tổng cộng là một đối số khác nhau ...


1
Nếu bạn có nghĩa là bằng cách "chạy tổng cộng" một loại tổng hợp nào đó (tối thiểu, tối đa, tổng), thì bất kỳ DBMS có thẩm quyền nào cũng sẽ đánh bật quần của một giải pháp dựa trên con trỏ, dựa trên con trỏ, nếu chỉ vì chức năng được thực hiện trong động cơ và không có máy khách <-> chi phí máy chủ. Có lẽ SQL Server không đủ năng lực?
Richard T

1
@ [Richard T]: chúng tôi đang thảo luận về con trỏ phía máy chủ, như trong một thủ tục được lưu trữ, không phải là con trỏ phía máy khách; xin lỗi vì sự nhầm lẫn!
Steven A. Lowe


2

Ngoài các vấn đề về hiệu năng (không), tôi nghĩ rằng sự thất bại lớn nhất của các con trỏ là chúng rất khó để gỡ lỗi. Đặc biệt so với mã trong hầu hết các ứng dụng khách, nơi gỡ lỗi có xu hướng tương đối dễ dàng và các tính năng ngôn ngữ có xu hướng dễ dàng hơn nhiều. Trên thực tế, tôi cho rằng gần như mọi thứ người ta đang làm trong SQL bằng một con trỏ có thể sẽ xảy ra trong ứng dụng khách ngay từ đầu.


2
SQL là đau đớn để gỡ lỗi, ngay cả khi không có con trỏ. Các công cụ bước qua MS SQL trong Visual Studio dường như không thích tôi (chúng treo rất nhiều hoặc hoàn toàn không có điểm dừng), vì vậy tôi thường rút gọn thành các câu lệnh IN ;-)
Steven A. Lowe

1

Bạn có thể gửi ví dụ con trỏ hoặc liên kết đến câu hỏi? Có lẽ có một cách thậm chí còn tốt hơn so với CTE đệ quy.

Ngoài các bình luận khác, con trỏ khi sử dụng không đúng cách (thường là) gây ra khóa trang / hàng không cần thiết.


1
có một cách tốt hơn - một con trỏ kỳ dị ;-)
Steven A. Lowe

1

Bạn có thể đã kết luận câu hỏi của bạn sau đoạn thứ hai, thay vì gọi mọi người là "điên rồ" chỉ vì họ có quan điểm khác với bạn và cố gắng chế giễu các chuyên gia có thể có lý do rất chính đáng để cảm nhận theo cách họ làm.

Đối với câu hỏi của bạn, trong khi chắc chắn có những tình huống có thể gọi con trỏ, nhưng theo kinh nghiệm của tôi, các nhà phát triển quyết định rằng con trỏ "phải" được sử dụng FAR thường xuyên hơn thực tế. Theo tôi, khả năng ai đó mắc lỗi khi sử dụng quá nhiều con trỏ so với việc không sử dụng chúng khi họ nên cao hơn theo quan điểm của tôi.


8
xin vui lòng đọc kỹ hơn, Tom - cụm từ chính xác là "hận thù điên rồ"; "Ghét" là đối tượng của tính từ "điên", không phải "người". Tiếng Anh đôi khi có thể hơi khó khăn ;-)
Steven A. Lowe

0

cơ bản 2 khối mã làm điều tương tự. có thể đó là một ví dụ hơi kỳ lạ nhưng nó chứng minh điều đó. Máy chủ SQL 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

bản cập nhật duy nhất mất 156 ms trong khi con trỏ mất 2016 ms.


3
vâng, nó chứng minh rằng đây là một cách thực sự ngu ngốc để sử dụng một con trỏ! nhưng nếu cập nhật của mỗi hàng phụ thuộc vào giá trị của hàng trước theo thứ tự ngày thì sao?
Steven A. Lowe

BEGIN TRAN CHỌN TOP 1 baseval TỪ bảng ĐẶT HÀNG B BYNG dấu thời gian Bảng DESC INSERT (trường) GIÁ TRỊ (vals, bao gồm giá trị xuất phát từ bản ghi trước)
CAM KẾT

@doofledorfer: sẽ chèn một hàng dựa trên hàng cuối cùng theo ngày, không cập nhật mỗi hàng theo một giá trị từ hàng trước của nó theo thứ tự ngày
Steven A. Lowe

Để thực sự sử dụng con trỏ, bạn nên sử dụng WHERE HIỆN TẠI trong bản cập nhật
erikkallen
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.