[Tôi chỉ nhận ra rằng tôi đã trả lời câu hỏi này trước đây]
Thực hiện điều này cho một thủ tục được lưu trữ phức tạp hơn nhiều so với một dạng xem hoặc bảng. Một trong những vấn đề là một thủ tục được lưu trữ có thể có nhiều đường dẫn mã khác nhau tùy thuộc vào các tham số đầu vào và thậm chí những thứ bạn không thể kiểm soát như trạng thái máy chủ, thời gian trong ngày, v.v. thủ tục được lưu trữ này? Điều gì sẽ xảy ra nếu có nhiều tập kết quả bất kể điều kiện?
CREATE PROCEDURE dbo.foo
@bar INT
AS
BEGIN
SET NOCOUNT ON;
IF @bar = 1
SELECT a, b, c FROM dbo.blat;
ELSE
SELECT d, e, f, g, h FROM dbo.splunge;
END
GO
Nếu thủ tục được lưu trữ của bạn không có đường dẫn mã và bạn tin tưởng rằng bạn sẽ luôn thấy cùng một tập hợp kết quả (và có thể xác định trước giá trị nào sẽ được cung cấp nếu thủ tục được lưu trữ có các tham số không phải là tùy chọn), hãy lấy một ví dụ đơn giản:
CREATE PROCEDURE dbo.bar
AS
BEGIN
SET NOCOUNT ON;
SELECT a = 'a', b = 1, c = GETDATE();
END
GO
FMTONLY
Một cách là làm như sau:
SET FMTONLY ON;
GO
EXEC dbo.bar;
Điều này sẽ cung cấp cho bạn một tập kết quả trống và ứng dụng khách của bạn có thể xem xét các thuộc tính của tập kết quả đó để xác định tên cột và kiểu dữ liệu.
Hiện tại, có rất nhiều vấn đề với SET FMTONLY ON;
mà tôi sẽ không đi sâu vào đây, nhưng ít nhất cần lưu ý rằng lệnh này không được dùng nữa - vì lý do chính đáng. Ngoài ra, hãy cẩn thận để SET FMTONLY OFF;
khi bạn hoàn tất, hoặc bạn sẽ thắc mắc tại sao bạn tạo thành công một thủ tục được lưu trữ nhưng sau đó không thể thực thi nó. Và không, tôi không cảnh báo bạn về điều đó bởi vì nó chỉ xảy ra với tôi. Trung thực. :-)
OPENQUERY
Bằng cách tạo một máy chủ được liên kết lặp lại, sau đó bạn có thể sử dụng các công cụ như OPENQUERY
để thực hiện một thủ tục được lưu trữ nhưng trả về tập kết quả có thể tổng hợp (tốt, vui lòng chấp nhận rằng đó là một định nghĩa cực kỳ lỏng lẻo) mà bạn có thể kiểm tra. Đầu tiên hãy tạo một máy chủ lặp lại (điều này giả sử một phiên bản cục bộ có tên là FOO
):
USE master;
GO
EXEC sp_addlinkedserver @server = N'.\FOO', @srvproduct=N'SQL Server'
GO
EXEC sp_serveroption @server=N'.\FOO', @optname=N'data access',
@optvalue=N'true';
Bây giờ chúng ta có thể thực hiện quy trình ở trên và đưa nó vào một truy vấn như sau:
SELECT * INTO #t
FROM OPENQUERY([.\FOO], 'EXEC dbname.dbo.bar;')
WHERE 1 = 0;
SELECT c.name, t.name
FROM tempdb.sys.columns AS c
INNER JOIN sys.types AS t
ON c.system_type_id = t.system_type_id
WHERE c.[object_id] = OBJECT_ID('tempdb..#t');
Điều này bỏ qua các loại bí danh (trước đây được gọi là loại dữ liệu do người dùng xác định) và cũng có thể hiển thị hai hàng cho các cột được xác định, ví dụ:sysname
. Nhưng từ trên nó tạo ra:
name name
---- --------
b int
c datetime
a varchar
Rõ ràng là có nhiều việc phải làm ở đây - varchar
không hiển thị độ dài và bạn sẽ phải lấy độ chính xác / tỷ lệ cho các loại khác như datetime2
, time
và decimal
. Nhưng đó là một sự khởi đầu.
SQL Server 2012
Có một số chức năng mới trong SQL Server 2012 giúp khám phá siêu dữ liệu dễ dàng hơn nhiều. Đối với quy trình trên, chúng ta có thể thực hiện như sau:
SELECT name, system_type_name
FROM sys.dm_exec_describe_first_result_set_for_object
(
OBJECT_ID('dbo.bar'),
NULL
);
Trong số những thứ khác, điều này thực sự cung cấp độ chính xác và tỷ lệ và giải quyết các loại bí danh cho chúng tôi. Đối với quy trình trên, kết quả là:
name system_type_name
---- ----------------
a varchar(1)
b int
c datetime
Không có nhiều sự khác biệt về mặt trực quan nhưng khi bạn bắt đầu tham gia vào tất cả các loại dữ liệu khác nhau với độ chính xác và tỷ lệ khác nhau, bạn sẽ đánh giá cao công việc bổ sung mà chức năng này mang lại cho bạn.
Nhược điểm:Trong SQL Server 2012, ít nhất các chức năng này chỉ hoạt động cho đầu tiên tập kết quả (như tên của hàm).