Pilih kolom dari himpunan hasil prosedur tersimpan

448

Saya memiliki prosedur tersimpan yang mengembalikan 80 kolom, dan 300 baris. Saya ingin menulis pilih yang mendapat 2 kolom tersebut. Sesuatu seperti

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

Ketika saya menggunakan sintaks di atas saya mendapatkan kesalahan:

"Nama Kolom Tidak Valid".

Saya tahu solusi termudah adalah mengubah prosedur yang tersimpan, tetapi saya tidak menulisnya, dan saya tidak bisa mengubahnya.

Apakah ada cara untuk melakukan apa yang saya inginkan?

  • Saya bisa membuat tabel temp untuk memasukkan hasilnya, tetapi karena ada 80 kolom jadi saya perlu membuat tabel temp 80 kolom hanya untuk mendapatkan 2 kolom. Saya ingin menghindari melacak semua kolom yang dikembalikan.

  • Saya mencoba menggunakan WITH SprocResults AS ....seperti yang disarankan oleh Mark, tetapi saya mendapat 2 kesalahan

    Sintaksis salah di dekat kata kunci 'EXEC'.
    Sintaksis salah dekat ')'.

  • Saya mencoba mendeklarasikan variabel tabel dan saya mendapatkan kesalahan berikut

    Sisipkan Kesalahan: Nama kolom atau jumlah nilai yang diberikan tidak cocok dengan definisi tabel

  • Jika saya mencoba
    SELECT * FROM EXEC MyStoredProc 'param1', 'param2'
    saya mendapatkan kesalahan:

    Sintaksis salah di dekat kata kunci 'exec'.

Rossini
sumber
Karena penasaran, apakah kueri ini berfungsi: PILIH * DARI EXEC MyStoredProc 'param1', 'param2' Jika demikian, nama kolom apa yang ditampilkan dalam kumpulan hasil, dan dapatkah Anda menggunakan nama kolom itu dalam daftar pilihan Anda?
Dave Costa
5
Saya tidak pernah menemukan jawaban untuk ini.
Rossini
32
Yah, Anda tidak pernah menjawab pertanyaan yang sangat penting! Platform SQL apa yang Anda tanyakan? MySQL, Microsoft SQL Server, Oracle, dll. Sepertinya saya seperti SQL Server, tetapi Anda perlu memberi tahu orang lain atau mereka tidak dapat menjawab pertanyaan Anda dengan andal.
JMTyler
6
Yah, itu pasti MS-SQL. EXECbukan kata kunci MySQL (padanan yang dipersiapkan MySQL adalah pernyataan ) Meskipun saya ingin tahu jawaban untuk MySQL, jawaban di bawah ini menargetkan T-SQL. Retagging.
bobobobo
1
Saya tidak pernah menemukan jawaban untuk ini
Rossini

Jawaban:

186

Bisakah Anda membagi kueri? Masukkan hasil proc yang disimpan ke dalam variabel tabel atau tabel temp. Kemudian, pilih 2 kolom dari variabel tabel.

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

SELECT col1, col2 FROM @tablevar
Gulzar Nazim
sumber
27
Ini juga tidak berfungsi ketika Anda tidak tahu definisi tabel
Ian Boyd
tidak tahu tentang tipe itu. Apakah mereka diterapkan sama dengan tabel temp? Atau itu hanya dalam memori?
d -_- b
Ini menarik: sqlnerd.blogspot.com/2005/09/...
d -_- b
Ini berfungsi dengan baik jika jumlah kolom yang disediakan dalam tabel temp adalah sama dengan yang ada di output dari prosedur tersimpan. Chagbert.
Chagbert
83

Berikut ini tautan ke dokumen yang cukup bagus menjelaskan semua cara berbeda untuk menyelesaikan masalah Anda (walaupun banyak dari mereka tidak dapat digunakan karena Anda tidak dapat mengubah prosedur tersimpan yang ada.)

Cara Berbagi Data Antara Prosedur yang Disimpan

Jawaban Gulzar akan bekerja (ini didokumentasikan dalam tautan di atas) tetapi akan merepotkan untuk menulis (Anda harus menentukan semua 80 nama kolom dalam pernyataan @tablevar (col1, ...) Anda. Dan di masa mendatang jika kolom ditambahkan ke skema atau output diubah, maka perlu diperbarui dalam kode Anda atau akan keluar kesalahan.

Lance McNearney
sumber
1
Saya pikir saran OPENQUERY di tautan itu lebih dekat dengan apa yang dicari OP.
Corin
80
CREATE TABLE #Result
(
  ID int,  Name varchar(500), Revenue money
)
INSERT #Result EXEC RevenueByAdvertiser '1/1/10', '2/1/10'
SELECT * FROM #Result ORDER BY Name
DROP TABLE #Result

Sumber:
http://stevesmithblog.com/blog/select-from-a-stored-procedure/

Peter Nazarov
sumber
@LawfulHacker Holy merokok. Apa yang Anda lakukan pada SQL Server 2000 di tahun 2014?
John Zabroski
1
Perusahaan besar dengan sistem warisan: D
LawfulHacker
39

Ini berfungsi untuk saya: (yaitu saya hanya membutuhkan 2 kolom dari 30+ yang dikembalikan oleh sp_help_job)

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

Sebelum ini berhasil, saya perlu menjalankan ini:

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

.... untuk memperbarui sys.serverstabel. (mis. Menggunakan referensi-sendiri dalam OPENQUERY tampaknya dinonaktifkan secara default.)

Untuk persyaratan sederhana saya, saya tidak menemukan masalah yang dijelaskan di bagian OPENQUERY dari tautan luar biasa Lance.

Rossini, jika Anda perlu mengatur parameter input secara dinamis, maka penggunaan OPENQUERY menjadi sedikit lebih fiddly:

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);

Saya tidak yakin tentang perbedaan (jika ada) antara menggunakan sp_serveroptionuntuk memperbarui sys.serversreferensi diri yang ada secara langsung, vs menggunakansp_addlinkedserver (seperti yang dijelaskan dalam tautan Lance) untuk membuat duplikat / alias.

Catatan 1: Saya lebih suka OPENQUERY daripada OPENROWSET, mengingat bahwa OPENQUERY tidak memerlukan definisi string-koneksi dalam proc.

Catatan 2: Setelah mengatakan semua ini: biasanya saya hanya akan menggunakan INSERT ... EXEC :) Ya, ini adalah pengetikan ekstra 10 menit, tetapi jika saya dapat membantu, saya lebih suka untuk tidak bermain-main dengan:
(a) kutipan dalam tanda kutip di dalam kutipan, dan
(b) tabel sistem, dan / atau pengaturan Linked Linked server referensi diri licik (yaitu untuk ini, saya perlu memohon kasus saya untuk semua DBA kuat kami :)

Namun dalam contoh ini, saya tidak bisa menggunakan INSERT ... EXEC, seperti sp_help_jobyang sudah digunakan. ("Pernyataan INSERT EXEC tidak dapat disarangkan.")

Merenzo
sumber
3
Saya memiliki 13 kutipan tunggal berturut-turut sebelumnya di dynamic-sql-that-generate-dynamic-sql-that-generate-dynamic-sql ...
ErikE
Saya perlu memeriksa apakah pekerjaan sudah selesai. "Pernyataan INSERT EXEC tidak dapat disarangkan". Aku benci kamu, Microsoft.
alexkovelsky
11

Untuk mencapai ini, pertama Anda buat #test_tableseperti di bawah ini:

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

Sekarang jalankan prosedur dan beri nilai #test_table:

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

Sekarang Anda mengambil nilai dari #test_table:

select col1,col2....,col80 from #test_table
Navneet
sumber
2
Apakah ada keuntungan untuk membuat tabel temp daripada variabel tabel?
Dave Kelly
1
solusi terbaik yang saya temukan di stackoverflow! :)
Umar
4
Bagaimana jika saya hanya membutuhkan satu kolom dari Prosedur Tersimpan lainnya?
Keval Patel
11

Jika Anda dapat memodifikasi prosedur tersimpan Anda, Anda dapat dengan mudah menempatkan definisi kolom yang diperlukan sebagai parameter dan menggunakan tabel sementara yang dibuat secara otomatis:

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

Dalam hal ini Anda tidak perlu membuat tabel temp secara manual - ini dibuat secara otomatis. Semoga ini membantu.

dyatchenko
sumber
Berhati-hatilah menggunakan tabel ## karena dibagi di antara sesi
eKelvin
1
Anda dapat menemukan deskripsi singkat untuk perbedaan antara # dan ## tabel di stackoverflow.com/a/34081438/352820
eKelvin
Apakah ini rentan terhadap SQL Injection?
Bushrod
11

Mungkin bermanfaat untuk mengetahui mengapa ini sangat sulit. Prosedur tersimpan hanya dapat mengembalikan teks (cetak 'teks'), atau dapat mengembalikan beberapa tabel, atau mungkin tidak mengembalikan tabel sama sekali.

Jadi sesuatu seperti SELECT * FROM (exec sp_tables) Table1 tidak akan berfungsi

pemula07
sumber
12
SQL Server bebas untuk meningkatkan kesalahan jika itu terjadi. mis. jika saya menulis subquery yang mengembalikan lebih dari satu nilai. Ya itu bisa terjadi, tetapi kenyataannya tidak. Dan bahkan jika itu terjadi: tidak sulit untuk meningkatkan kesalahan.
Ian Boyd
8

(Mengasumsikan SQL Server)

Satu-satunya cara untuk bekerja dengan hasil prosedur yang tersimpan di T-SQL adalah dengan menggunakan INSERT INTO ... EXECsintaks. Itu memberi Anda opsi memasukkan ke dalam tabel temp atau variabel tabel dan dari sana memilih data yang Anda butuhkan.

Brannon
sumber
1
Itu membutuhkan mengetahui definisi tabel. Tidak berguna.
Triynko
8

Peretasan cepat adalah dengan menambahkan parameter baru '@Column_Name'dan membuat fungsi panggilan menentukan nama kolom yang akan diambil. Di bagian pengembalian sproc Anda, Anda akan memiliki pernyataan if / else dan hanya mengembalikan kolom yang ditentukan, atau jika kosong - kembalikan semua.

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
Samir Basic
sumber
7

Jika Anda melakukan ini untuk validasi data secara manual, Anda dapat melakukannya dengan LINQPad.

Buat koneksi ke database di LinqPad kemudian buat pernyataan C # yang mirip dengan yang berikut:

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();

Referensi http://www.global-webnet.net/blogengine/post/2008/09/10/LINQPAD-Using-Stored-Procedures-Accessing-a-DataSet.aspx

ShawnFeatherly
sumber
7

Untuk SQL Server, saya menemukan ini berfungsi dengan baik:

Buat tabel temp (atau tabel permanen, tidak terlalu penting), dan lakukan penyisipan terhadap prosedur tersimpan. Rangkaian hasil SP harus cocok dengan kolom di tabel Anda, jika tidak Anda akan mendapatkan kesalahan.

Ini sebuah contoh:

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;

Itu dia!

LTMOD
sumber
Untuk ini, perlu membuat salinan definisi tabel. Apakah ada cara untuk menghindarinya?
Hardik
7

Seperti yang telah disebutkan dalam pertanyaan, sulit untuk menentukan tabel temp 80 kolom sebelum menjalankan prosedur tersimpan.

Jadi cara sebaliknya adalah mengisi tabel berdasarkan pada set hasil prosedur tersimpan.

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

Jika Anda mendapatkan kesalahan, Anda harus mengaktifkan kueri yang didistribusikan secara ad hoc dengan menjalankan kueri berikut.

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

Untuk mengeksekusi sp_configuredengan kedua parameter untuk mengubah opsi konfigurasi atau untuk menjalankan RECONFIGUREpernyataan, Anda harus diberikan ALTER SETTINGSizin tingkat server

Sekarang Anda dapat memilih kolom spesifik Anda dari tabel yang dihasilkan

SELECT col1, col2
FROM #temp
sqluser
sumber
4

coba ini

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
SelvirK
sumber
1
Lol - dia tidak. Dia mengkodekannya ke dalam doa prosedur tersimpan di mana itu bisa jauh lebih mudah diperoleh tanpa akses ke database oleh jaringan sniffing.
Martin Milan
Cukup mudah untuk mengeluarkannya dari Github juga.
Nomen
3

Saya tahu mengeksekusi dari sp dan memasukkan ke dalam tabel temp atau variabel tabel akan menjadi pilihan tapi saya tidak berpikir itu kebutuhan Anda. Sesuai kebutuhan Anda, pernyataan kueri di bawah ini akan berfungsi:

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

jika Anda memiliki koneksi tepercaya maka gunakan pernyataan kueri di bawah ini:

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

jika Anda mendapatkan kesalahan untuk menjalankan pernyataan di atas maka jalankan saja pernyataan ini di bawah:

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

Saya harap ini akan membantu seseorang yang akan menghadapi masalah serupa ini. Jika seseorang ingin mencoba dengan tabel temp atau variabel tabel yang seharusnya seperti ini di bawah ini tetapi dalam skenario ini Anda harus tahu berapa banyak kolom sp Anda kembali maka Anda harus membuat banyak kolom dalam tabel temp atau variabel tabel:

--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
Humayoun_Kabir
sumber
1

Bagi siapa pun yang memiliki SQL 2012 atau lebih baru, saya bisa menyelesaikan ini dengan prosedur tersimpan yang tidak dinamis dan memiliki output kolom yang sama setiap kali.

Gagasan umum adalah saya membuat kueri dinamis untuk membuat, menyisipkan ke dalam, memilih dari, dan menjatuhkan tabel temp, dan jalankan ini setelah semuanya dihasilkan. Saya secara dinamis menghasilkan tabel temp dengan terlebih dahulu mengambil nama dan tipe kolom dari prosedur tersimpan .

Catatan: ada banyak solusi yang lebih baik dan lebih universal yang akan bekerja dengan lebih sedikit baris kode jika Anda bersedia / dapat memperbarui SP atau mengubah konfigurasi dan penggunaan OPENROWSET. Gunakan di bawah ini jika Anda tidak punya cara lain.

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)
Emil
sumber
0

Cara termudah untuk dilakukan jika Anda hanya perlu ini sekali:

Ekspor ke excel di Impor dan Ekspor panduan dan kemudian impor excel ini ke dalam tabel.

Martijn Tromp
sumber
4
Inti dari pembuatan proc yang disimpan adalah usabilitas ulang. Jawaban Anda sangat bertentangan dengan itu.
deutschZuid
6
Untuk mengatasi deutschZuid, dalam postingan asli, dia tidak menyebutkan apakah dia ingin menggunakan ini atau tidak atau dia hanya mencoba melihat-lihat hasil dari proc yang disimpan. Martin benar, ini mungkin cara termudah jika ia hanya perlu melakukannya sekali.
Uskup
0

Buat tampilan dinamis dan dapatkan hasilnya dari .......

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
Nilesh Umaretiya
sumber
-1

Saya akan memotong dan menempelkan SP asli dan menghapus semua kolom kecuali 2 yang Anda inginkan. Atau. Saya akan mengembalikan set hasil, memetakannya ke objek bisnis yang tepat, lalu LINQ keluar dua kolom.

Andrew
sumber
1
Orang tidak melakukan ini. Ini akan melanggar Prinsip KERING. Ketika segalanya berubah, bukan jika, Anda sekarang perlu melacak dan memasukkan perubahan Anda di semua lokasi.
jriver27