Chọn các cột từ tập kết quả của thủ tục được lưu trữ


447

Tôi có một thủ tục được lưu trữ trả về 80 cột và 300 hàng. Tôi muốn viết một lựa chọn có được 2 trong số các cột đó. Cái gì đó như

SELECT col1, col2 FROM EXEC MyStoredProc 'param1', 'param2'

Khi tôi sử dụng cú pháp trên, tôi gặp lỗi:

"Tên cột không hợp lệ".

Tôi biết giải pháp đơn giản nhất là thay đổi thủ tục được lưu trữ, nhưng tôi đã không viết nó và tôi không thể thay đổi nó.

Có cách nào để làm những gì tôi muốn?

  • Tôi có thể tạo một bảng tạm thời để đưa kết quả vào, nhưng vì có 80 cột nên tôi cần tạo một bảng tạm thời 80 cột chỉ để có được 2 cột. Tôi muốn tránh theo dõi tất cả các cột được trả lại.

  • Tôi đã thử sử dụng WITH SprocResults AS ....theo đề xuất của Mark, nhưng tôi đã gặp 2 lỗi

    Cú pháp không chính xác gần từ khóa 'EXEC'.
    Cú pháp không chính xác gần ')'.

  • Tôi đã thử khai báo một biến bảng và tôi đã gặp lỗi sau

    Lỗi chèn: Tên cột hoặc số lượng giá trị được cung cấp không khớp với định nghĩa bảng

  • Nếu tôi thử
    SELECT * FROM EXEC MyStoredProc 'param1', 'param2'
    tôi nhận được lỗi:

    Cú pháp không chính xác gần từ khóa 'exec'.


Vì tò mò, truy vấn này có hoạt động không: CHỌN * TỪ EXEC MyStoredProc 'param1', 'param2' Nếu vậy, tên cột nào sẽ hiển thị trong tập kết quả và bạn có thể sử dụng các tên cột đó trong danh sách lựa chọn của mình không?
Dave Costa

5
Tôi chưa bao giờ tìm thấy một câu trả lời cho điều này.
Rossini

32
Vâng, bạn không bao giờ trả lời một câu hỏi rất quan trọng! Bạn đang hỏi về nền tảng SQL nào? MySQL, Microsoft SQL Server, Oracle, v.v ... Có vẻ như tôi là SQL Server, nhưng bạn cần nói với mọi người hoặc họ không thể trả lời câu hỏi của bạn một cách đáng tin cậy.
JMTyler

6
Chà, nó phải là MS-SQL. EXECkhông phải là một từ khóa MySQL (tương đương với MySQL là các câu lệnh được chuẩn bị ). Mặc dù tôi muốn biết câu trả lời cho MySQL, nhưng câu trả lời bên dưới mục tiêu T-SQL. Đang thử lại.
bobobobo

1
Tôi chưa bao giờ tìm thấy câu trả lời cho điều này
Rossini

Câu trả lời:


186

Bạn có thể tách ra truy vấn? Chèn kết quả Proc được lưu trữ vào một biến bảng hoặc bảng tạm thời. Sau đó, chọn 2 cột từ biến bảng.

Declare @tablevar table(col1 col1Type,..
insert into @tablevar(col1,..) exec MyStoredProc 'param1', 'param2'

SELECT col1, col2 FROM @tablevar

27
Nó cũng không hoạt động khi bạn không biết định nghĩa bảng
Ian Boyd

không biết về loại đó. Có phải chúng được thực hiện giống như bảng tạm thời? Hay là nó nghiêm ngặt trong bộ nhớ?
d -_- b

Điều này thật thú vị: sqlnerd.blogspot.com/2005/09/ Cách
d -_- b

Điều này hoạt động tốt nếu số lượng cột được cung cấp trong bảng tạm thời giống như số cột trong đầu ra của thủ tục được lưu trữ. chagbert.
Chagbert

83

Đây là một liên kết đến một tài liệu khá hay giải thích tất cả các cách khác nhau để giải quyết vấn đề của bạn (mặc dù rất nhiều trong số chúng không thể được sử dụng vì bạn không thể sửa đổi quy trình được lưu trữ hiện có.)

Cách chia sẻ dữ liệu giữa các thủ tục lưu trữ

Câu trả lời của Gulzar sẽ hoạt động (được ghi lại trong liên kết ở trên) nhưng sẽ rất khó để viết (bạn sẽ cần chỉ định tất cả 80 tên cột trong câu lệnh @tablevar (col1, ...) của mình. Và trong tương lai nếu một cột được thêm vào lược đồ hoặc đầu ra bị thay đổi, nó sẽ cần được cập nhật trong mã của bạn hoặc nó sẽ bị lỗi.


1
Tôi nghĩ rằng đề xuất OPENQUERY trong liên kết đó gần hơn với những gì OP đang tìm kiếm.
Corin


39

Điều này hoạt động với tôi: (tức là tôi chỉ cần 2 cột trong số 30+ được trả về sp_help_job)

SELECT name, current_execution_status 
FROM OPENQUERY (MYSERVER, 
  'EXEC msdb.dbo.sp_help_job @job_name = ''My Job'', @job_aspect = ''JOB''');  

Trước khi nó hoạt động, tôi cần chạy nó:

sp_serveroption 'MYSERVER', 'DATA ACCESS', TRUE;

.... để cập nhật sys.serversbảng. (tức là sử dụng tự tham chiếu trong OPENQUERY dường như bị tắt theo mặc định.)

Đối với yêu cầu đơn giản của tôi, tôi đã gặp phải bất kỳ vấn đề nào được mô tả trong phần OPENQUERY trong liên kết tuyệt vời của Lance.

Rossini, nếu bạn cần thiết lập động các tham số đầu vào đó, thì việc sử dụng OPENQUERY sẽ trở nên khó khăn hơn một chút:

DECLARE @innerSql varchar(1000);
DECLARE @outerSql varchar(1000);

-- Set up the original stored proc definition.
SET @innerSql = 
'EXEC msdb.dbo.sp_help_job @job_name = '''+@param1+''', @job_aspect = N'''+@param2+'''' ;

-- Handle quotes.
SET @innerSql = REPLACE(@innerSql, '''', '''''');

-- Set up the OPENQUERY definition.
SET @outerSql = 
'SELECT name, current_execution_status 
FROM OPENQUERY (MYSERVER, ''' + @innerSql + ''');';

-- Execute.
EXEC (@outerSql);

Tôi không chắc chắn về sự khác biệt (nếu có) giữa việc sử dụng sp_serveroptionđể cập nhật sys.serverstrực tiếp bản tự tham chiếu hiện có , so với việc sử dụng sp_addlinkedserver(như được mô tả trong liên kết của Lance) để tạo một bản sao / bí danh.

Lưu ý 1: Tôi thích OPENQUERY hơn OPENWAYSET, với điều kiện là OPENQUERY không yêu cầu định nghĩa chuỗi kết nối trong Proc.

Lưu ý 2: Đã nói tất cả những điều này: thông thường tôi sẽ chỉ sử dụng CHERTN ... EXEC :) Vâng, đó là thêm 10 phút gõ, nhưng nếu tôi có thể giúp nó, tôi không muốn nói lung tung với:
(a) trích dẫn trong dấu ngoặc kép trong trích dẫn và
(b) sys bảng và / hoặc lén lút tự thiết lập Máy chủ được liên kết (ví dụ: đối với những điều này, tôi cần phải giải quyết trường hợp của mình cho các DBA toàn năng của chúng tôi :)

Tuy nhiên, trong trường hợp này, tôi không thể sử dụng cấu trúc INSERT ... EXEC, như sp_help_jobđã sử dụng một cấu trúc. ("Không thể lồng câu lệnh INSERT EXEC.")


3
Tôi đã có 13 trích dẫn liên tiếp trước đó trong động-sql-that-
created

Tôi cần kiểm tra nếu công việc kết thúc. "Một câu lệnh INSERT EXEC không thể được lồng nhau". Tôi ghét bạn Microsoft.
alexkovelsky

11

Để đạt được điều này, đầu tiên bạn tạo một #test_tablelike như dưới đây:

create table #test_table(
    col1 int,
    col2 int,
   .
   .
   .
    col80 int
)

Bây giờ thực hiện thủ tục và đặt giá trị vào #test_table:

insert into #test_table
EXEC MyStoredProc 'param1', 'param2'

Bây giờ bạn lấy giá trị từ #test_table:

select col1,col2....,col80 from #test_table

2
Có lợi thế nào khi tạo bảng tạm thời thay vì biến bảng không?
Dave Kelly

1
giải pháp tốt nhất tôi tìm thấy trên stackoverflow! :)
Umar

4
Điều gì xảy ra nếu tôi chỉ cần một cột từ Thủ tục lưu trữ khác?
Keval Patel

11

Nếu bạn có thể sửa đổi quy trình được lưu trữ của mình, bạn có thể dễ dàng đặt các định nghĩa cột cần thiết làm tham số và sử dụng bảng tạm thời được tạo tự động:

CREATE PROCEDURE sp_GetDiffDataExample
      @columnsStatement NVARCHAR(MAX) -- required columns statement (e.g. "field1, field2")
AS
BEGIN
    DECLARE @query NVARCHAR(MAX)
    SET @query = N'SELECT ' + @columnsStatement + N' INTO ##TempTable FROM dbo.TestTable'
    EXEC sp_executeSql @query
    SELECT * FROM ##TempTable
    DROP TABLE ##TempTable
END

Trong trường hợp này, bạn không cần tạo bảng tạm thời theo cách thủ công - nó được tạo tự động. Hi vọng điêu nay co ich.


Hãy cẩn thận khi sử dụng ## bảng vì chúng được chia sẻ giữa các phiên
eKelvin

1
Bạn có thể tìm thấy một mô tả ngắn về sự khác biệt giữa các bảng # và ## trong stackoverflow.com/a/34081438/352820
eKelvin

Đây có phải là xu hướng SQL tiêm?
Bushrod

11

Nó có thể hữu ích để biết tại sao điều này là rất khó khăn. Một thủ tục được lưu trữ chỉ có thể trả về văn bản (in 'văn bản') hoặc có thể trả về nhiều bảng hoặc có thể không trả lại bảng nào cả.

Vì vậy, một cái gì đó như SELECT * FROM (exec sp_tables) Table1 sẽ không hoạt động


12
SQL Server có thể tự do đưa ra lỗi nếu điều đó xảy ra. ví dụ: nếu tôi viết một truy vấn con trả về nhiều hơn một giá trị. Vâng, nó có thể xảy ra, nhưng trong thực tế thì không. Và ngay cả khi nó đã xảy ra: không khó để gây ra lỗi.
Ian Boyd

8

(Giả sử máy chủ SQL)

Cách duy nhất để làm việc với các kết quả của một thủ tục được lưu trữ trong T-SQL là sử dụng INSERT INTO ... EXECcú pháp. Điều đó cho bạn tùy chọn chèn vào bảng tạm thời hoặc biến bảng và từ đó chọn dữ liệu bạn cần.


1
Điều đó đòi hỏi phải biết định nghĩa bảng. Không hữu ích.
Triynko

8

Một cách nhanh chóng sẽ là thêm một tham số mới '@Column_Name'và có chức năng gọi xác định tên cột sẽ được lấy. Trong phần trả về của sproc của bạn, bạn sẽ có các câu lệnh if / other và chỉ trả về cột được chỉ định hoặc nếu trống - trả về tất cả.

CREATE PROCEDURE [dbo].[MySproc]
        @Column_Name AS VARCHAR(50)
AS
BEGIN
    IF (@Column_Name = 'ColumnName1')
        BEGIN
            SELECT @ColumnItem1 as 'ColumnName1'
        END
    ELSE
        BEGIN
            SELECT @ColumnItem1 as 'ColumnName1', @ColumnItem2 as 'ColumnName2', @ColumnItem3 as 'ColumnName3'
        END
END

7

Nếu bạn đang làm điều này để xác thực dữ liệu thủ công, bạn có thể làm điều này với LINQPad.

Tạo kết nối tới cơ sở dữ liệu trong LinqPad, sau đó tạo các câu lệnh C # tương tự như sau:

DataTable table = MyStoredProc (param1, param2).Tables[0];
(from row in table.AsEnumerable()
 select new
 {
  Col1 = row.Field<string>("col1"),
  Col2 = row.Field<string>("col2"),
 }).Dump();

Tham khảo http://www.global-webnet.net/blogengine/post/2008/09/10/LINEQPAD-Using-Stored-Procedures-Accessing-a-DataSet.aspx


7

Đối với SQL Server, tôi thấy rằng điều này hoạt động tốt:

Tạo một bảng tạm thời (hoặc bảng cố định, không thực sự quan trọng) và thực hiện chèn vào câu lệnh đối với thủ tục được lưu trữ. Tập kết quả của SP phải khớp với các cột trong bảng của bạn, nếu không bạn sẽ gặp lỗi.

Đây là một ví dụ:

DECLARE @temp TABLE (firstname NVARCHAR(30), lastname nvarchar(50));

INSERT INTO @temp EXEC dbo.GetPersonName @param1,@param2;
-- assumption is that dbo.GetPersonName returns a table with firstname / lastname columns

SELECT * FROM @temp;

Đó là nó!


Đối với điều này, cần phải tạo một bản sao của định nghĩa bảng. Có cách nào để tránh nó không?
Hardik

7

Như đã đề cập trong câu hỏi, thật khó để xác định bảng tạm thời 80 cột trước khi thực hiện thủ tục được lưu trữ.

Vì vậy, cách khác để làm điều này là điền vào bảng dựa trên tập kết quả thủ tục được lưu trữ.

SELECT * INTO #temp FROM OPENROWSET('SQLNCLI', 'Server=localhost;Trusted_Connection=yes;'
                                   ,'EXEC MyStoredProc')

Nếu bạn gặp bất kỳ lỗi nào, bạn cần kích hoạt các truy vấn phân tán ad hoc bằng cách thực hiện truy vấn sau.

sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO
sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO

Để thực thi sp_configurevới cả hai tham số để thay đổi tùy chọn cấu hình hoặc để chạy RECONFIGUREcâu lệnh, bạn phải được cấp quyền ALTER SETTINGSmáy chủ

Bây giờ bạn có thể chọn các cột cụ thể của mình từ bảng đã tạo

SELECT col1, col2
FROM #temp

4

thử cái này

use mydatabase
create procedure sp_onetwothree as
select 1 as '1', 2 as '2', 3 as '3'
go
SELECT a.[1], a.[2]
FROM OPENROWSET('SQLOLEDB','myserver';'sa';'mysapass',
    'exec mydatabase.dbo.sp_onetwothree') AS a
GO

1
Lol - anh ấy đã không làm thế. Ông đã mã hóa nó thành lời gọi của thủ tục được lưu trữ thay vào đó nó có thể dễ dàng lấy được hơn mà không cần truy cập vào cơ sở dữ liệu bằng cách đánh hơi mạng.
Martin Milan

Khá dễ dàng để lấy nó ra khỏi Github.
Tên của

3

Tôi biết thực thi từ sp và chèn vào bảng tạm thời hoặc biến bảng sẽ là một tùy chọn nhưng tôi không nghĩ đó là yêu cầu của bạn. Theo yêu cầu của bạn, câu lệnh truy vấn dưới đây sẽ hoạt động:

Declare @sql nvarchar(max)
Set @sql='SELECT   col1, col2 FROM OPENROWSET(''SQLNCLI'', ''Server=(local);uid=test;pwd=test'',
     ''EXEC MyStoredProc ''''param1'''', ''''param2'''''')'
 Exec(@sql)

nếu bạn có kết nối đáng tin cậy thì hãy sử dụng câu lệnh bên dưới:

Declare @sql nvarchar(max)
Set @sql='SELECT   col1, col2 FROM OPENROWSET(''SQLNCLI'', ''Server=(local);Trusted_Connection=yes;'',
     ''EXEC MyStoredProc ''''param1'''', ''''param2'''''')'
 Exec(@sql)

Nếu bạn gặp lỗi khi chạy câu lệnh trên thì chỉ cần chạy câu lệnh dưới đây:

sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO
sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO

Tôi hy vọng điều này sẽ giúp được ai đó sẽ phải đối mặt với loại vấn đề tương tự này. Nếu ai đó muốn thử với bảng tạm thời hoặc biến bảng nên như thế này bên dưới nhưng trong trường hợp này, bạn nên biết sp của mình trả về bao nhiêu cột thì bạn nên tạo nhiều cột đó trong bảng tạm thời hoặc biến bảng:

--for table variable 
Declare @t table(col1 col1Type, col2 col2Type)
insert into @t exec MyStoredProc 'param1', 'param2'
SELECT col1, col2 FROM @t

--for temp table
create table #t(col1 col1Type, col2 col2Type)
insert into #t exec MyStoredProc 'param1', 'param2'
SELECT col1, col2 FROM #t

1

Đối với bất kỳ ai có SQL 2012 trở lên, tôi có thể thực hiện việc này với các quy trình được lưu trữ không động và có cùng một đầu ra cột mỗi lần.

Ý tưởng chung là tôi xây dựng truy vấn động để tạo, chèn vào, chọn và thả bảng tạm thời và thực hiện điều này sau khi tất cả được tạo. Tôi tự động tạo bảng tạm thời bằng cách truy xuất tên và kiểu cột đầu tiên từ thủ tục được lưu trữ .

Lưu ý: có nhiều giải pháp tốt hơn, phổ quát hơn sẽ hoạt động với ít dòng mã hơn nếu bạn sẵn sàng / có thể cập nhật SP hoặc thay đổi cấu hình và sử dụng OPENROWSET. Sử dụng dưới đây nếu bạn không có cách nào khác.

DECLARE @spName VARCHAR(MAX) = 'MyStoredProc'
DECLARE @tempTableName VARCHAR(MAX) = '#tempTable'

-- might need to update this if your param value is a string and you need to escape quotes
DECLARE @insertCommand VARCHAR(MAX) = 'INSERT INTO ' + @tempTableName + ' EXEC MyStoredProc @param=value'

DECLARE @createTableCommand VARCHAR(MAX)

-- update this to select the columns you want
DECLARE @selectCommand VARCHAR(MAX) = 'SELECT col1, col2 FROM ' + @tempTableName

DECLARE @dropCommand VARCHAR(MAX) = 'DROP TABLE ' + @tempTableName

-- Generate command to create temp table
SELECT @createTableCommand = 'CREATE TABLE ' + @tempTableName + ' (' +
    STUFF
    (
        (
            SELECT ', ' + CONCAT('[', name, ']', ' ', system_type_name)
            FROM sys.dm_exec_describe_first_result_set_for_object
            (
              OBJECT_ID(@spName), 
              NULL
            )
            FOR XML PATH('')
        )
        ,1
        ,1
        ,''
    ) + ')'

EXEC( @createTableCommand + ' '+ @insertCommand + ' ' + @selectCommand + ' ' + @dropCommand)

0

Cách dễ nhất để làm nếu bạn chỉ cần điều này một lần:

Xuất ra excel trong Trình hướng dẫn Nhập và Xuất rồi nhập excel này vào bảng.


4
Toàn bộ điểm tạo ra một lưu trữ được lưu trữ là tái sử dụng. Câu trả lời của bạn hoàn toàn mâu thuẫn với điều đó.
deutschZuid

6
Để chống lại deutschZuid, trong bài viết gốc, anh ta không đề cập đến việc anh ta có muốn sử dụng lại điều này hay không nếu anh ta chỉ cố gắng xem qua kết quả của một Proc được lưu trữ. Martin đã đúng, đây có lẽ là cách dễ nhất nếu anh ta chỉ cần làm một lần.
Đức cha

0

Tạo chế độ xem động và nhận kết quả từ nó .......

CREATE PROCEDURE dbo.usp_userwise_columns_value
(
    @userid BIGINT
)
AS 
BEGIN
        DECLARE @maincmd NVARCHAR(max);
        DECLARE @columnlist NVARCHAR(max);
        DECLARE @columnname VARCHAR(150);
        DECLARE @nickname VARCHAR(50);

        SET @maincmd = '';
        SET @columnname = '';
        SET @columnlist = '';
        SET @nickname = '';

        DECLARE CUR_COLUMNLIST CURSOR FAST_FORWARD
        FOR
            SELECT columnname , nickname
            FROM dbo.v_userwise_columns 
            WHERE userid = @userid

        OPEN CUR_COLUMNLIST
        IF @@ERROR <> 0
            BEGIN
                ROLLBACK
                RETURN
            END   

        FETCH NEXT FROM CUR_COLUMNLIST
        INTO @columnname, @nickname

        WHILE @@FETCH_STATUS = 0
            BEGIN
                SET @columnlist = @columnlist + @columnname + ','

                FETCH NEXT FROM CUR_COLUMNLIST
                INTO @columnname, @nickname
            END
        CLOSE CUR_COLUMNLIST
        DEALLOCATE CUR_COLUMNLIST  

        IF NOT EXISTS (SELECT * FROM sys.views WHERE name = 'v_userwise_columns_value')
            BEGIN
                SET @maincmd = 'CREATE VIEW dbo.v_userwise_columns_value AS SELECT sjoid, CONVERT(BIGINT, ' + CONVERT(VARCHAR(10), @userid) + ') as userid , ' 
                            + CHAR(39) + @nickname + CHAR(39) + ' as nickname, ' 
                            + @columnlist + ' compcode FROM dbo.SJOTran '
            END
        ELSE
            BEGIN
                SET @maincmd = 'ALTER VIEW dbo.v_userwise_columns_value AS SELECT sjoid, CONVERT(BIGINT, ' + CONVERT(VARCHAR(10), @userid) + ') as userid , ' 
                            + CHAR(39) + @nickname + CHAR(39) + ' as nickname, ' 
                            + @columnlist + ' compcode FROM dbo.SJOTran '
            END

        --PRINT @maincmd
        EXECUTE sp_executesql @maincmd
END

-----------------------------------------------
SELECT * FROM dbo.v_userwise_columns_value

-1

Tôi sẽ cắt và dán SP gốc và xóa tất cả các cột trừ 2 cột bạn muốn. Hoặc là. Tôi sẽ đưa kết quả được đặt lại, ánh xạ nó tới một đối tượng kinh doanh phù hợp, sau đó LINQ ra hai cột.


1
Mọi người đừng làm điều này. Điều này sẽ vi phạm Nguyên tắc DRY. Khi mọi thứ thay đổi, không phải bây giờ, bạn sẽ cần theo dõi và nhập thay đổi của mình ở tất cả các vị trí.
jriver27
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.