Tại sao các truy vấn được phân tích cú pháp theo cách không cho phép sử dụng bí danh cột trong hầu hết các mệnh đề?


16

Trong khi cố gắng viết một truy vấn, tôi đã phát hiện ra (một cách khó khăn) rằng SQL Server phân tích WHERE trong một truy vấn từ lâu trước khi phân tích các CHỌN khi thực hiện một truy vấn.

Các tài liệu MSDN nói rằng thứ tự phân tích cú pháp logic chung sao cho CHỌN được phân tích cú pháp gần như cuối cùng (do đó dẫn đến lỗi "không có đối tượng [bí danh]" khi cố gắng sử dụng bí danh cột trong các mệnh đề khác). Thậm chí còn có một đề xuất cho phép sử dụng bí danh ở bất cứ đâu, bị nhóm Microsoft bắn hạ, trích dẫn các vấn đề tuân thủ tiêu chuẩn ANSI (điều này cho thấy hành vi này là một phần của tiêu chuẩn ANSI).

Là một lập trình viên (không phải là DBA), tôi thấy hành vi này hơi khó hiểu, vì dường như nó đánh bại phần lớn mục đích của việc có các bí danh cột (hoặc, ít nhất, các bí danh cột có thể được tạo ra mạnh hơn đáng kể nếu chúng được phân tích cú pháp trước đó trong quá trình thực hiện truy vấn), vì nơi duy nhất bạn thực sự có thể sử dụng các bí danh là trong ORDER BY. Là một lập trình viên, có vẻ như nó đang bỏ lỡ một cơ hội lớn để làm cho các truy vấn trở nên mạnh mẽ, thuận tiện và DRY hơn.

Dường như đó là một vấn đề rõ ràng đến mức nó có lý do, sau đó, có những lý do khác để quyết định rằng bí danh cột không được phép trong bất cứ điều gì khác ngoài CHỌN và ĐẶT HÀNG B, NG, nhưng những lý do đó là gì?

Câu trả lời:


19

Tóm lược

Không có lý do hợp lý nào không thể thực hiện được, nhưng lợi ích thì nhỏ và có một số cạm bẫy có thể không rõ ràng ngay lập tức.

Kết quả nghiên cứu

Tôi đã làm một số nghiên cứu và tìm thấy một số thông tin tốt. Sau đây là trích dẫn trực tiếp từ một nguồn chính đáng tin cậy (muốn ẩn danh) vào lúc 2012-08-09 17:49 GMT:

Khi SQL lần đầu tiên được phát minh, nó không có bí danh trong mệnh đề SELECT. Đây là một thiếu sót nghiêm trọng đã được sửa chữa khi ngôn ngữ được ANSI chuẩn hóa vào khoảng năm 1986.

Ngôn ngữ được dự định là "phi thủ tục" - nói cách khác, để mô tả dữ liệu bạn muốn mà không chỉ định cách tìm nó. Vì vậy, theo như tôi biết, không có lý do nào khiến việc triển khai SQL không thể phân tích toàn bộ truy vấn trước khi xử lý nó và cho phép các bí danh được xác định ở bất cứ đâu và được sử dụng ở mọi nơi. Ví dụ: tôi không thấy bất kỳ lý do nào khiến truy vấn sau không hợp lệ:

select name, salary + bonus as pay
from employee
where pay > 100000

Mặc dù tôi nghĩ rằng đây là một truy vấn hợp lý, một số hệ thống dựa trên SQL có thể đưa ra các hạn chế trong việc sử dụng bí danh vì một số lý do liên quan đến triển khai. Tôi không ngạc nhiên khi biết rằng SQL Server làm điều này.

Tôi quan tâm đến nghiên cứu sâu hơn về tiêu chuẩn SQL-86 và tại sao các DBMS hiện đại không hỗ trợ tái sử dụng bí danh, nhưng vẫn chưa có thời gian để đi xa với nó. Để bắt đầu, tôi không biết lấy tài liệu ở đâu hoặc làm thế nào để tìm ra chính xác ai là người tạo nên ủy ban. Bất cứ ai có thể giúp đỡ? Tôi cũng muốn biết thêm về sản phẩm Sybase gốc mà SQL Server đến từ.

Từ nghiên cứu này và một số suy nghĩ xa hơn, tôi đã nghi ngờ rằng việc sử dụng bí danh trong các mệnh đề khác, trong khi hoàn toàn có thể, đơn giản chưa bao giờ là ưu tiên cao đối với các nhà sản xuất DBMS so với các tính năng ngôn ngữ khác. Vì nó không phải là một trở ngại quá lớn, nên người viết truy vấn dễ dàng làm việc xung quanh, nên nỗ lực vượt qua các tiến bộ khác là không tối ưu. Ngoài ra, nó sẽ là độc quyền vì rõ ràng nó không phải là một phần của tiêu chuẩn SQL (mặc dù tôi đang chờ đợi để tìm hiểu thêm về điều đó) và do đó sẽ là một cải tiến nhỏ, phá vỡ khả năng tương thích SQL giữa các DBMS. Nếu so sánh, CROSS APPLY(thực sự không có gì khác hơn một bảng dẫn xuất cho phép các tham chiếu bên ngoài) là một thay đổi lớn, trong khi độc quyền cung cấp sức mạnh biểu cảm đáng kinh ngạc không dễ dàng thực hiện theo những cách khác.

Vấn đề với việc sử dụng bí danh ở mọi nơi

Nếu bạn cho phép các mục CHỌN được đặt trong mệnh đề WHERE, bạn không chỉ có thể làm nổ tung sự phức tạp của truy vấn (và do đó sự phức tạp của việc tìm kiếm một kế hoạch thực hiện tốt) có thể đưa ra những thứ hoàn toàn phi logic. Thử:

SELECT X + 5 Y FROM MyTable WHERE Y = X

Điều gì xảy ra nếu MyTable đã có cột Y, mệnh đề WHERE đề cập đến điều gì? Giải pháp là sử dụng CTE hoặc bảng dẫn xuất, trong hầu hết các trường hợp sẽ không tốn thêm chi phí nào nhưng đạt được kết quả cuối cùng tương tự. CTE và các bảng dẫn xuất ít nhất thực thi việc giải quyết sự mơ hồ bằng cách cho phép một bí danh chỉ được sử dụng một lần.

Ngoài ra, không sử dụng bí danh trong mệnh đề TỪ có ý nghĩa nổi bật. Bạn không thể làm điều này:

SELECT
   T3.ID + (SELECT Min(Interval) FROM Intervals WHERE IntName = 'T') CalcID
FROM
   Table1 T
   INNER JOIN Table2 T2
      ON T2.ID = CalcID
   INNER JOIN Table3 T3
      ON T2.ID = T3.ID

Đó là một tham chiếu vòng tròn (theo nghĩa là T2 đang bí mật đề cập đến một giá trị từ T3, trước khi bảng đó được trình bày trong danh sách THAM GIA), và rất khó nhìn thấy. Làm thế nào về điều này:

INSERT dbo.FinalTransaction
SELECT
   newid() FinalTransactionGUID,
   'GUID is: ' + Convert(varchar(50), FinalTransactionGUID) TextGUID,
   T.*
FROM
   dbo.MyTable T

Bạn muốn đặt cược bao nhiêu rằng hàm newid () sẽ được đưa vào kế hoạch thực hiện hai lần, hoàn toàn bất ngờ làm cho hai cột hiển thị các giá trị khác nhau? Điều gì về khi truy vấn trên được sử dụng mức N sâu trong CTE hoặc bảng dẫn xuất. Tôi đảm bảo rằng vấn đề tồi tệ hơn bạn có thể tưởng tượng. Có đã vấn đề mâu thuẫn nghiêm túc về việc khi mọi thứ được đánh giá chỉ một lần hoặc ít điểm nào trong một kế hoạch truy vấn, và Microsoft cho biết họ sẽ không sửa chữamột số trong số chúng vì chúng thể hiện đại số truy vấn đúng cách - nếu một kết quả không mong muốn, hãy chia truy vấn thành nhiều phần. Cho phép các tham chiếu được xâu chuỗi, phát hiện các tham chiếu vòng tròn thông qua các chuỗi có khả năng rất dài như vậy, đây là những vấn đề khá khó khăn. Giới thiệu song song và bạn đã có một cơn ác mộng trong quá trình thực hiện.

Lưu ý: Sử dụng bí danh trong WHERE hoặc GROUP BY sẽ không tạo ra sự khác biệt cho các vấn đề với các hàm như newid () hoặc rand ().

Một cách SQL Server để tạo các biểu thức có thể sử dụng lại

CROSS ỨNG DỤNG / OUTER ỨNG DỤNG là một cách trong SQL Server để tạo các biểu thức có thể được sử dụng ở bất kỳ nơi nào khác trong truy vấn (không sớm hơn trong mệnh đề TỪ):

SELECT
   X.CalcID
FROM
   Table1 T
   INNER JOIN Table3 T3
      ON T.ID = T3.ID
   CROSS APPLY (
      SELECT
         T3.ID + (SELECT Min(Interval) FROM Intervals WHERE IntName = 'T') CalcID
   ) X
   INNER JOIN Table2 T2
      ON T2.ID = X.CalcID

Điều này có hai điều:

  1. Làm cho tất cả các biểu thức trong CROSS ỨNG DỤNG có được một "không gian tên" (bí danh bảng, ở đây, X) và là duy nhất trong không gian tên đó.
  2. Làm cho nó rõ ràng ở khắp mọi nơi không chỉ là CalcID đến từ X, mà còn làm rõ lý do tại sao bạn không thể sử dụng bất cứ thứ gì từ X khi tham gia bảng T1 và T3, vì X chưa được giới thiệu.

Tôi thực sự khá thích CROSS ỨNG DỤNG. Nó đã trở thành người bạn trung thành của tôi và tôi sử dụng nó mọi lúc. Cần một UNPIVOT một phần (sẽ yêu cầu PIVOT / UNPIVOT hoặc UNPIVOT / PIVOT bằng cú pháp riêng)? Thực hiện với CROSS ỨNG DỤNG. Cần một giá trị tính toán sẽ được sử dụng lại nhiều lần? Làm xong. Cần cứng nhắc thực thi lệnh thực thi cho các cuộc gọi qua máy chủ được liên kết? Xong - với một sự cải thiện đáng sợ về tốc độ. Chỉ cần một loại hàng tách thành 2 hàng hoặc có thêm điều kiện? Làm xong.

Vì vậy, ít nhất, trong DBMS SQL Server 2005 trở lên, bạn không còn lý do nào để khiếu nại: CROSS ỨNG DỤNG là cách bạn DRY theo cách bạn muốn.


14

Tôi không thể cho bạn biết lý do chính xác, nhưng tôi sẽ nói với bạn rằng có cách giải quyết để lặp lại các biểu thức, ví dụ như sử dụng CTE, truy vấn con, bảng dẫn xuất, v.v. để tránh lặp lại.

Nếu bạn hiển thị một truy vấn với một biểu thức lặp đi lặp lại, có lẽ chúng tôi có thể chỉ cho bạn cách viết lại nó để biểu thức chỉ được liệt kê một lần. Tuy nhiên, điều này chỉ làm giảm sự phức tạp trong việc viết / đọc truy vấn, không có khả năng thay đổi nhiều về hiệu quả. SQL Server thường khá tốt khi nhận ra rằng các biểu thức được lặp lại và nó sẽ không thực hiện công việc đó hai lần. Có những trường hợp ngoại lệ đi theo một cách khác, nhưng bạn chỉ nên quan tâm đến hiệu quả khi bạn thực sự quan sát điều này xảy ra. Tôi nghi ngờ hầu hết các biểu thức lặp đi lặp lại mà bạn viết thực sự được thu gọn vào chỉ một thao tác trong kế hoạch.

Như đã nói, tôi cũng sẽ lặp lại một phần câu trả lời của mình từ câu hỏi này:

/dba/19762/why-is-the-select-clause-listed-first


Dưới đây là lời giải thích của Joe Celko về cách xử lý một truy vấn theo tiêu chuẩn (Tôi đã đánh cắp điều này từ bài viết aspfaq.com của riêng tôi , trong đó đã đánh cắp trích dẫn có thể từ một bài đăng trên nhóm tin của Celko):

Đây là cách một CHỌN hoạt động trong SQL ... ít nhất là trên lý thuyết. Sản phẩm thực sự sẽ tối ưu hóa mọi thứ khi họ có thể.

Bắt đầu trong mệnh đề TỪ và xây dựng một bảng làm việc từ tất cả các phép nối, liên hiệp, giao điểm và bất kỳ hàm tạo bảng nào khác ở đó. Tùy chọn AS cho phép bạn đặt tên cho bảng làm việc này mà sau đó bạn phải sử dụng cho phần còn lại của truy vấn có chứa.

Chuyển đến mệnh đề WHERE và xóa các hàng không vượt qua tiêu chí; nghĩa là, điều đó không kiểm tra TRUE (từ chối UNKNOWN và FALSE). Mệnh đề WHERE được áp dụng để làm việc trong mệnh đề TỪ.

Chuyển đến mệnh đề GROUP BY tùy chọn, tạo các nhóm và giảm mỗi nhóm thành một hàng duy nhất, thay thế bảng làm việc ban đầu bằng bảng được nhóm mới. Các hàng của bảng được nhóm phải là các đặc điểm của nhóm: (1) cột nhóm (2) một thống kê về nhóm (tức là các hàm tổng hợp) (3) một hàm hoặc (4) một biểu thức được tạo thành từ ba mục đó.

Đi đến mệnh đề HAVING tùy chọn và áp dụng nó vào bảng làm việc được nhóm; nếu không có mệnh đề GROUP BY, hãy coi toàn bộ bảng là một nhóm.

Chuyển đến mệnh đề SELECT và xây dựng các biểu thức trong danh sách. Điều này có nghĩa là các truy vấn con vô hướng, các hàm gọi và biểu thức trong SELECT được thực hiện sau khi tất cả các mệnh đề khác được thực hiện. Toán tử AS cũng có thể đặt tên cho các biểu thức trong danh sách CHỌN. Những tên mới này xuất hiện cùng một lúc, nhưng sau khi mệnh đề WHERE đã được thực thi; bạn không thể sử dụng chúng trong danh sách CHỌN hoặc cluase WHERE vì lý do đó.

Các biểu thức truy vấn lồng nhau tuân theo các quy tắc phạm vi thông thường mà bạn mong đợi từ một ngôn ngữ có cấu trúc khối như C, Pascal, Algol, v.v. Cụ thể, các truy vấn trong cùng có thể tham chiếu các cột và bảng trong các truy vấn chứa chúng.

Điều này có nghĩa là CHỌN không thể có nhiều cột hơn NHÓM THEO; nhưng nó chắc chắn có thể có ít cột hơn.

Giờ đây, Celko là một trong những người đóng góp chính cho các phiên bản trước đó của tiêu chuẩn. Tôi không biết liệu bạn có bao giờ nhận được câu trả lời dứt khoát cho WHY?câu hỏi không, ngoại trừ suy đoán. Tôi đoán là việc liệt kê hoạt động thực tế trước tiên giúp trình phân tích cú pháp dễ dàng biết chính xác loại hoạt động sẽ diễn ra. Hãy tưởng tượng một phép nối 20 bảng có thể là một SELECThoặc UPDATEhoặc DELETE, và hãy nhớ rằng mã cho các công cụ này ban đầu được viết lại vào thời mà phân tích chuỗi khá tốn kém.

Lưu ý rằng nếu tiêu chuẩn SQL được đưa FROMra trước, các nhà cung cấp có thể đã độc lập quyết định phân tích ngữ pháp theo một thứ tự khác, do đó, sẽ không có ý nghĩa gì khi mong đợi thứ tự các mệnh đề như được viết để hoàn toàn tuân theo thứ tự xử lý 100% thời gian.

Điều này cũng đúng với những thứ như CASE. Chúng tôi đã thấy các kịch bản ngay tại đây trên trang web này , ví dụ, nơi mà huyền thoại trước đây CASEluôn luôn xử lý theo thứ tự và ngắn mạch, là sai. Và điều này cũng mở rộng cho các niềm tin phổ biến khác, chẳng hạn như SQL Server đánh giá các phép nối theo thứ tự chúng được viết, các mệnh đề ngắn mạch WHEREtừ trái sang phải hoặc xử lý CTE một lần hoặc theo một thứ tự nhất định ngay cả khi chúng được tham chiếu nhiều lần. Các sản phẩm được tự do tối ưu hóa cách chúng thấy phù hợp ngay cả khi nó không phản ánh chính xác cách bạn đã nêu truy vấn nên hoạt động theo cách khai báo.


2
Cũng lưu ý rằng khả năng sử dụng hoặc không sử dụng các bí danh trong các phần khác nhau của truy vấn được thực thi bởi trình phân tích cú pháp, không phải bởi trình tối ưu hóa hoặc công cụ thực thi. Làm thế nào công cụ thực sự thực hiện truy vấn không nhất thiết phản ánh các hạn chế ảnh hưởng đến cú pháp.
Aaron Bertrand

2

Trong Entity SQL , bạn CÓ THỂ sử dụng các bí danh từ các biểu thức ở những nơi khác trong truy vấn trong một số tình huống:

select k1, count(t.a), sum(t.a)
from T as t
group by t.b + t.c as k1

Lưu ý rằng ở đây bạn PHẢI xác định biểu thức trong GROUP BYmệnh đề để sử dụng nó trong SELECTmệnh đề.

Rõ ràng có thể cho phép một số loại biểu thức bí danh này có thể tái sử dụng trong các truy vấn SQL.

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.