แทรกผลลัพธ์ของกระบวนงานที่เก็บไว้ในตารางชั่วคราว


1578

ฉันจะทำSELECT * INTO [temp table] FROM [stored procedure]อย่างไร ไม่ได้FROM [Table]และไม่มีการกำหนด[temp table]?

Select ข้อมูลทั้งหมดจาก BusinessLineไปยังtmpBusLineทำงานได้ดี

select *
into tmpBusLine
from BusinessLine

ฉันพยายามแบบเดียวกัน แต่ใช้ stored procedureที่คืนค่านั้นไม่เหมือนกัน

select *
into tmpBusLine
from
exec getBusinessLineHistory '16 Mar 2009'

ข้อความที่ส่งออก:

ข่าวสารเกี่ยวกับ 156, ระดับ 15, สถานะ 1, บรรทัด 2 ไวยากรณ์ไม่ถูกต้องใกล้กับคำหลัก 'exec'

ฉันได้อ่านหลายตัวอย่างของการสร้างตารางชั่วคราวที่มีโครงสร้างเดียวกันกับขั้นตอนการจัดเก็บผลลัพธ์ที่ทำงานได้ดี แต่มันจะดีที่จะไม่ให้คอลัมน์ใด ๆ


22
ด้วย SELECT * INTO [Table NAME] คุณจะรู้ว่าคอลัมน์เหล่านั้นถูกคัดลอกมาจากตารางต้นฉบับ นี่คือสิ่งที่ฉันต้องการถ้าฉันทำสิ่งเดียวกันกับขั้นตอนการจัดเก็บ
Ferdeen


7
แค่ต้องการชี้ให้เห็นว่า "select * to tmpBusLine" สร้างตารางถาวร คุณอาจต้องการ "select * to #tmpBusLine" ฉันแน่ใจว่าผู้โพสต์ดั้งเดิมได้ค้นพบสิ่งนี้แล้ว แต่มันอาจช่วยคนอื่น ๆ ที่ค้นหาโพสต์นี้เนื่องจากมันเป็นผลการค้นหาอันดับต้น ๆ ในขณะนี้สำหรับการค้นหา "select
to

2
ฉันไม่ทราบว่าสิ่งนี้ได้รับการจัดการหรือไม่ แต่สาเหตุที่คุณได้รับข้อผิดพลาดนั้นเป็นเพราะคำหลักจาก
Wes Palmer

9
Microsoft ต้องการเพิ่ม SELECT * เข้าจาก EXEC! โปรด!
kjmerf

คำตอบ:


704

คุณสามารถใช้OPENROWSETสำหรับสิ่งนี้ ได้ดู ฉันได้รวมรหัส sp_configure เพื่อเปิดใช้งานแบบสอบถามแบบกระจายแบบเฉพาะกิจในกรณีที่ยังไม่ได้เปิดใช้งาน

CREATE PROC getBusinessLineHistory
AS
BEGIN
    SELECT * FROM sys.databases
END
GO

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

SELECT * INTO #MyTempTable FROM OPENROWSET('SQLNCLI', 'Server=(local)\SQL2008;Trusted_Connection=yes;',
     'EXEC getBusinessLineHistory')

SELECT * FROM #MyTempTable

28
นี่เป็นวิธีที่ถูกต้องที่จะทำ OPENROWSET เป็นวิธีเดียวที่จะรักษาผลลัพธ์ของกระบวนงานที่เก็บไว้เป็นนิพจน์ตาราง
Rob Farley

37
ดูเหมือนว่าจะยุ่งยากในการแทรกลงในตาราง มีการกำหนดค่าให้ทำมากมาย นอกจากนี้เมื่อฉันลองมันฉันได้รับ "ข่าวสารเกี่ยวกับ 7357 ระดับ 16 สถานะ 2 บรรทัดที่ 1 ไม่สามารถดำเนินการวัตถุ" EXEC GetPartyAnalysisData 146 "ผู้ให้บริการ OLE DB" SQLNCLI "สำหรับเซิร์ฟเวอร์ที่เชื่อมโยง" (null) "บ่งชี้ว่าวัตถุนั้นมี ไม่มีคอลัมน์หรือผู้ใช้ปัจจุบันไม่มีสิทธิ์ในวัตถุนั้น " ดังนั้นคุณจะต้องตั้งเซิร์ฟเวอร์ที่เชื่อมโยง ...
Ferdeen

10
คุณไม่ต้องการเซิร์ฟเวอร์ที่เชื่อมโยง แต่คุณจะต้องได้รับสตริงการเชื่อมต่อที่ถูกต้อง ... และระบุเส้นทางแบบเต็มไปยังโพรซีเดอร์ที่เก็บไว้รวมถึงชื่อฐานข้อมูลและเจ้าของของ sp
MartW

18
eeeeew! อ้างอิงถึงเซิร์ฟเวอร์เดียวกันหรือไม่ น่ารังเกียจ การแฮ็กมากกว่าการสร้างตารางชั่วคราวด้วยตนเอง
Tim Abell

23
ฉันยอมรับว่านี่เป็นแฮ็กและควรหลีกเลี่ยงหากคุณไม่ได้อยู่ข้างหลังกำแพง การเปลี่ยน sp เป็นฟังก์ชันน่าจะเป็นมุมที่ดีกว่า IMHO
เกอร์

624

ถ้าคุณต้องการที่จะทำมันโดยไม่ต้องประกาศตารางชั่วคราวก่อนคุณสามารถลองสร้างฟังก์ชั่นที่ผู้ใช้กำหนดแทนขั้นตอนการจัดเก็บและทำให้ฟังก์ชั่นที่ผู้ใช้กำหนดนั้นส่งคืนตาราง หรือถ้าคุณต้องการใช้โพรซีเดอร์ที่เก็บไว้ลองทำดังนี้:

CREATE TABLE #tmpBus
(
   COL1 INT,
   COL2 INT
)

INSERT INTO #tmpBus
Exec SpGetRecords 'Params'

170
ฉันคิดว่าประเด็นคือการสร้างสคีมาโดยไม่ต้องประกาศอย่างชัดเจน
เครก

5
ฉันสนใจที่จะทราบว่าความแตกต่างระหว่างสิ่งนี้กับโซลูชันของ @Aaron Alton ด้านบนอย่างไร อันนี้ดูง่ายกว่ามาก แต่ฉันไม่แน่ใจว่าเกี่ยวข้องกับเรื่องอื่นใด
funkymushroom

11
สิ่งนี้จะใช้งานได้ แต่ถ้าคุณเพิ่มคอลัมน์เพิ่มเติมลงในกระบวนงานที่เก็บ SpGetRecords สิ่งนี้จะระเบิดขึ้น
เบรดี้โฮลท์

15
คุณได้รับ INSERT INTO EXEC หนึ่งต่อการโทรหนึ่งครั้ง SpGetRecords และ proc อื่น ๆ ที่เรียกใช้อาจไม่ใช้กลยุทธ์นี้ในรหัสของตนเอง สิ่งนี้สามารถทำให้ผู้ดูแลระบบของ SpGetRecords ประหลาดใจ
Matt Stephenson

33
นี่ไม่ตอบคำถามเลยและฉันไม่เห็นว่าทำไม upvoted มาก OP ระบุไว้อย่างชัดเจน "โดยไม่ต้องกำหนด [ตารางชั่วคราว]" และบรรทัดแรกของคุณมีคำสั่งสร้างตารางชั่วคราว
NickG

296

ใน SQL Server 2005 คุณสามารถใช้INSERT INTO ... EXECเพื่อแทรกผลลัพธ์ของกระบวนงานที่เก็บไว้ในตาราง จากเอกสารของ MSDNINSERT (สำหรับ SQL Server 2000 จริงๆแล้ว):

--INSERT...EXECUTE procedure example
INSERT author_sales EXECUTE get_author_sales

122
สิ่งนี้ต้องการให้กำหนด_s_salesไว้ล่วงหน้า ฉันพยายามหลีกเลี่ยงสิ่งนี้ ขอบคุณ
Ferdeen

5
ฉันคิดมาก มีประโยชน์ดังนั้นการแทรกลงในตาราง tmp ได้ทันที แต่ไม่มีประโยชน์หากคุณจำเป็นต้องรู้โครงสร้างชุดข้อมูลที่ส่งคืนจาก proc ที่เก็บไว้ ขอบคุณสำหรับความช่วยเหลือ
Ferdeen

3
มีบทความที่ดีที่นี่msdn.microsoft.com/en-us/library/aa175921.aspx
Rich Andrews

4
หากต้องการใช้สคีมาเดียวกันคุณสามารถทำสำเนาได้ดังนี้: เลือก top 0 * ไปที่ tempTable จาก realTable ( stackoverflow.com/a/9206463/73794 )
แม้ Mien

@EvenMien ฉันรู้สึกตื่นเต้นสักครู่เมื่อฉันเห็นความคิดเห็นของคุณ ... แต่น่าเสียดายที่ใช้ได้เฉพาะในกรณีที่ผลลัพธ์ของ proc ของคุณสะท้อนตารางจริง :(
BVernon

193

นี่คือคำตอบสำหรับคำถามที่คุณแก้ไขเล็กน้อย ถ้าคุณสามารถละทิ้งการใช้กระบวนงานที่เก็บไว้สำหรับฟังก์ชันที่ผู้ใช้กำหนดคุณสามารถใช้ฟังก์ชันที่ผู้ใช้กำหนดเองแบบอินไลน์ได้ นี่เป็นกระบวนการที่เก็บไว้เป็นหลัก (จะรับพารามิเตอร์) ที่ส่งคืนตารางเป็นชุดผลลัพธ์ และดังนั้นจะวางไว้อย่างดีด้วยคำสั่งเป็น

นี่คือบทความด่วนที่ดีเกี่ยวกับมันและฟังก์ชั่นที่ผู้ใช้กำหนดอื่น ๆ หากคุณยังมีความต้องการในการขับขี่สำหรับกระบวนงานที่เก็บไว้คุณสามารถห่อฟังก์ชั่นที่ผู้ใช้กำหนดเองแบบอินไลน์ได้ด้วยกระบวนการจัดเก็บ โพรซีเดอร์ที่เก็บเพียงแค่ส่งพารามิเตอร์เมื่อเรียกใช้ select * จากฟังก์ชันที่ผู้ใช้กำหนดเองแบบอินไลน์ตาราง

ตัวอย่างเช่นคุณมีฟังก์ชั่นที่ผู้ใช้กำหนดเองแบบอินไลน์ตารางเพื่อรับรายชื่อลูกค้าในแต่ละภูมิภาค:

CREATE FUNCTION CustomersByRegion 
(  
    @RegionID int  
)
RETURNS TABLE 
AS
RETURN 
  SELECT *
  FROM customers
  WHERE RegionID = @RegionID
GO

จากนั้นคุณสามารถเรียกใช้ฟังก์ชันนี้เพื่อให้ได้ผลลัพธ์ของคุณ:

SELECT * FROM CustomersbyRegion(1)

หรือเพื่อทำ SELECT INTO:

SELECT * INTO CustList FROM CustomersbyRegion(1)

หากคุณยังต้องการโพรซีเดอร์ที่จัดเก็บไว้ให้ปิดฟังก์ชั่นดังกล่าว:

CREATE PROCEDURE uspCustomersByRegion 
(  
    @regionID int  
)
AS
BEGIN
     SELECT * FROM CustomersbyRegion(@regionID);
END
GO

ฉันคิดว่านี่เป็นวิธีที่ 'แฮ็คน้อยที่สุด' เพื่อให้ได้ผลลัพธ์ที่ต้องการ มันใช้คุณสมบัติที่มีอยู่ตามที่ตั้งใจไว้ให้ใช้งานโดยไม่มีความยุ่งยากเพิ่มเติม ด้วยการซ้อนฟังก์ชันที่ผู้ใช้กำหนดให้กับตารางแบบอินไลน์ในกระบวนงานที่เก็บไว้คุณจะสามารถเข้าถึงฟังก์ชันการทำงานได้สองวิธี Plus! คุณมีจุดบำรุงรักษาเพียงจุดเดียวสำหรับรหัส SQL ที่แท้จริง

แนะนำให้ใช้ OPENROWSET แต่นี่ไม่ใช่สิ่งที่ฟังก์ชั่น OPENROWSET ตั้งใจใช้ (สำหรับหนังสือออนไลน์):

รวมข้อมูลการเชื่อมต่อทั้งหมดที่จำเป็นในการเข้าถึงข้อมูลระยะไกลจากแหล่งข้อมูล OLE DB วิธีนี้เป็นอีกทางเลือกหนึ่งในการเข้าถึงตารางในเซิร์ฟเวอร์ที่เชื่อมโยงและเป็นวิธีการเฉพาะกิจในการเชื่อมต่อและเข้าถึงข้อมูลระยะไกลโดยใช้ OLE DB สำหรับการอ้างอิงถึงแหล่งข้อมูล OLE DB บ่อยให้ใช้เซิร์ฟเวอร์ที่เชื่อมโยงแทน

การใช้ OPENROWSET จะทำให้งานเสร็จ แต่จะมีค่าใช้จ่ายเพิ่มเติมสำหรับการเปิดการเชื่อมต่อท้องถิ่นและข้อมูลการเรียงข้อมูล และอาจไม่เป็นตัวเลือกในทุกกรณีเนื่องจากต้องได้รับอนุญาตจากแบบสอบถามเฉพาะกิจซึ่งมีความเสี่ยงด้านความปลอดภัยและอาจไม่ต้องการ นอกจากนี้วิธี OPENROWSET จะขัดขวางการใช้โพรซีเดอร์ที่เก็บไว้ซึ่งส่งคืนชุดผลลัพธ์มากกว่าหนึ่งชุด การห่อฟังก์ชั่นที่ผู้ใช้กำหนดเองหลายค่าในตารางแบบอินไลน์ในขั้นตอนเดียวที่เก็บไว้สามารถทำได้


4
+1 ฟังก์ชั่นที่มีค่าเป็นตารางเป็นโซลูชันที่เหมาะสม เราควรจดบันทึกข้อเสียเล็กน้อย: ฟังก์ชันค่าตารางเป็นวัตถุฐานข้อมูลเพิ่มเติมและอาจจำเป็นต้องให้สิทธิพิเศษแก่มัน
spencer7593

2
รักการแก้ปัญหา อุปสรรค์เล็ก ๆ น้อย ๆ ที่ฉันตีคือโต๊ะของฉันไม่สามารถสั่งซื้อได้โดยที่มันสามารถมีได้ในขั้นตอนการจัดเก็บ เป็นไงฉันจะจัดเรียงมันออกมา
mrwaim

5
อีกหนึ่งอุปสรรค์ - "ไม่สามารถเข้าถึงตารางชั่วคราวจากภายในฟังก์ชั่น"
mrwaim

7
คำถามเดิมคือเราจะสร้างตารางชั่วคราวที่มีผลลัพธ์ของ sp ได้อย่างไร นี่เป็นรูปแบบที่ดี แต่ไม่ได้ตอบคำถามนี้
greg

16
greg บรรทัดแรกในคำตอบของฉันระบุว่า "นี่เป็นคำตอบสำหรับคำถามที่คุณแก้ไขเล็กน้อย" ความคิดเห็นของคุณซ้ำซ้อน
Christian Loris

131
EXEC sp_serveroption 'YOURSERVERNAME', 'DATA ACCESS', TRUE

SELECT  *
INTO    #tmpTable
FROM    OPENQUERY(YOURSERVERNAME, 'EXEC db.schema.sproc 1')

2
ได้รับ "ข่าวสารเกี่ยวกับ 208 ระดับ 16 สถานะ 1 ชื่อวัตถุสาย 1 ไม่ถูกต้อง 'tmpBusLine' (อาจจะเป็นก็ไม่ได้กำหนดขึ้นหน้า).
Ferdeen

1
@Ferds: ขออภัยไม่เข้าใจคำขอของคุณในตอนแรก อัปเดตด้วยโซลูชันอื่น
Quassnoi

26
ทางออกที่ดี หนึ่งข้อสังเกตคุณจะต้องเปิดใช้งาน 'DATA ACCESS' บนเซิร์ฟเวอร์ของคุณ: EXEC sp_serveroption 'TheServerName', 'DATA ACCESS', TRUE
jcollum

8
คุณจะต้องอนุญาตการเข้าถึงเซิร์ฟเวอร์จากระยะไกล นี้จะมีเครือข่ายความปลอดภัย
BraveNewMath

7
สิ่งนี้จะไม่ทำงานหากโพรซีเดอร์ที่เก็บเป้าหมายใช้ตารางชั่วคราว
Sal

125

ทางออกที่ง่ายที่สุด:

CREATE TABLE #temp (...);

INSERT INTO #temp
EXEC [sproc];

หากคุณไม่ทราบสคีมาคุณสามารถทำสิ่งต่อไปนี้ โปรดทราบว่าวิธีนี้มีความเสี่ยงด้านความปลอดภัยที่รุนแรง

SELECT * 
INTO #temp
FROM OPENROWSET('SQLNCLI', 
                'Server=localhost;Trusted_Connection=yes;', 
                'EXEC [db].[schema].[sproc]')

ถ้าฉันไม่รู้คอลัมน์ของ resultset ที่ได้คืนมาแล้ว ??? ฉันหมายถึงคอลัมน์อาจแตกต่างกันไป ดังนั้นวิธีการแทรกผลลัพธ์ลงในตารางชั่วคราว ???
SHEKHAR SHETE

คุณสามารถใช้ OPENQUERY แต่ไม่แนะนำเพราะมันมาพร้อมกับข้อบกพร่องด้านความปลอดภัย
Tigerjz32

1
"ถ้าฉันไม่รู้คอลัมน์ของ resultset ที่ได้คืนมา" จากนั้นคุณไม่สามารถใช้มันในตรรกะของคุณ คุณจะใช้ข้อมูลอย่างไรถ้าคุณไม่รู้ว่ามันคืออะไร
Adriaan Davel

@AdriaanDavel ฉันเห็นด้วยกับคุณว่าคุณควรรู้ข้อมูลของคุณเสมอ (แนวปฏิบัติที่ดีที่สุด) อย่างไรก็ตามสิ่งที่เขาอาจจะพูดคือมีบางครั้งที่ sproc ส่งคืนคอลัมน์แบบไดนามิกและคุณไม่ทราบว่าสคีมาจะเป็นอย่างไร ในกรณีนั้นคุณสามารถใช้ OPENROWSET เพื่อแทรกและสร้างตารางได้ทันที อย่างไรก็ตามมีความเสี่ยงด้านความปลอดภัยที่เห็นได้ชัดในการทำเช่นนี้ ...
Tigerjz32

1
@nurettin บางครั้งคุณไม่ทราบว่าขั้นตอนการจัดเก็บจะกลับมาได้อย่างไร เกิดอะไรขึ้นในกรณีนั้น? คุณจะสร้างตาราง temp ได้อย่างไร (เมื่อคุณไม่รู้ว่าโพรซีเดอร์ที่เก็บไว้จะส่งคืนอะไร) และแทรกลงในโพรซีเดอร์ที่เก็บไว้?
Tigerjz32

106

เมื่อโพรซีเดอร์ที่เก็บไว้ส่งคืนคอลัมน์จำนวนมากและคุณไม่ต้องการ "สร้าง" ตารางชั่วคราวเพื่อเก็บผลลัพธ์ด้วยตนเองฉันพบวิธีที่ง่ายที่สุดคือไปที่โพรซีเดอร์ที่เก็บไว้และเพิ่มส่วน "เป็น" ใน เลือกคำสั่งสุดท้ายและเพิ่ม 1 = 0 ไปยังประโยคที่

เรียกใช้กระบวนงานที่เก็บไว้หนึ่งครั้งและย้อนกลับและลบรหัส SQL ที่คุณเพิ่งเพิ่ม ตอนนี้คุณจะมีตารางว่างตรงกับผลลัพธ์ของขั้นตอนการจัดเก็บ คุณสามารถ "สร้างตารางสคริปต์เป็นสร้าง" สำหรับตารางชั่วคราวหรือเพียงแค่แทรกลงในตารางนั้นโดยตรง


9
+1 ข้อเสนอแนะที่ยอดเยี่ยม คุณสามารถเพิ่มตัวแปรทางเลือกด่วนให้กับ sproc ที่ชื่อว่า @TableCreate หรือสิ่งที่คล้ายกันว่าเมื่อไม่เป็นโมฆะให้ทำตามขั้นตอนด้านบน ไม่ต้องการเปลี่ยนแปลงของ sproc จากนั้นเมื่อตั้งค่าแล้ว
เอียนโร้ค

1
@dotjoe คุณทำSELECT INTOตารางชั่วคราวและทำตารางสคริปต์เป็นสร้างจากตารางชั่วคราวหรือไม่ ตารางชั่วคราวปรากฏในtempdbแต่ฉันไม่สามารถคลิกขวาและสร้างสคริปต์ได้ ความช่วยเหลือใด ๆ ที่ชื่นชม
DotnetDude

2
@DotNetDude คุณสามารถselect ... into new_tableสร้างตารางจริงได้
dotjoe

จากนั้นหยิบนิยามคอลัมน์แบบคร่าวๆจากสคีมาของตารางที่ว่าง แทนที่ '... ' ที่ท้ายด้วยdeclare @s varchar(max)='';select @s=@s+','+COLUMN_NAME+' '+DATA_TYPE+isnull('('+case CHARACTER_MAXIMUM_LENGTH when -1 then 'max' else cast(CHARACTER_MAXIMUM_LENGTH as varchar(10))end+')','')from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='...';select @s
legitables_NAME

นี่คือทางออกที่ดีที่สุด!
Lucas925

66
declare @temp table
(
    name varchar(255),
    field varchar(255),
    filename varchar(255),
    filegroup varchar(255),
    size varchar(255),
    maxsize varchar(255),
    growth varchar(255),
    usage varchar(255)
);
INSERT @temp  Exec sp_helpfile;
select * from @temp;

3
ไม่ตอบคำถามต้นฉบับของ OP ทำการแทรกโดยไม่ต้องกำหนดตารางอุณหภูมิก่อน
t.durden

48

ขั้นตอนการจัดเก็บของคุณเรียกข้อมูลหรือแก้ไขมันด้วยหรือไม่ หากใช้เพื่อดึงข้อมูลเท่านั้นคุณสามารถแปลงกระบวนงานที่เก็บไว้เป็นฟังก์ชันและใช้ Common Table Expressions (CTEs) โดยไม่ต้องประกาศดังต่อไปนี้:

with temp as (
    select * from dbo.fnFunctionName(10, 20)
)
select col1, col2 from temp

อย่างไรก็ตามสิ่งที่จำเป็นต้องเรียกคืนจาก CTE ควรใช้ในคำสั่งเดียวเท่านั้น คุณไม่สามารถทำwith temp as ...และลองใช้มันหลังจากใช้ SQL สองบรรทัด คุณสามารถมี CTE หลายรายการในคำสั่งเดียวสำหรับการสืบค้นที่ซับซ้อนมากขึ้น

ตัวอย่างเช่น,

with temp1020 as (
    select id from dbo.fnFunctionName(10, 20)
),
temp2030 as (
    select id from dbo.fnFunctionName(20, 30)
)
select * from temp1020 
where id not in (select id from temp2030)

1
ตารางเหล่านี้ไม่ใช่ตารางชั่วคราวคือ CTE technet.microsoft.com/en-us/library/...
yucer

5
ขอบคุณ @yucer ... ฉันเชื่อว่าฉันไม่รู้ว่าพวกเขาถูกเรียกว่า CTE แล้วตอนนี้ :)
ดังนั้นผู้ใช้

48

หากตารางผลลัพธ์ของ proc ที่จัดเก็บของคุณซับซ้อนเกินไปที่จะพิมพ์คำสั่ง "สร้างตาราง" ด้วยมือและคุณไม่สามารถใช้ OPENQUERY หรือ OPENROWSET คุณสามารถใช้ sp_help เพื่อสร้างรายการคอลัมน์และชนิดข้อมูลสำหรับคุณ เมื่อคุณมีรายการคอลัมน์มันเป็นเรื่องของการจัดรูปแบบตามความต้องการของคุณ

ขั้นตอนที่ 1: เพิ่ม "ลงใน #temp" ลงในแบบสอบถามผลลัพธ์ (เช่น "เลือก [... ] ลงใน #temp จาก [... ]")

วิธีที่ง่ายที่สุดคือการแก้ไขแบบสอบถามผลลัพธ์ใน proc โดยตรง หากคุณไม่สามารถเปลี่ยน proc ที่เก็บไว้คุณสามารถคัดลอกเนื้อหาลงในหน้าต่างแบบสอบถามใหม่และปรับเปลี่ยนแบบสอบถามที่นั่น

ขั้นตอนที่ 2: เรียกใช้ sp_help บนตารางชั่วคราว (เช่น "exec tempdb..sp_help #temp")

หลังจากสร้างตาราง temp แล้วให้เรียกใช้ sp_help บนตาราง temp เพื่อรับรายการคอลัมน์และชนิดข้อมูลรวมถึงขนาดของเขตข้อมูล varchar

ขั้นตอนที่ 3: คัดลอกคอลัมน์ข้อมูล & ประเภทลงในคำสั่งสร้างตาราง

ฉันมีแผ่นงาน Excel ที่ฉันใช้เพื่อจัดรูปแบบผลลัพธ์ของ sp_help ลงในคำสั่ง "สร้างตาราง" คุณไม่ต้องการอะไรที่แปลกใหม่เพียงแค่คัดลอกและวางลงในตัวแก้ไข SQL ของคุณ ใช้ชื่อคอลัมน์ขนาดและประเภทเพื่อสร้างคำสั่ง "สร้างตาราง #x [... ]" หรือ "ประกาศ @x ตาราง [... ]" ซึ่งคุณสามารถใช้เพื่อแทรกผลลัพธ์ของกระบวนงานที่เก็บไว้

ขั้นตอนที่ 4: แทรกลงในตารางที่สร้างขึ้นใหม่

ตอนนี้คุณจะมีข้อความค้นหาที่เหมือนกับโซลูชันอื่น ๆ ที่อธิบายไว้ในชุดข้อความนี้

DECLARE @t TABLE 
(
   --these columns were copied from sp_help
   COL1 INT,
   COL2 INT   
)

INSERT INTO @t 
Exec spMyProc 

เทคนิคนี้ยังสามารถใช้ในการแปลง temp table ( #temp) ไปเป็นตัวแปร table ( @temp) ขณะนี้อาจเป็นขั้นตอนมากกว่าการเขียนcreate tableคำสั่งด้วยตนเอง แต่จะป้องกันข้อผิดพลาดด้วยตนเองเช่นการพิมพ์ผิดและชนิดข้อมูลไม่ตรงกันในกระบวนการขนาดใหญ่ การแก้ไขข้อผิดพลาดพิมพ์อาจใช้เวลามากกว่าการเขียนแบบสอบถามในสถานที่แรก


37

หาก OPENROWSET ก่อให้เกิดปัญหากับคุณมีวิธีอื่นในปี 2012 เป็นต้นไป ใช้ประโยชน์จาก sys.dm_exec_describe_first_result_set_for_object ตามที่ได้กล่าวไว้ที่นี่: เรียกชื่อคอลัมน์และประเภทของกระบวนงานที่เก็บไว้หรือไม่

ก่อนอื่นให้สร้างโพรซีเดอร์ที่เก็บไว้นี้เพื่อสร้าง SQL สำหรับตารางชั่วคราว:

CREATE PROCEDURE dbo.usp_GetStoredProcTableDefinition(
    @ProcedureName  nvarchar(128),
    @TableName      nvarchar(128),
    @SQL            nvarchar(max) OUTPUT
)
AS
SET @SQL = 'CREATE TABLE ' + @tableName + ' ('

SELECT @SQL = @SQL + '['+name +'] '+ system_type_name +''  + ','
        FROM sys.dm_exec_describe_first_result_set_for_object
        (
          OBJECT_ID(@ProcedureName), 
          NULL
        );

--Remove trailing comma
SET @SQL = SUBSTRING(@SQL,0,LEN(@SQL))    
SET @SQL =  @SQL +')'

ในการใช้โพรซีเดอร์ให้เรียกใช้ด้วยวิธีดังต่อไปนี้:

DECLARE     @SQL    NVARCHAR(MAX)

exec dbo.usp_GetStoredProcTableDefinition
    @ProcedureName='dbo.usp_YourProcedure',
    @TableName='##YourGlobalTempTable',@SQL = @SQL OUTPUT

INSERT INTO ##YourGlobalTempTable
EXEC    [dbo].usp_YourProcedure

select * from ##YourGlobalTempTable

โปรดทราบว่าฉันกำลังใช้ตารางชั่วคราวทั่วโลก นั่นเป็นเพราะการใช้ EXEC เพื่อเรียกใช้ SQL แบบไดนามิกสร้างเซสชันของตัวเองดังนั้นตารางชั่วคราวธรรมดาจะไม่อยู่ในขอบเขตของโค้ดที่ตามมา หากตารางชั่วคราวทั่วโลกเป็นปัญหาคุณสามารถใช้ตารางชั่วคราวธรรมดาได้ แต่ SQL ที่ตามมาจะต้องเป็นแบบไดนามิกนั่นคือจะถูกดำเนินการโดยคำสั่ง EXEC


4
@SQLคุณลืมที่จะสร้างตารางจาก
เดินขบวน

32

Quassnoi ทำให้ฉันไปที่นั่น แต่สิ่งหนึ่งที่ขาดหาย

**** ฉันต้องการใช้พารามิเตอร์ในขั้นตอนการจัดเก็บ ****

และ OPENQUERY ไม่อนุญาตให้สิ่งนี้เกิดขึ้น:

ดังนั้นฉันจึงพบวิธีการทำงานของระบบและไม่จำเป็นต้องสร้างคำจำกัดความของตารางที่เข้มงวดและกำหนดมันใหม่ภายในกระบวนงานที่เก็บไว้อีกครั้ง

ได้คุณสามารถสร้างคำจำกัดความของตารางที่ส่งคืนจากโพรซีเดอร์ที่จัดเก็บแบบไดนามิกได้โดยใช้คำสั่ง OPENQUERY กับ bogus varaiables (ตราบใดที่NO RESULT SETส่งคืนจำนวนฟิลด์ที่เท่ากันและอยู่ในตำแหน่งเดียวกับชุดข้อมูลที่มีข้อมูลดี)

เมื่อสร้างตารางแล้วคุณสามารถใช้โพรซีเดอร์ที่จัดเก็บไว้ในตารางชั่วคราวตลอดทั้งวัน


และเพื่อทราบ (ตามที่ระบุข้างต้น) คุณต้องเปิดใช้งานการเข้าถึงข้อมูล

EXEC sp_serveroption 'MYSERVERNAME', 'DATA ACCESS', TRUE

รหัส:

declare @locCompanyId varchar(8)
declare @locDateOne datetime
declare @locDateTwo datetime

set @locDateOne = '2/11/2010'
set @locDateTwo = getdate()

--Build temporary table (based on bogus variable values)
--because we just want the table definition and
--since openquery does not allow variable definitions...
--I am going to use bogus variables to get the table defintion.

select * into #tempCoAttendanceRpt20100211
FROM OPENQUERY(DBASESERVER,
  'EXEC DATABASE.dbo.Proc_MyStoredProc 1,"2/1/2010","2/15/2010 3:00 pm"')

set @locCompanyId = '7753231'

insert into #tempCoAttendanceRpt20100211
EXEC DATABASE.dbo.Proc_MyStoredProc @locCompanyId,@locDateOne,@locDateTwo

set @locCompanyId = '9872231'

insert into #tempCoAttendanceRpt20100211
EXEC DATABASE.dbo.Proc_MyStoredProc @locCompanyId,@locDateOne,@locDateTwo

select * from #tempCoAttendanceRpt20100211
drop table #tempCoAttendanceRpt20100211

ขอบคุณสำหรับข้อมูลที่ให้มา แต่เดิม ... ใช่ในที่สุดฉันก็ไม่จำเป็นต้องสร้างข้อบกพร่องตาราง (เข้มงวด) เหล่านี้ทั้งหมดเมื่อใช้ข้อมูลจากขั้นตอนหรือฐานข้อมูลอื่นที่เก็บไว้และใช่คุณสามารถใช้พารามิเตอร์ได้เช่นกัน

ค้นหาแท็กอ้างอิง:

  • SQL 2005 จัดเก็บโพรซีเดอร์ในตาราง temp

  • openquery พร้อมโพรซีเดอร์และตัวแปรที่เก็บไว้ในปี 2005

  • openquery พร้อมตัวแปร

  • รันโพรซีเดอร์ที่เก็บไว้ในตารางชั่วคราว

อัปเดต: สิ่งนี้จะไม่ทำงานกับตารางชั่วคราวดังนั้นฉันจึงต้องหันไปสร้างตารางชั่วคราวด้วยตนเอง

แจ้งให้ทราบล่วงหน้า Bummer : นี้จะไม่ทำงานกับตารางชั่วคราว , http://www.sommarskog.se/share_data.html#OPENQUERY

การอ้างอิง: สิ่งต่อไปคือการกำหนด LOCALSERVER อาจดูเหมือนคำหลักในตัวอย่าง แต่จริงๆแล้วเป็นเพียงชื่อ นี่คือวิธีที่คุณทำ:

sp_addlinkedserver @server = 'LOCALSERVER',  @srvproduct = '',
                   @provider = 'SQLOLEDB', @datasrc = @@servername

ในการสร้างเซิร์ฟเวอร์ที่เชื่อมโยงคุณจะต้องได้รับอนุญาตจาก ALTER ANY SERVER หรือเป็นสมาชิกของ sysadmin หรือ setupadmin

OPENQUERY เปิดการเชื่อมต่อใหม่ไปยัง SQL Server สิ่งนี้มีความหมายบางอย่าง:

ขั้นตอนที่คุณโทรด้วย OPENQUERY ไม่สามารถอ้างถึงตารางชั่วคราวที่สร้างขึ้นในการเชื่อมต่อปัจจุบัน

การเชื่อมต่อใหม่มีฐานข้อมูลเริ่มต้นของตัวเอง (กำหนดด้วย sp_addlinkedserver ค่าเริ่มต้นคือต้นแบบ) ดังนั้นข้อมูลจำเพาะของวัตถุทั้งหมดจะต้องมีชื่อฐานข้อมูล

หากคุณมีธุรกรรมเปิดและถือล็อคเมื่อคุณเรียก OPENQUERY ขั้นตอนที่เรียกว่าไม่สามารถเข้าถึงสิ่งที่คุณล็อค นั่นคือถ้าคุณไม่ระวังคุณจะบล็อกตัวเอง

การเชื่อมต่อไม่ได้ฟรีดังนั้นจึงมีโทษประสิทธิภาพ


1
SELECT @@SERVERNAMEหากคุณไม่ทราบชื่อเซิร์ฟเวอร์ของคุณใช้ คุณสามารถใช้EXEC sp_serveroption @@SERVERNAME, 'DATA ACCESS', TRUE
Contango

24

หากคุณโชคดีพอที่จะมี SQL 2012 ขึ้นไปคุณสามารถใช้ dm_exec_describe_first_result_set_for_object

ฉันเพิ่งแก้ไข sql ที่ได้รับจาก gotqn ขอบคุณ gotqn

สิ่งนี้จะสร้างตารางชั่วคราวที่มีชื่อเหมือนกับชื่อกระบวนงาน สามารถใช้ตารางชั่วคราวได้ในภายหลังตามต้องการ อย่าลืมวางไว้ก่อนดำเนินการอีกครั้ง

    declare @procname nvarchar(255) = 'myProcedure',
            @sql nvarchar(max) 

    set @sql = 'create table ##' + @procname + ' ('
    begin
            select      @sql = @sql + '[' + r.name + '] ' +  r.system_type_name + ','
            from        sys.procedures AS p
            cross apply sys.dm_exec_describe_first_result_set_for_object(p.object_id, 0) AS r
            where       p.name = @procname

            set @sql = substring(@sql,1,len(@sql)-1) + ')'
            execute (@sql)
            execute('insert ##' + @procname + ' exec ' + @procname)
    end

1
ยอดเยี่ยม เพียงหนึ่งคำพูด: ใช้sys.all_objectsแทนsys.proceduresถ้าคุณต้องการที่จะทำเช่นนี้สำหรับกระบวนการที่เก็บไว้ในตัว
Gert Arnold

2
สิ่งนี้จะล้มเหลวหาก SP ใช้ตารางชั่วคราวภายใน ( แต่สวยมีประโยชน์ให้มีนี้เป็น proc ในคลังแสงของคุณ)
Trubs

23

proc ที่เก็บไว้นี้ทำงาน:

CREATE PROCEDURE [dbo].[ExecIntoTable]
(
    @tableName          NVARCHAR(256),
    @storedProcWithParameters   NVARCHAR(MAX)
)
AS
BEGIN
    DECLARE @driver         VARCHAR(10)
    DECLARE @connectionString   NVARCHAR(600)
    DECLARE @sql            NVARCHAR(MAX)
    DECLARE @rowsetSql      NVARCHAR(MAX)

    SET @driver = '''SQLNCLI'''

    SET @connectionString = 
        '''server=' + 
            CAST(SERVERPROPERTY('ServerName') AS NVARCHAR(256)) + 
            COALESCE('\' + CAST(SERVERPROPERTY('InstanceName') AS NVARCHAR(256)), '') + 
        ';trusted_connection=yes'''

    SET @rowsetSql = '''EXEC ' + REPLACE(@storedProcWithParameters, '''', '''''') + ''''

    SET @sql = '
SELECT
    *
INTO 
    ' + @tableName + ' 
FROM
    OPENROWSET(' + @driver + ',' + @connectionString + ',' + @rowsetSql + ')'

    EXEC (@sql)
END
GO

มันเป็นการปรับปรุงเล็กน้อย: แทรกผลลัพธ์ของโพรซีเดอร์ที่เก็บไว้ในตารางเพื่อให้ใช้งานได้จริง

หากคุณต้องการให้ทำงานกับตารางชั่วคราวคุณจะต้องใช้##GLOBALตารางและวางในภายหลัง


17

ในการแทรกชุดระเบียนแรกของกระบวนงานที่เก็บไว้ในตารางชั่วคราวคุณจำเป็นต้องรู้สิ่งต่อไปนี้:

  1. เฉพาะชุดแถวแรกของกระบวนงานที่เก็บไว้เท่านั้นที่สามารถแทรกลงในตารางชั่วคราว
  2. โพรซีเดอร์ที่เก็บต้องไม่เรียกใช้งานคำสั่ง T-SQL แบบไดนามิก (sp_executesql )
  3. คุณต้องกำหนดโครงสร้างของตารางชั่วคราวก่อน

ข้างต้นอาจมีข้อ จำกัด แต่ IMHO สมบูรณ์แบบหากคุณกำลังใช้sp_executesqlคุณสามารถส่งคืนสองคอลัมน์และสิบครั้งและหากคุณมีชุดผลลัพธ์หลายชุดคุณจะไม่สามารถแทรกชุดเหล่านั้นลงในหลายตารางได้เช่นกัน - คุณสามารถใส่สูงสุดได้ ในสองตารางในหนึ่งคำสั่ง T-SQL (โดยใช้OUTPUTส่วนคำสั่งและไม่มีทริกเกอร์)

ดังนั้นปัญหาส่วนใหญ่จะเป็นวิธีการกำหนดโครงสร้างตารางชั่วคราวก่อนดำเนินการEXEC ... INTO ...คำสั่ง

วิธีแรกใช้งานได้OBJECT_IDในขณะที่ข้อที่สองและข้อที่สามใช้ได้กับคำสั่ง Ad-hoc เช่นกัน ฉันชอบใช้ DMV แทน sp เนื่องจากคุณสามารถใช้CROSS APPLYและสร้างคำนิยามตารางชั่วคราวสำหรับหลายขั้นตอนในเวลาเดียวกัน

SELECT p.name, r.* 
FROM sys.procedures AS p
CROSS APPLY sys.dm_exec_describe_first_result_set_for_object(p.object_id, 0) AS r;

นอกจากนี้ให้ความสนใจกับsystem_type_nameสนามเพราะมันจะมีประโยชน์มาก มันเก็บคำจำกัดความที่สมบูรณ์ของคอลัมน์ ตัวอย่างเช่น:

smalldatetime
nvarchar(max)
uniqueidentifier
nvarchar(1000)
real
smalldatetime
decimal(18,2)

และคุณสามารถใช้มันได้โดยตรงในกรณีส่วนใหญ่เพื่อสร้างคำจำกัดความของตาราง

ดังนั้นฉันคิดว่าในกรณีส่วนใหญ่ (ถ้าขั้นตอนการจัดเก็บตรงกับเกณฑ์บางอย่าง) คุณสามารถสร้างงบแบบไดนามิกสำหรับการแก้ปัญหาดังกล่าว (สร้างตารางชั่วคราวแทรกผลการจัดเก็บในขั้นตอนทำสิ่งที่คุณต้องการกับข้อมูล) .


โปรดทราบว่าวัตถุดังกล่าวล้มเหลวในการกำหนดข้อมูลชุดผลลัพธ์แรกในบางกรณีเช่นเมื่อรันคำสั่ง T-SQL แบบไดนามิกหรือใช้ตารางชั่วคราวในขั้นตอนการจัดเก็บ


ข้อสังเกตเชิงปฏิบัติเกี่ยวกับข้อ จำกัด : หากคุณต้องแทรกเอาต์พุตของ sp บางตัว (เรียกว่า SP_LEVEL_0) ไปยังตาราง temp ที่สร้างขึ้นแบบไดนามิกโดยใช้วิธีการข้างต้นใน sp อื่น (ให้เรียกว่า SP_LEVEL_1) คุณไม่สามารถทำแบบเดียวกันสำหรับผลลัพธ์ของ SP_LEVEL_1 นี้ ตารางอุณหภูมิอื่นใน SP_LEVEL_2
พฤศจิกายน

17
  1. ฉันกำลังสร้างตารางที่มีสคีมาและข้อมูลต่อไปนี้
  2. สร้างกระบวนงานที่เก็บไว้
  3. ตอนนี้ฉันรู้แล้วว่าผลลัพธ์ของโพรซีเดอร์ของฉันคืออะไรดังนั้นฉันกำลังดำเนินการค้นหาต่อไปนี้

    CREATE TABLE [dbo].[tblTestingTree](
        [Id] [int] IDENTITY(1,1) NOT NULL,
        [ParentId] [int] NULL,
        [IsLeft] [bit] NULL,
        [IsRight] [bit] NULL,
    CONSTRAINT [PK_tblTestingTree] PRIMARY KEY CLUSTERED
    (
        [Id] ASC
    ) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    GO
    SET IDENTITY_INSERT [dbo].[tblTestingTree] ON
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (1, NULL, NULL, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (2, 1, 1, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (3, 1, NULL, 1)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (4, 2, 1, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (5, 2, NULL, 1)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (6, 3, 1, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (7, 3, NULL, 1)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (8, 4, 1, NULL)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (9, 4, NULL, 1)
    INSERT [dbo].[tblTestingTree] ([Id], [ParentId], [IsLeft], [IsRight]) VALUES (10, 5, 1, NULL)
    
    SET IDENTITY_INSERT [dbo].[tblTestingTree] OFF

    ค่า (10, 5, 1, NULL) SET IDENTITY_INSERT [dbo]. [tblTestingTree] เปิด

    create procedure GetDate
    as
    begin
        select Id,ParentId from tblTestingTree
    end
    
    create table tbltemp
    (
        id int,
        ParentId int
    )
    insert into tbltemp
    exec GetDate
    
    select * from tbltemp;

15

ถ้าแบบสอบถามไม่ได้มีพารามิเตอร์ที่ใช้การใช้งานอื่นOpenQueryOpenRowset

สิ่งพื้นฐานคือการสร้างสคีมาตามขั้นตอนการจัดเก็บและแทรกลงในตารางนั้น เช่น:

DECLARE @abc TABLE(
                  RequisitionTypeSourceTypeID INT
                , RequisitionTypeID INT
                , RequisitionSourcingTypeID INT
                , AutoDistOverride INT
                , AllowManagerToWithdrawDistributedReq INT
                , ResumeRequired INT
                , WarnSupplierOnDNRReqSubmission  INT
                , MSPApprovalReqd INT
                , EnableMSPSupplierCounterOffer INT
                , RequireVendorToAcceptOffer INT
                , UseCertification INT
                , UseCompetency INT
                , RequireRequisitionTemplate INT
                , CreatedByID INT
                , CreatedDate DATE
                , ModifiedByID INT
                , ModifiedDate DATE
                , UseCandidateScheduledHours INT
                , WeekEndingDayOfWeekID INT
                , AllowAutoEnroll INT
                )
INSERT INTO @abc
EXEC [dbo].[usp_MySp] 726,3
SELECT * FROM @abc

13

รหัส

CREATE TABLE #T1
(
    col1 INT NOT NULL,
    col2 NCHAR(50) NOT NULL,
    col3 TEXT NOT NULL,
    col4 DATETIME NULL,
    col5 NCHAR(50) NULL,
    col6 CHAR(2) NULL,
    col6 NCHAR(100) NULL,
    col7 INT NULL,
    col8 NCHAR(50) NULL,
    col9 DATETIME NULL,
    col10 DATETIME NULL
)

DECLARE @Para1 int
DECLARE @Para2 varchar(32)
DECLARE @Para3 varchar(100)
DECLARE @Para4 varchar(15)
DECLARE @Para5 varchar (12)
DECLARE @Para6 varchar(1)
DECLARE @Para7 varchar(1)


SET @Para1 = 1025
SET @Para2 = N'6as54fsd56f46sd4f65sd'
SET @Para3 = N'XXXX\UserName'
SET @Para4 = N'127.0.0.1'
SET @Para5 = N'XXXXXXX'
SET @Para6 = N'X'
SET @Para7 = N'X'

INSERT INTO #T1
(
    col1,
    col2,
    col3,
    col4,
    col5,
    col6,
    col6,
    col7,
    col8,
    col9,
    col10,
)
EXEC [dbo].[usp_ProcedureName] @Para1, @Para2, @Para3, @Para4, @Para5, @Para6, @Para6

ฉันหวังว่านี่จะช่วยได้. โปรดมีคุณสมบัติตามที่เหมาะสม


11

ฉันพบPassing Array / DataTables ในกระบวนงานที่เก็บซึ่งอาจทำให้คุณมีความคิดอื่นเกี่ยวกับวิธีที่คุณอาจแก้ไขปัญหาของคุณ

ลิงค์แนะนำให้ใช้พารามิเตอร์ประเภทภาพเพื่อส่งผ่านไปยังโพรซีเดอร์ที่เก็บไว้ จากนั้นในขั้นตอนการจัดเก็บภาพจะถูกเปลี่ยนเป็นตัวแปรตารางที่มีข้อมูลต้นฉบับ

อาจมีวิธีนี้สามารถใช้กับตารางชั่วคราว


4
นี้ไม่จำเป็นอีกต่อไปในรุ่น SQL2008 และต่อมาด้วยการแนะนำของตารางค่าพารามิเตอร์ ตอนนี้คุณสามารถส่งชุดข้อมูล. net หรือวัตถุ datatable ไปยัง sql ที่จัดเก็บไว้โดยไม่ต้องทำการแปลงเป็น byte ตามที่กล่าวไว้ในลิงค์ด้านบน
EndlessSpace

10

ผมได้พบกับปัญหาเดียวกันและนี่คือสิ่งที่ฉันได้สำหรับนี้จากข้อเสนอแนะของพอล ส่วนหลักคือที่นี่คือการใช้NEWID()เพื่อหลีกเลี่ยงผู้ใช้หลายคนเรียกใช้ขั้นตอนการจัดเก็บ / สคริปต์ในเวลาเดียวกันความเจ็บปวดสำหรับตารางชั่วคราวทั่วโลก

DECLARE @sql varchar(max) = '', 
@tmp_global_table varchar(255) = '##global_tmp_' + CONVERT(varchar(36), NEWID())
SET @sql = @sql + 'select * into [' + @tmp_global_table + '] from YOURTABLE'
EXEC(@sql)

EXEC('SELECT * FROM [' + @tmp_global_table + ']')

9

อีกวิธีหนึ่งคือการสร้างประเภทและใช้ PIPELINED แล้วส่งกลับวัตถุของคุณ นี่เป็นการ จำกัด การรู้คอลัมน์อย่างไรก็ตาม แต่มันก็มีข้อดีที่สามารถทำได้:

SELECT * 
FROM TABLE(CAST(f$my_functions('8028767') AS my_tab_type))

นี่คืออะไร? ดูเหมือนจะไม่เป็นอะไรจะทำอย่างไรกับ SQL Server ซึ่งคำถามนี้เป็นเรื่องเกี่ยวกับ
มาร์ตินสมิ ธ

8

มันเป็นกระบวนการง่ายๆ 2 ขั้นตอน: - สร้างตารางชั่วคราว - แทรกลงในตารางชั่วคราว

รหัสเพื่อดำเนินการเดียวกัน:

CREATE TABLE #tempTable (Column1 int, Column2 varchar(max));
INSERT INTO #tempTable 
EXEC [app].[Sproc_name]
@param1 = 1,
@param2 =2;

downvoted; คล้ายกับคำตอบที่มีอยู่มาก
iokevins

6

หลังจากค้นหาไปรอบ ๆ ฉันพบวิธีสร้างตารางชั่วคราวแบบไดนามิกสำหรับขั้นตอนการจัดเก็บใด ๆ โดยไม่ต้องใช้OPENROWSETหรือOPENQUERYใช้สคีมาทั่วไปของการกำหนดผลลัพธ์ของ Stored Procedure โดยเฉพาะเมื่อคุณไม่ใช่ผู้ดูแลระบบฐานข้อมูล

เซิร์ฟเวอร์ Sql มี proc แบบ buit-in sp_describe_first_result_setที่สามารถให้สคีมาของชุดผลลัพธ์ของโพรซีเดอร์ใด ๆ ฉันสร้างตาราง schema จากผลลัพธ์ของโพรซีเดอร์นี้และตั้งค่าฟิลด์ทั้งหมดเป็น NULLABLE ด้วยตนเอง

declare @procname varchar(100) = 'PROCEDURENAME' -- your procedure name
declare @param varchar(max) = '''2019-06-06''' -- your parameters 
declare @execstr nvarchar(max) = N'exec ' + @procname
declare @qry nvarchar(max)

-- Schema table to store the result from sp_describe_first_result_set.
create table #d
(is_hidden  bit  NULL, column_ordinal   int  NULL, name sysname NULL, is_nullable   bit  NULL, system_type_id   int  NULL, system_type_name nvarchar(256) NULL,
max_length  smallint  NULL, precision   tinyint  NULL,  scale   tinyint  NULL,  collation_name  sysname NULL, user_type_id  int NULL, user_type_database    sysname NULL,
user_type_schema    sysname NULL,user_type_name sysname NULL,assembly_qualified_type_name   nvarchar(4000),xml_collection_id    int NULL,xml_collection_database    sysname NULL,
xml_collection_schema   sysname NULL,xml_collection_name    sysname NULL,is_xml_document    bit  NULL,is_case_sensitive bit  NULL,is_fixed_length_clr_type  bit  NULL,
source_server   sysname NULL,source_database    sysname NULL,source_schema  sysname NULL,source_table   sysname NULL,source_column  sysname NULL,is_identity_column bit NULL,
is_part_of_unique_key   bit NULL,is_updateable  bit NULL,is_computed_column bit NULL,is_sparse_column_set   bit NULL,ordinal_in_order_by_list   smallint NULL,
order_by_list_length    smallint NULL,order_by_is_descending    smallint NULL,tds_type_id   int  NULL,tds_length    int  NULL,tds_collation_id  int NULL,
tds_collation_sort_id   tinyint NULL)


-- Get result set definition of your procedure
insert into #d
EXEC sp_describe_first_result_set @exestr, NULL, 0

-- Create a query to generate and populate a global temp table from above results
select 
@qry = 'Create table ##t(' +
stuff(  
    (select ',' + name + ' '+ system_type_name + ' NULL'
    from #d d For XML Path, TYPE)
    .value(N'.[1]', N'nvarchar(max)')
, 1,1,'')
+ ')

insert into ##t 
Exec '+@procname+' ' + @param

Exec sp_executesql @qry

-- Use below global temp table to query the data as you may
select * from ##t

-- **WARNING** Don't forget to drop the global temp table ##t.
--drop table ##t
drop table #d 

พัฒนาและทดสอบกับเวอร์ชั่นของ SQL Server - Microsoft SQL Server 2016 (RTM) - 13.0.1601.5 (Build 17134 :)

คุณสามารถปรับแต่งคีมาสำหรับเวอร์ชั่นเซิร์ฟเวอร์ SQL ที่คุณใช้งานอยู่ (ถ้าจำเป็น)


4

หากคุณรู้พารามิเตอร์ที่ถูกส่งผ่านและหากคุณไม่มีสิทธิ์เข้าถึงเพื่อสร้าง sp_configure ให้แก้ไขโพรซีเดอร์ที่เก็บด้วยพารามิเตอร์เหล่านี้และสามารถเก็บไว้ในตารางสากล ## ได้


3

สิ่งนี้สามารถทำได้ใน SQL Server 2014+ โดยให้ขั้นตอนการจัดเก็บส่งคืนเพียงหนึ่งตาราง หากใครพบวิธีการทำเช่นนี้สำหรับหลายตารางฉันชอบที่จะรู้เกี่ยวกับมัน

DECLARE @storedProcname NVARCHAR(MAX) = ''
SET @storedProcname = 'myStoredProc'

DECLARE @strSQL AS VARCHAR(MAX) = 'CREATE TABLE myTableName '

SELECT @strSQL = @strSQL+STUFF((
SELECT ',' +name+' ' + system_type_name 
FROM sys.dm_exec_describe_first_result_set_for_object (OBJECT_ID(@storedProcname),0)
FOR XML PATH('')
),1,1,'(') + ')'

EXEC (@strSQL)

INSERT INTO myTableName

EXEC ('myStoredProc @param1=1, @param2=2')

SELECT * FROM myTableName

DROP TABLE myTableName

สิ่งนี้จะดึงคำจำกัดความของตารางที่ส่งคืนจากตารางระบบและใช้เพื่อสร้างตารางชั่วคราวสำหรับคุณ จากนั้นคุณสามารถเติมได้จากขั้นตอนการจัดเก็บตามที่ระบุไว้ก่อนหน้า

นอกจากนี้ยังมีรุ่นที่ทำงานกับ Dynamic SQL เช่นกัน


2

ไม่กี่ปีที่ผ่านมาคำถาม แต่ฉันต้องการสิ่งนี้เพื่อสร้างรหัสที่รวดเร็วและสกปรก ฉันเชื่อว่าคนอื่น ๆ ระบุว่ามันง่ายกว่าที่จะกำหนดตารางอุณหภูมิไว้ข้างหน้า แต่วิธีนี้ควรใช้สำหรับการค้นหาโพรซีเดอร์ที่เก็บไว้อย่างง่ายหรือ statments sql

สิ่งนี้จะซับซ้อนเล็กน้อย แต่ยืมมาจากผู้สนับสนุนที่นี่รวมถึงโซลูชันของ Paul White จาก DBA Stack Exchange รับคอลัมน์ผลลัพธ์ประเภทกระบวนงานที่เก็บไว้รับการจัดเก็บผลขั้นตอนคอลัมน์ประเภทอีกครั้งเพื่อย้ำแนวทางและตัวอย่างนี้ไม่ได้ออกแบบมาสำหรับกระบวนการในสภาพแวดล้อมผู้ใช้หลายคน ในกรณีนี้การกำหนดตารางจะถูกกำหนดเป็นเวลาสั้น ๆ ในตารางชั่วคราวทั่วโลกสำหรับการอ้างอิงโดยกระบวนการแม่แบบการสร้างรหัส

ฉันยังไม่ได้ทดสอบอย่างเต็มที่ดังนั้นอาจมีข้อควรระวังดังนั้นคุณอาจต้องการไปที่ลิงก์ MSDN ในคำตอบของ Paul White สิ่งนี้ใช้กับ SQL 2012 และสูงกว่า

ขั้นแรกให้ใช้กระบวนงานที่เก็บไว้sp_describe_first_result_setซึ่งคล้ายกับคำอธิบายของ Oracle

การดำเนินการนี้จะประเมินแถวแรกของชุดผลลัพธ์แรกดังนั้นหากขั้นตอนหรือคำสั่งที่เก็บไว้ของคุณส่งคืนแบบสอบถามจำนวนมากมันจะอธิบายผลลัพธ์แรกเท่านั้น

ฉันสร้าง proc ที่เก็บไว้เพื่อแยกย่อยงานที่ส่งกลับเขตข้อมูลเดียวเพื่อเลือกจากเพื่อสร้างข้อกำหนดของตาราง temp

CREATE OR ALTER PROCEDURE [dbo].[sp_GetTableDefinitionFromSqlBatch_DescribeFirstResultSet]
(
     @sql NVARCHAR(4000)
    ,@table_name VARCHAR(100)
    ,@TableDefinition NVARCHAR(MAX) OUTPUT
)
AS
BEGIN
    SET NOCOUNT ON
    DECLARE @TempTableDefinition NVARCHAR(MAX)
    DECLARE @NewLine NVARCHAR(4) = CHAR(13)+CHAR(10)

    DECLARE @ResultDefinition TABLE (  --The View Definition per MSDN
      is_hidden         bit NOT NULL
    , column_ordinal    int NOT NULL
    , [name]            sysname NULL
    , is_nullable       bit NOT NULL
    , system_type_id    int NOT NULL
    , system_type_name  nvarchar(256) NULL
    , max_length        smallint NOT NULL
    , [precision]       tinyint NOT NULL
    , scale             tinyint NOT NULL
    , collation_name    sysname NULL    
    , user_type_id      int NULL
    , user_type_database    sysname NULL    
    , user_type_schema  sysname NULL
    , user_type_name    sysname NULL    
    , assembly_qualified_type_name      nvarchar(4000)  
    , xml_collection_id         int NULL
    , xml_collection_database   sysname NULL    
    , xml_collection_schema     sysname NULL    
    , xml_collection_name       sysname NULL
    , is_xml_document           bit NOT NULL            
    , is_case_sensitive         bit NOT NULL            
    , is_fixed_length_clr_type  bit NOT NULL    
    , source_server             sysname NULL            
    , source_database           sysname NULL
    , source_schema             sysname NULL
    , source_table              sysname NULL
    , source_column             sysname NULL
    , is_identity_column        bit NULL
    , is_part_of_unique_key     bit NULL
    , is_updateable             bit NULL
    , is_computed_column        bit NULL
    , is_sparse_column_set      bit NULL
    , ordinal_in_order_by_list  smallint NULL   
    , order_by_is_descending    smallint NULL   
    , order_by_list_length      smallint NULL
    , tds_type_id               int NOT NULL
    , tds_length                int NOT NULL
    , tds_collation_id          int NULL
    , tds_collation_sort_id     tinyint NULL
    )

    --Insert the description into table variable    
    INSERT @ResultDefinition
    EXEC sp_describe_first_result_set @sql

    --Now Build the string to create the table via union select statement
    ;WITH STMT AS (
        SELECT N'CREATE TABLE ' + @table_name + N' (' AS TextVal
        UNION ALL

        SELECT 
         CONCAT(
                CASE column_ordinal
                    WHEN 1 THEN '     ' ELSE '   , ' END  --Determines if comma should precede
                , QUOTENAME([name]) , '   ', system_type_name  -- Column Name and SQL TYPE
                ,CASE is_nullable 
                    WHEN 0 THEN '   NOT NULL' ELSE '   NULL' END --NULLABLE CONSTRAINT          
               ) AS TextVal
        FROM @ResultDefinition WHERE is_hidden = 0  -- May not be needed
        UNION ALL

        SELECT N');' + @NewLine
    ) 

    --Now Combine the rows to a single String
    SELECT @TempTableDefinition = COALESCE (@TempTableDefinition + @NewLine + TextVal, TextVal) FROM STMT

    SELECT @TableDefinition = @TempTableDefinition
END

ปริศนาคือคุณต้องใช้ตารางทั่วโลก แต่คุณต้องทำให้มันไม่ซ้ำกันเพื่อให้คุณสามารถวางและสร้างจากมันบ่อยครั้งโดยไม่ต้องกังวลเกี่ยวกับการปะทะกัน
ในตัวอย่างฉันใช้ Guid (FE264BF5_9C32_438F_8462_8A5DC8DEE49E) สำหรับตัวแปรโกลบอลแทนที่ยัติภังค์ด้วยขีดล่าง

DECLARE @sql NVARCHAR(4000) = N'SELECT @@SERVERNAME as ServerName, GETDATE() AS Today;'
DECLARE @GlobalTempTable VARCHAR(100) = N'##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable'

--@sql can be a stored procedure name like dbo.foo without parameters

DECLARE @TableDef NVARCHAR(MAX)

DROP TABLE IF EXISTS #MyTempTable
DROP TABLE IF EXISTS ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable

EXEC [dbo].[sp_GetTableDefinitionFromSqlBatch_DescribeFirstResultSet] 
    @sql, @GlobalTempTable, @TableDef OUTPUT

--Creates the global table ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable
EXEC sp_executesql @TableDef 

--Now Call the stored procedure, SQL Statement with Params etc.
INSERT ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable
    EXEC sp_executesql @sql 

--Select the results into your undefined Temp Table from the Global Table
SELECT * 
INTO #MyTempTable
FROM ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable

SELECT * FROM #MyTempTable

DROP TABLE IF EXISTS #MyTempTable
DROP TABLE IF EXISTS ##FE264BF5_9C32_438F_8462_8A5DC8DEE49E_MyTempTable

อีกครั้งฉันได้ทดสอบเฉพาะกับแบบสอบถามขั้นตอนการจัดเก็บง่ายและแบบสอบถามง่ายๆเพื่อให้ระยะของคุณอาจแตกต่างกันไป หวังว่านี่จะช่วยใครซักคน


1

คุณต้องสร้างตาราง temp แต่ไม่จำเป็นต้องมี schema ที่ถูกต้อง .... ฉันได้สร้างโพรซีเดอร์ที่เก็บไว้ซึ่งปรับเปลี่ยนตาราง temp ที่มีอยู่แล้วเพื่อให้มีคอลัมน์ที่ต้องการพร้อมกับข้อมูลที่ถูกต้อง ประเภทและคำสั่ง (วางคอลัมน์ที่มีอยู่ทั้งหมดเพิ่มคอลัมน์ใหม่):

GO
create procedure #TempTableForSP(@tableId int, @procedureId int)  
as   
begin  
    declare @tableName varchar(max) =  (select name  
                                        from tempdb.sys.tables 
                                        where object_id = @tableId
                                        );    
    declare @tsql nvarchar(max);    
    declare @tempId nvarchar(max) = newid();      
    set @tsql = '    
    declare @drop nvarchar(max) = (select  ''alter table tempdb.dbo.' + @tableName 
            +  ' drop column ''  + quotename(c.name) + '';''+ char(10)  
                                   from tempdb.sys.columns c   
                                   where c.object_id =  ' + 
                                         cast(@tableId as varchar(max)) + '  
                                   for xml path('''')  
                                  )    
    alter table tempdb.dbo.' + @tableName + ' add ' + QUOTENAME(@tempId) + ' int;
    exec sp_executeSQL @drop;    
    declare @add nvarchar(max) = (    
                                select ''alter table ' + @tableName 
                                      + ' add '' + name 
                                      + '' '' + system_type_name 
                           + case when d.is_nullable=1 then '' null '' else '''' end 
                                      + char(10)   
                              from sys.dm_exec_describe_first_result_set_for_object(' 
                               + cast(@procedureId as varchar(max)) + ', 0) d  
                                order by column_ordinal  
                                for xml path(''''))    

    execute sp_executeSQL  @add;    
    alter table '  + @tableName + ' drop column ' + quotename(@tempId) + '  ';      
    execute sp_executeSQL @tsql;  
end         
GO

create table #exampleTable (pk int);

declare @tableId int = object_Id('tempdb..#exampleTable')
declare @procedureId int = object_id('examplestoredProcedure')

exec #TempTableForSP @tableId, @procedureId;

insert into #exampleTable
exec examplestoredProcedure

หมายเหตุสิ่งนี้จะไม่ทำงานหาก sys.dm_exec_describe_first_result_set_for_object ไม่สามารถระบุผลลัพธ์ของกระบวนงานที่เก็บไว้ได้ (ตัวอย่างเช่นหากใช้ตารางชั่วคราว)


0

หากคุณปล่อยให้ไดนามิก SQL สร้างตารางชั่วคราวตารางนี้จะเป็นเจ้าของโดยการเชื่อมต่อไดนามิก SQL ซึ่งตรงข้ามกับการเชื่อมต่อที่เรียกว่ากระบวนงานที่เก็บไว้ของคุณ

DECLARE @COMMA_SEPARATED_KEYS varchar(MAX);
DROP TABLE IF EXISTS KV;
CREATE TABLE KV (id_person int, mykey varchar(30), myvalue int);
INSERT INTO KV VALUES
(1, 'age', 16),
(1, 'weight', 63),
(1, 'height', 175),
(2, 'age', 26),
(2, 'weight', 83),
(2, 'height', 185);
WITH cte(mykey) AS (
    SELECT DISTINCT mykey FROM KV
) 
SELECT @COMMA_SEPARATED_KEYS=STRING_AGG(mykey,',') FROM cte;
SELECT @COMMA_SEPARATED_KEYS AS keys;

ป้อนคำอธิบายรูปภาพที่นี่

DECLARE @ExecuteExpression varchar(MAX);

DROP TABLE IF EXISTS #Pivoted;

SET @ExecuteExpression = N'
SELECT * 
INTO #Pivoted
FROM
(
    SELECT
        mykey,
        myvalue,
        id_person
    FROM KV
) AS t
PIVOT(
    MAX(t.myvalue) 
    FOR mykey IN (COMMA_SEPARATED_KEYS)
) AS pivot_table;
';

SET @ExecuteExpression = REPLACE(@ExecuteExpression, 'COMMA_SEPARATED_KEYS', @COMMA_SEPARATED_KEYS);

EXEC(@ExecuteExpression);

SELECT * FROM #Pivoted;

ข่าวสารเกี่ยวกับ 208, ระดับ 16, สถานะ 0 ชื่อวัตถุไม่ถูกต้อง '#Pivoted' นี่เป็นเพราะ #Pivoted เป็นเจ้าของโดยการเชื่อมต่อ Dynamic SQL ดังนั้นคำสั่งสุดท้าย

SELECT * FROM #Pivoted

ล้มเหลว

วิธีหนึ่งที่จะไม่ประสบปัญหานี้คือการตรวจสอบให้แน่ใจว่าการอ้างอิงถึง #Pivoted ทั้งหมดนั้นทำจากภายในเคียวรีแบบไดนามิกเอง:

DECLARE @COMMA_SEPARATED_KEYS varchar(MAX);
DROP TABLE IF EXISTS KV;
CREATE TABLE KV (id_person int, mykey varchar(30), myvalue int);
INSERT INTO KV VALUES
(1, 'age', 16),
(1, 'weight', 63),
(1, 'height', 175),
(2, 'age', 26),
(2, 'weight', 83),
(2, 'height', 185);
WITH cte(mykey) AS (
    SELECT DISTINCT mykey FROM KV
) 
SELECT @COMMA_SEPARATED_KEYS=STRING_AGG(mykey,',') FROM cte;
SELECT @COMMA_SEPARATED_KEYS AS keys;


DECLARE @ExecuteExpression varchar(MAX);

DROP TABLE IF EXISTS #Pivoted;

SET @ExecuteExpression = N'
SELECT * 
INTO #Pivoted
FROM
(
    SELECT
        mykey,
        myvalue,
        id_person
    FROM KV
) AS t
PIVOT(
    MAX(t.myvalue) 
    FOR mykey IN (COMMA_SEPARATED_KEYS)
) AS pivot_table;
SELECT * FROM #Pivoted;
';

SET @ExecuteExpression = REPLACE(@ExecuteExpression, 'COMMA_SEPARATED_KEYS', @COMMA_SEPARATED_KEYS);

EXEC(@ExecuteExpression);

ป้อนคำอธิบายรูปภาพที่นี่


-5

ฉันจะทำต่อไปนี้

  1. สร้าง (แปลง SP เป็น) UDF (ค่าตาราง UDF)

  2. select * into #tmpBusLine from dbo.UDF_getBusinessLineHistory '16 Mar 2009'


2
อาจมีสิ่งกีดขวางทำให้ขั้นตอนแรกของคุณ ตัวอย่างเช่นถ้า SP ดั้งเดิมใช้ตารางชั่วคราว UDF ไม่สามารถใช้ตารางชั่วคราวได้
yucer
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.