Tại sao tôi không thể sử dụng câu lệnh CASE để xem nếu một cột tồn tại và không CHỌN từ nó?


17

Tại sao một cái gì đó như thế này không hoạt động?

SELECT
CASE 
WHEN NULLIF(COL_LENGTH('Customers', 'Somecol'), '') IS NULL THEN NULL
ELSE Somecol
END AS MyTest
FROM Customers;

Tôi chỉ kiểm tra xem cột có tồn tại hay không, tuy nhiên, SQL Server phàn nàn về việc Somecolkhông tồn tại. Có một sự thay thế cho điều này trong một tuyên bố duy nhất?


3
Bạn có một ví dụ cho lý do tại sao bạn muốn làm điều này? Tôi không thể hiểu tại sao bạn muốn viết một truy vấn cố gắng chọn từ một cột có thể không tồn tại.
Mark Sinkinson

4
SQL Server đánh giá rằng cú pháp câu lệnh của bạn là chính xác trước khi nó thực thi nó. Do đó, tất cả các cột được tham chiếu phải tồn tại trong các bảng, ngay cả khi chúng được gói trong một CASEcâu lệnh.
Mark Sinkinson

@MarkSinkinson: Tên được kiểm tra theo cú pháp, nhưng vâng, SQL Server thực hiện điều đó trước khi thực sự chạy lô.
Andriy M

1
Chọn từ INFORMATION_SCHEMAcó thể làm việc như một cách giải quyết.
Brilliand

Câu trả lời:


43

Các truy vấn sau đây sử dụng ý tưởng giống như trong câu trả lời tuyệt vời này bởi ypercube :

SELECT x.*
FROM (SELECT NULL AS SomeCol) AS dummy
CROSS APPLY
(
  SELECT
    ID,
    SomeCol AS MyTest
  FROM dbo.Customers
) AS x;

Nó hoạt động như thế này:

  • nếu dbo.Customerscó một cột được đặt tên SomeCol, thì SomeColtrong SomeCol AS MyTestsẽ giải quyết như dbo.Customers.SomeCol;

  • nếu bảng không có cột như vậy, tham chiếu vẫn sẽ hợp lệ, vì bây giờ nó sẽ được giải quyết dưới dạng dummy.SomeCol: dummycác cột có thể được tham chiếu trong ngữ cảnh đó.

Bạn có thể chỉ định nhiều cột "dự phòng" theo cách đó. Mẹo nhỏ là không sử dụng bí danh bảng cho các cột như vậy (đây là một cách thực hành trong hầu hết các tình huống, nhưng trong trường hợp này, bỏ qua bí danh bảng sẽ giúp bạn giải quyết vấn đề).

Nếu bảng được sử dụng trong một liên kết và bảng khác có bảng riêng SomeCol, có lẽ bạn sẽ cần sử dụng truy vấn trên dưới dạng bảng dẫn xuất trước khi sử dụng nó trong liên kết để giữ cho thủ thuật hoạt động, đại loại như sau:

SELECT ...
FROM
(
  SELECT x.*
  FROM (SELECT NULL AS SomeCol) AS dummy
  CROSS APPLY (
    SELECT
      ID,
      SomeCol AS MyTest
    FROM dbo.Customers
  ) AS x
) AS cust
INNER JOIN ...
;

1
Tôi tự hỏi nếu trình biên dịch SQL chỉ là một chút phức tạp. Siêu mát mẻ những gì bạn có thể làm.
Max Vernon

9

Một cách để làm điều này là kiểm tra sự tồn tại của các cột, sau đó xây dựng SQL động dựa trên việc cột đó có tồn tại hay không.

Không có SQL động, SQL Server sẽ cố gắng đánh giá xem cột có tồn tại hay không trước khi nó thực thi quy chế, dẫn đến lỗi.

Tuy nhiên, điều đó có nghĩa là bạn sẽ có 2 truy vấn để viết và có khả năng thay đổi trong tương lai. Nhưng tôi không tin rằng bạn thực sự nên nhắm mục tiêu các SELECTtuyên bố trên các cột có thể không tồn tại.

declare @SQL varchar(max)

If exists (select 1 from sys.columns where Name = N'NameOfColumn' and object_id=object_id(N'yourTableName'))
begin
set @SQL = 'select ID, NameOfColumn from yourTableName'
exec(@sql)
end
else
begin
Print 'Column does not exist'
end

Vâng, có ý nghĩa, tuy nhiên, phải trong một tuyên bố duy nhất. Cuối cùng, tôi đang tìm kiếm một số chức năng hệ thống ma thuật không tồn tại.
Carson Rebke

4

Bạn có thể sử dụng một số XML để truy vấn các cột có thể có trong bảng.

Xây dựng một XML từ tất cả các cột trên mỗi hàng trong một ứng dụng chéo và trích xuất giá trị bằng cách sử dụng values()hàm.

Trong truy vấn này ID được biết đến vì vậy hãy lấy nó từ bảng trực tiếp. Col1 và Col2 có thể ở đó hoặc không để họ sử dụng XML.

select T.ID,
       TX.X.value('(Col1/text())[1]', 'int') as Col1,
       TX.X.value('(Col2/text())[1]', 'int') as Col2
from T
  cross apply (select T.* for xml path(''), type) as TX(X)

Câu đố SQL


-1

Cách tiếp cận của tôi chỉ khác một chút so với những người khác. Tôi thích sử dụng hệ thống cho việc này và chỉ cần lấy số đếm vì bạn có thể gán số đếm cột cho một biến ở đầu truy vấn và sau đó chọn tiếp tục hoặc không dựa trên điều đó. Nhược điểm đó là tầm nếu bạn có cùng tên cột trong nhiều bảng, bạn không chắc chắn rằng cột tồn tại trong bảng bạn muốn truy vấn. Tuy nhiên, kỹ thuật này cũng hoạt động trên các bảng cụ thể, vì bạn chỉ tìm cách để có được số đếm.

"Vấn đề" khi yêu cầu cụ thể là - rắc rối bạn đang gặp phải. Nói chung, nếu giá trị NULL khiến bạn gặp sự cố thì hãy tìm cách khác để xác minh sự tồn tại. Đây là một cách để làm điều đó mà không có nguy cơ làm đảo lộn máy chủ.

SELECT COUNT(*) FROM sys.columns WHERE sys.columns.name = 'FarmID'

1
Tại sao không sử dụng sysobjectscũng như trong truy vấn của bạn để kiểm tra xem bảng cụ thể có cột như vậy không?
ypercubeᵀᴹ

Có, tôi đã đề cập rằng có thể được thực hiện. đề cập rằng bạn cũng có thể gán nó cho một biến. (ví dụ: CHỌN COUNT (*) NHƯ myVarName Mạnh)
jinzai

1
Tôi không thể thấy làm thế nào điều này sẽ tốt hơn truy vấn của Mark. SELECT 1 ...cũng không lỗi.
ypercubeᵀᴹ

Tôi không nói nó tốt hơn, nhưng đó là cách đơn giản hơn nhiều để đạt được kết quả tương tự. CHỌN 1 có thể không có lỗi, nhưng nó không giống với COUNT. CHỌN trả về SOMETHING ngay cả khi đó là NULL. COUNT chỉ phải trả về một số duy nhất. Cách này sẽ nhanh hơn và tôi đã đề cập rằng số đếm có thể được sử dụng sau này.
jinzai

Nếu bạn cần số lượng ok. Nhưng EXISTS (SELECT ...)thường là nhanh hơn (SELECT COUNT(*) ...), không phải là cách khác.
ypercubeᵀᴹ

-3

Nếu tôi hiểu đúng ...

Bạn có thể sử dụng truy vấn tương tự như bên dưới và hành động phù hợp dựa trên số đếm ... Nếu số đếm là> 1 thì có nghĩa là bạn có col trong bảng đó và đếm = 0 thì bạn không có col đó trong đó bàn

Số lượng CHỌN (*)
TỪ THÔNG
TIN_SCHema.COLUMNS WHERE COLUMN_NAME IN ('Id') VÀ TABLE_SCHema = 'dbo' và TABLE_NAME = 'UserBase';


4
Không, bạn không hiểu chính xác. Đồng thời kiểm tra trường hợp đối với các lượt xem của Information_SCHema từ @AaronBertrand
Kin Shah
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.