Saklı yordamın sonuç kümesinden sütunları seçme


448

80 sütun ve 300 satır döndüren bir saklı yordam var. Bu sütunlardan 2'sini alan bir seçim yazmak istiyorum. Gibi bir şey

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

Yukarıdaki sözdizimini kullandığımda hatayı alıyorum:

"Geçersiz sütun ismi".

Saklı yordamı değiştirmek için en kolay çözüm olacağını biliyorum, ama yazmadım ve değiştiremiyorum.

İstediğimi yapmanın bir yolu var mı?

  • Sonuçları koymak için bir geçici tablo yapabilir, ancak 80 sütun olduğundan, sadece 2 sütun almak için 80 sütun geçici tablo yapmak gerekir. Döndürülen tüm sütunları izlemekten kaçınmak istedim.

  • WITH SprocResults AS ....Mark'ın önerdiği şekilde kullanmayı denedim , ancak 2 hata aldım

    'EXEC' anahtar kelimesinin yanında yanlış sözdizimi.
    Yanlış sözdizimi yanında ')'.

  • Bir tablo değişkeni bildirmeye çalıştım ve aşağıdaki hatayı aldım

    Ekleme Hatası: Sütun adı veya sağlanan değerlerin sayısı tablo tanımıyla eşleşmiyor

  • Eğer denersem
    SELECT * FROM EXEC MyStoredProc 'param1', 'param2'
    hatayı alıyorum:

    'Exec' anahtar kelimesinin yanında yanlış sözdizimi.


Merak etme, bu sorgu işe yarıyor: SELECT * EXEC EXS MyStoredProc 'param1', 'param2' Öyleyse, sonuç kümesinde hangi sütun adlarını görüntüler ve bu sütun adlarını seçim listenizde kullanabilir misiniz?
Dave Costa

5
Bunun için bir cevap bulamadım.
Rossini

32
Asla çok önemli bir soruya cevap vermediniz! Hangi SQL platformunu soruyorsunuz? MySQL, Microsoft SQL Server, Oracle, vs. Bana SQL Server gibi görünüyor, ama insanlara söylemeniz gerekiyor ya da sorunuzu güvenilir bir şekilde cevaplayamıyorlar.
JMTyler

6
Peki, MS-SQL olmalı. EXECbir MySQL anahtar kelimesi değildir (MySQL eşdeğeri ifadeler hazırlanmıştır ). MySQL'in cevabını bilmek istesem de, aşağıdaki cevaplar T-SQL'i hedefliyor. Yeniden Etiketleme.
bobobobo

1
Bunun için bir cevap bulamadım
Rossini

Yanıtlar:


186

Sorguyu bölebilir misiniz? Saklanan proc sonuçlarını bir tablo değişkenine veya geçici tabloya ekleyin. Ardından, tablo değişkeninden 2 sütunu seçin.

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

SELECT col1, col2 FROM @tablevar

27
Tablo tanımını bilmediğinizde de çalışmaz
Ian Boyd

bu tür hakkında bilmiyordum. Geçici tablolarla aynı şekilde mi uygulanıyorlar? Yoksa kesinlikle hafızada mı?
d -_- b


Geçici tabloda verilen sütun sayısı, saklı yordamın çıktısındaki ile aynı ise, bu iyi çalışır. chagbert.
Chagbert

83

Sorununuzu çözmenin tüm farklı yollarını açıklayan oldukça iyi bir belgenin bağlantısı (mevcut saklı yordamı değiştiremediğiniz için birçoğu kullanılamasa da).

Saklı Yordamlar Arasında Veri Paylaşma

Gulzar'ın cevabı işe yarar (yukarıdaki bağlantıda belgelenmiştir), ancak yazmak için bir güçlük olacaktır (@tablevar (col1, ...) ifadenizde 80 sütun adının tümünü belirtmeniz gerekecektir. şemaya bir sütun eklenirse veya çıktı değiştirilirse, kodunuzda güncellenmesi gerekir veya hata verir.


1
Ben bu bağlantıda OPENQUERY öneri OP aradığı şey çok daha yakın olduğunu düşünüyorum.
Corin


39

Bu benim için çalışıyor: (yani ben tarafından iade 30 + sadece 2 sütun gerekir sp_help_job)

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

Bu işe yaramadan önce bunu çalıştırmam gerekiyordu:

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

... sys.serverstabloyu güncellemek için . (yani OPENQUERY içinde bir öz referans kullanmak varsayılan olarak devre dışı bırakılmış gibi görünür.)

Basit gereksinimim için Lance'in mükemmel bağlantısının AÇIK bölümünde açıklanan sorunların hiçbirine rastlamadım .

Rossini, bu giriş parametrelerini dinamik olarak ayarlamanız gerekiyorsa, OPENQUERY kullanımı biraz daha zorlaşır:

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

sp_serveroptionMevcut sys.serversöz referansı doğrudan güncellemek için kullanma ile sp_addlinkedserveryinelenen / takma ad oluşturmak için (Lance'in bağlantısında açıklandığı gibi ) kullanma arasındaki farklardan (varsa) emin değilim .

Not 1: OPENQUERY'nin proc içinde bağlantı dizesi tanımı gerektirmediği göz önüne alındığında OPENQUERY'i OPENROWSET yerine tercih ederim.

Not 2: Tüm bunları söyledikten sonra: normalde sadece INSERT ... EXEC :) kullanırım Evet, 10 dakika ekstra yazım, ancak yardımcı olabilirsem, etrafında jigger olmamayı tercih ederim:
(a) içinde tırnak işaretleri tırnak ve
(b) sys tabloları ve / veya gizli kendinden referanslı Bağlantılı Sunucu kurulumları (yani bunlar için, benim durumumun tüm güçlü DBA'larımıza yalvarmam gerekiyor :)

Ancak bu örnekte, sp_help_jobzaten kullandığımız gibi INSERT ... EXEC yapısını kullanamadım . ("INSERT EXEC deyimi iç içe olamaz.")


3
Dinamik-sql-o-oluşturulan-dinamik-sql-o-oluşturulan-dinamik-sql daha önce üst üste 13 tek tırnak yaşadım ...
ErikE

İşin bitip bitmediğini kontrol etmem gerekiyor. Msgstr "INSERT EXEC deyimi iç içe olamaz". Senden nefret ediyorum Microsoft.
alexkovelsky

11

Bunu başarmak için önce #test_tableaşağıda bir benzer oluşturun :

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

Şimdi prosedürü yürütün ve değeri şuraya koyun #test_table:

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

Şimdi, değeri şuradan alırsınız #test_table:

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

2
Tablo değişkeni yerine geçici tablo oluşturmanın bir avantajı var mı?
Dave Kelly

1
stackoverflow bulunan en iyi çözüm! :)
Umar

4
Diğer Saklı Yordamdan yalnızca bir sütuna ihtiyacım olursa ne olur?
Keval Patel

11

Saklı yordamınızı değiştirebiliyorsanız, gerekli sütun tanımlarını kolayca parametre olarak ekleyebilir ve otomatik olarak oluşturulan geçici bir tablo kullanabilirsiniz:

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

Bu durumda elle geçici bir tablo oluşturmanız gerekmez - otomatik olarak oluşturulur. Bu yardımcı olur umarım.


Oturumlar arasında paylaşıldıkları için ## tablolarını kullanırken dikkatli olun
eKelvin

1
# Ve ## tabloları arasındaki farklar için kısa bir açıklamayı stackoverflow.com/a/34081438/352820
eKelvin

Bu SQL Enjeksiyonuna eğilimli mi?
Bushrod

11

Bunun neden bu kadar zor olduğunu bilmek faydalı olabilir. Saklı yordam yalnızca metin döndürebilir ('metin' yazdırır) veya birden çok tablo döndürebilir veya hiç tablo döndürmeyebilir.

Yani böyle bir şey SELECT * FROM (exec sp_tables) Table1 işe yaramayacak


12
SQL Server, bu durumda bir hata oluşturmakta serbesttir. örneğin birden fazla değer döndüren bir alt sorgu yazarsanız. Evet bu olabilir olur, ama gerçekte öyle değil. Ve öyle olsa bile: bir hata oluşturmak zor değildir.
Ian Boyd

8

(SQL Server varsayıldığında)

T-SQL'de saklı yordamın sonuçlarıyla çalışmanın tek yolu INSERT INTO ... EXECsözdizimini kullanmaktır . Bu size geçici tabloya veya tablo değişkenine ekleme ve oradan ihtiyacınız olan verileri seçme seçeneği sunar.


1
Bu tablo tanımını bilmeyi gerektirir. Yararlı değil.
Triynko

8

Hızlı bir saldırı, yeni bir parametre eklemek '@Column_Name've çağrılama işlevinin alınacak sütun adını tanımlamasını sağlamaktır. Sproc'nizin dönüş bölümünde if / else deyimine ve yalnızca belirtilen sütunu döndürür veya boşsa - tümünü döndürürsünüz.

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

Verileri manuel olarak doğrulamak için bunu yapıyorsanız, bunu LINQPad ile yapabilirsiniz.

LinqPad'de veritabanına bir bağlantı oluşturun ve sonra aşağıdakine benzer C # ifadeleri oluşturun:

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

Referans http://www.global-webnet.net/blogengine/post/2008/09/10/LINQPAD-Using-Stored-Procedures-Accessing-a bazıSet.aspx


7

SQL Server için bunun işe yaradığını buluyorum:

Bir geçici tablo (veya kalıcı tablo, gerçekten önemli değil) oluşturun ve saklı yordama karşı deyime bir ekleme yapın. SP sonuç kümesi tablonuzdaki sütunlarla eşleşmelidir, aksi takdirde bir hata alırsınız.

İşte bir örnek:

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;

Bu kadar!


Bunun için tablo tanımının bir kopyasını oluşturmanız gerekir. Bundan kaçınmanın bir yolu var mı?
Hardik

7

Soruda bahsedildiği gibi, saklı yordamı uygulamadan önce 80 sütun geçici tablosunu tanımlamak zordur.

Bu nedenle, diğer bir yol da, saklı yordam sonuç kümesine göre tablo doldurmaktır.

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

Herhangi bir hata alıyorsanız, aşağıdaki sorguyu yürüterek geçici dağıtılmış sorguları etkinleştirmeniz gerekir.

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

sp_configureBir yapılandırma seçeneğini değiştirmek veya RECONFIGUREifadeyi çalıştırmak için her iki parametreyle yürütmek için , ALTER SETTINGSsunucu düzeyinde izin almanız gerekir

Artık oluşturulan tablodan belirli sütunlarınızı seçebilirsiniz

SELECT col1, col2
FROM #temp

4

bunu dene

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 - yapmadı. Bunu, ağ koklama ile veritabanına erişim olmadan çok daha kolay elde edilebileceği saklı yordamın çağrılmasıyla kodladı.
Martin Milan

Github'dan çıkarmak oldukça kolay.
nomen

3

Sp yürütme ve geçici tablo veya tablo değişkeni içine eklemek bir seçenek olacağını biliyorum ama bu sizin şart olduğunu sanmıyorum. Gereksiniminize göre aşağıdaki sorgu deyimi çalışmalıdır:

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

güvenilen bağlantınız varsa aşağıdaki sorgu deyimini kullanın:

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

yukarıdaki ifadeyi çalıştırmak için hata alıyorsanız, aşağıdaki ifadeyi çalıştırın:

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

Umarım bu tür bir sorunla karşılaşmış birine yardım eder. Birisi aşağıdaki gibi olması gereken geçici tablo veya tablo değişkeni ile denemek istiyor, ancak bu senaryoda sp'nizin kaç sütun döndürdüğünü bilmelisiniz, o zaman geçici tablo veya tablo değişkeninde bu kadar sütun oluşturmalısınız:

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

SQL 2012 veya daha yenisi olan herkes için bunu dinamik olmayan ve her seferinde aynı sütun çıktısına sahip saklı yordamlarla başardım.

Genel fikir, geçici tablo oluşturmak, içine eklemek, seçim yapmak ve geçici tabloyu bırakmak ve tüm oluşturulan sonra bu yürütmek için dinamik sorgu oluşturmak olduğunu. İlk olarak saklı yordamdan sütun adlarını ve türlerini alarak geçici tabloyu dinamik olarak oluşturun .

Not: SP'yi güncellemek veya yapılandırmayı ve kullanımı değiştirmek istiyorsanız / daha az kod satırıyla çalışacak çok daha iyi, daha evrensel çözümler vardır OPENROWSET. Başka yolunuz yoksa aşağıdakileri kullanın.

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

Sadece bir kez ihtiyacınız varsa yapmanın en kolay yolu:

İçe ve Dışa Aktar sihirbazında excel'e dışa aktarın ve ardından bu excel'i bir tabloya içe aktarın.


4
Depolanmış bir proc yaratmanın tüm noktası yeniden kullanılabilirliktir. Cevabınız tamamen bununla çelişiyor.
deutschZuid

6
DeutschZuid'e karşı çıkmak için, orijinal gönderide, bunu tekrar kullanmak isteyip istemediğinden veya sadece depolanan bir işlemin sonuçlarını araştırmaya çalışıp çalışmadığından bahsetmez. Martin haklı, sadece bir kez yapması gerekiyorsa muhtemelen en kolay yol budur.
Bishop

0

Dinamik bir görünüm oluşturun ve ondan sonuç alı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

Orijinal SP'yi kesip yapıştırır ve istediğiniz 2 dışındaki tüm sütunları silerim. Veya. Sonuç kümesini geri getiririm, uygun bir iş nesnesine eşlerim, sonra iki sütunu dışarı çıkarırdım.


1
İnsanlar bunu yapma. Bu, KURU Prensibini ihlal edecektir. İşler değiştiğinde, değişmezse, artık tüm konumlardaki değişikliğinizi izlemeniz ve girmeniz gerekecek.
jriver27
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.