รวมถึงพารามิเตอร์ใน OPENQUERY


86

ฉันจะใช้พารามิเตอร์ภายใน sql openquery ได้อย่างไรเช่น:

SELECT * FROM OPENQUERY([NameOfLinkedSERVER], 'SELECT * FROM TABLENAME
where field1=@someParameter') T1 INNER JOIN MYSQLSERVER.DATABASE.DBO.TABLENAME
T2 ON T1.PK = T2.PK

วิธีแก้ปัญหาคือสร้างมุมมองด้วย openQuery จากนั้นใช้มุมมองในการเข้าร่วม
Ismael

คำตอบ:


156

จากเอกสารOPENQUERYระบุว่า:

OPENQUERY ไม่ยอมรับตัวแปรสำหรับอาร์กิวเมนต์

ดูบทความนี้สำหรับวิธีแก้ปัญหาชั่วคราว

อัพเดท:

ตามที่แนะนำฉันรวมคำแนะนำจากบทความด้านล่าง

ส่งผ่านค่าพื้นฐาน

เมื่อทราบคำสั่ง Transact-SQL พื้นฐาน แต่คุณต้องส่งผ่านค่าเฉพาะอย่างน้อยหนึ่งค่าให้ใช้โค้ดที่คล้ายกับตัวอย่างต่อไปนี้:

DECLARE @TSQL varchar(8000), @VAR char(2)
SELECT  @VAR = 'CA'
SELECT  @TSQL = 'SELECT * FROM OPENQUERY(MyLinkedServer,''SELECT * FROM pubs.dbo.authors WHERE state = ''''' + @VAR + ''''''')'
EXEC (@TSQL)

ส่งแบบสอบถามทั้งหมด

เมื่อคุณต้องส่งผ่านคิวรี Transact-SQL ทั้งหมดหรือชื่อของเซิร์ฟเวอร์ที่เชื่อมโยง (หรือทั้งสองอย่าง) ให้ใช้โค้ดที่คล้ายกับตัวอย่างต่อไปนี้:

DECLARE @OPENQUERY nvarchar(4000), @TSQL nvarchar(4000), @LinkedServer nvarchar(4000)
SET @LinkedServer = 'MyLinkedServer'
SET @OPENQUERY = 'SELECT * FROM OPENQUERY('+ @LinkedServer + ','''
SET @TSQL = 'SELECT au_lname, au_id FROM pubs..authors'')' 
EXEC (@OPENQUERY+@TSQL) 

ใช้ Sp_executesql Stored Procedure

เพื่อหลีกเลี่ยงเครื่องหมายคำพูดหลายชั้นให้ใช้รหัสที่คล้ายกับตัวอย่างต่อไปนี้:

DECLARE @VAR char(2)
SELECT  @VAR = 'CA'
EXEC MyLinkedServer.master.dbo.sp_executesql
N'SELECT * FROM pubs.dbo.authors WHERE state = @state',
N'@state char(2)',
@VAR

9
จากตัวอย่างเหล่านี้คุณจะดึงข้อมูลที่ส่งคืนจากคำสั่ง exec ได้อย่างไร?
Philreed

2
ในการดึงบันทึกฉันได้สร้างตัวแปรตารางหรือตารางอุณหภูมิของชุดผลลัพธ์ของฉันเสมอจากนั้นใช้INSERT INTO @TableVariable EXEC sp_executeSql @TSQL
Bret

6
@JamesChen เป็นเรื่องง่ายที่สุดที่จะนึกถึงเมื่อคุณทำงานย้อนหลัง เริ่มต้นด้วยการสอบถามของ SELECT * FROM tab WHERE col = 'Y'OpenQuery: เพื่อส่งผ่านคำสั่งที่เป็นสตริงเพื่อ OpenQuery SELECT * FROM OPENQUERY(Server, 'SELECT * FROM tab WHERE col = ''Y'' ')ทั้งหมดราคาเดียวต้องหนี: จากนั้นจะผ่านการเลือกใช้ OpenQuery แบบไดนามิก SQL, EXEC sp_executeSQL 'SELECT * FROM OPENQUERY(Server, ''SELECT * FROM tab WHERE col = ''''Y'''' '')'คำพูดเหล่านั้นจะต้องหนี: หวังว่านี่จะช่วยได้!
Bret

(ไม่สามารถใช้ @ ในความคิดเห็นดังนั้นโดยใช้ 'at') Dynamic DAX พร้อมพารามิเตอร์ ... ฉันเตรียมสตริง DAX แต่ Openquery ไม่ทำงาน ผ่านวิธีการค้นหาทั้งหมดใช้งานได้ เรียกใช้ DAX ของฉันผ่านสิ่งนี้: SET atDAX = REPLACE (atDAX, '' '', '' '' '') และในตอนท้ายฉันต้องปิดสตริงและเพิ่มวงเล็บสุดท้าย ... ดำเนินการ (atOPENQUERY + atDAX + '' '' + ')')
TDP

@TDP คุณสามารถทำได้ถ้าคุณจัดรูปแบบโค้ดอินไลน์เป็นโค้ดอินไลน์ (โดยใช้แบ็คทิก):SET @DAX = REPLACE(@DAX, '''', '''''')
Mathieu Guindon

15

คุณสามารถเรียกใช้สตริงด้วย OPENQUERY เมื่อคุณสร้างมันขึ้นมา หากคุณไปเส้นทางนี้ให้คิดถึงความปลอดภัยและระวังอย่าเชื่อมข้อความที่ผู้ใช้ป้อนเข้ากับ SQL ของคุณ!

DECLARE @Sql VARCHAR(8000)
SET @Sql = 'SELECT * FROM Tbl WHERE Field1 < ''someVal'' AND Field2 IN '+ @valueList 
SET @Sql = 'SELECT * FROM OPENQUERY(SVRNAME, ''' + REPLACE(@Sql, '''', '''''') + ''')'
EXEC(@Sql)

น่าเสียดายที่มันใช้งานไม่ได้หากคุณต้องการใช้ OpenQuery ในบริบทด้วยifเช่นในif (SELECT Col1 FROM OPENQUERY('Select ...') > 0 ) BEGIN ... END
Stefan Brendle

@Stefan - คุณสามารถเลือกจาก openquery และแทรกผลลัพธ์ลงในตารางชั่วคราว จากนั้นตัวเลือกของคุณจะเปิดขึ้นมากมายแน่นอน
Jagd

13

จากหน้า MSDN :

OPENQUERY ไม่ยอมรับตัวแปรสำหรับอาร์กิวเมนต์

โดยพื้นฐานแล้วหมายความว่าคุณไม่สามารถออกแบบสอบถามแบบไดนามิกได้ เพื่อให้บรรลุสิ่งที่ตัวอย่างของคุณพยายามลองสิ่งนี้:

SELECT * FROM 
   OPENQUERY([NameOfLinkedSERVER], 'SELECT * FROM TABLENAME') T1 
   INNER JOIN 
   MYSQLSERVER.DATABASE.DBO.TABLENAME T2 ON T1.PK = T2.PK 
where
   T1.field1 = @someParameter

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


สิ่งนี้ชี้ให้ฉันเห็นเส้นทางที่ถูกต้องเพื่อประสบความสำเร็จในสิ่งที่ฉันพยายามทำให้สำเร็จ! ขอขอบคุณ! น่าจะมีการโหวตเพิ่มมากขึ้น
Malachi

7

อันที่จริงเราพบวิธีการทำสิ่งนี้:

DECLARE @username varchar(50)
SET @username = 'username'
DECLARE @Output as numeric(18,4)
DECLARE @OpenSelect As nvarchar(500)
SET @OpenSelect = '(SELECT @Output = CAST((CAST(pwdLastSet As bigint) / 864000000000) As numeric(18,4)) FROM OpenQuery (ADSI,''SELECT pwdLastSet
                                FROM  ''''LDAP://domain.net.intra/DC=domain,DC=net,DC=intra''''
                                WHERE objectClass =  ''''User'''' AND sAMAccountName = ''''' + @username + '''''
                          '') AS tblADSI)'
EXEC sp_executesql @OpenSelect, N'@Output numeric(18,4) out', @Output out
SELECT @Output As Outputs

สิ่งนี้จะกำหนดผลลัพธ์ของการดำเนินการ OpenQuery ในตัวแปร @Output

เราทดสอบขั้นตอนการจัดเก็บใน MSSQL 2012 แต่ควรทำงานกับ MSSQL 2008+

Microsoft กล่าวว่า sp_executesql (Transact-SQL): ใช้กับ: SQL Server (SQL Server 2008 จนถึงเวอร์ชันปัจจุบัน), ฐานข้อมูล Windows Azure SQL (รุ่นเริ่มต้นจนถึงรุ่นปัจจุบัน) ( http://msdn.microsoft.com/en-us/library/ms188001.aspx )


ใช้งานได้กับเอาต์พุต Scalar เท่านั้น ลอง Xml หรือตัวแปร Table แล้วจะไม่ได้ผล
user5855178

4
DECLARE @guid varchar(36);  select @guid= convert(varchar(36), NEWID() );
/*
    The one caveat to this technique is that ##ContextSpecificGlobal__Temp should ALWAYS have the exact same columns.  
    So make up your global temp table name in the sproc you're using it in and only there!
    In this example I wanted to pass in the name of a global temporary table dynamically.  I have 1 procedure dropping 
    off temporary data in whatever @TableSrc is and another procedure picking it up but we are dynamically passing 
    in the name of our pickup table as a parameter for OPENQUERY.
*/
IF ( OBJECT_ID('tempdb..##ContextSpecificGlobal__Temp' , 'U') IS NULL )
    EXEC ('SELECT * INTO ##ContextSpecificGlobal__Temp FROM OPENQUERY(loopback, ''Select *,''''' +  @guid +''''' as tempid FROM ' + @TableSrc + ''')')
ELSE 
    EXEC ('INSERT ##ContextSpecificGlobal__Temp SELECT * FROM OPENQUERY(loopback, ''Select *,''''' +  @guid +''''' as tempid FROM ' + @TableSrc + ''')')

--If this proc is run frequently we could run into race conditions, that's why we are adding a guid and only deleting
--the data we added to ##ContextSpecificGlobal__Temp
SELECT * INTO #TableSrc FROM ##ContextSpecificGlobal__Temp WHERE tempid = @guid

BEGIN TRAN t1
    IF ( OBJECT_ID('tempdb..##ContextSpecificGlobal__Temp' , 'U') IS NOT NULL ) 
    BEGIN
        -- Here we wipe out our left overs if there if everyones done eating the data
        IF (SELECT COUNT(*) FROM ##ContextSpecificGlobal__Temp) = 0
            DROP TABLE ##ContextSpecificGlobal__Temp
    END
COMMIT TRAN t1

-- YEAH! Now I can use the data from my openquery without wrapping the whole !$#@$@ thing in a string.

2
SELECT field1 FROM OPENQUERY 
                   ([NameOfLinkedSERVER], 
                   'SELECT field1 FROM TABLENAME') 
                           WHERE field1=@someParameter T1 
                                 INNER JOIN MYSQLSERVER.DATABASE.DBO.TABLENAME           
                                 T2 ON T1.PK = T2.PK

4
รหัสนี้ต้องการคำเตือนว่า A) field1 สำหรับทุกแถวของ TABLENAME จะถูกส่งข้ามจากเซิร์ฟเวอร์ที่เชื่อมโยงซึ่งอาจเป็นการดำเนินการที่มีราคาแพงมาก B) การเข้าร่วมภายในอาจ 'แพงมาก'
brewmanz

2

รวม Dynamic SQL กับ OpenQuery (ไปที่เซิร์ฟเวอร์ Teradata)

DECLARE 
    @dayOfWk    TINYINT = DATEPART(DW, GETDATE()),
    @qSQL       NVARCHAR(MAX) = '';

SET @qSQL = '
SELECT
    *
FROM
    OPENQUERY(TERASERVER,''
        SELECT DISTINCT
            CASE
                WHEN ' + CAST(@dayOfWk AS NCHAR(1)) + ' = 2
                THEN ''''Monday''''
                ELSE ''''Not Monday''''
            END
        '');';

EXEC sp_executesql @qSQL;

1

ในตัวอย่างต่อไปนี้ฉันกำลังส่งพารามิเตอร์แผนกไปยังโพรซีเดอร์ที่เก็บไว้ (spIncreaseTotalsRpt) และในเวลาเดียวกันฉันกำลังสร้างตารางชั่วคราวทั้งหมดจาก OPENQUERY ตาราง Temp ต้องเป็น Temp ส่วนกลาง (##) จึงจะอ้างอิงได้ภายนอก Intance โดยใช้ exec sp_executesql คุณสามารถส่งผ่านพารามิเตอร์แผนก

หมายเหตุ: โปรดใช้ความระมัดระวังเมื่อใช้ sp_executeSQL นอกจากนี้ผู้ดูแลระบบของคุณอาจไม่มีตัวเลือกนี้ให้คุณ

หวังว่านี่จะช่วยใครบางคนได้

 IF OBJECT_ID('tempdb..##Temp') IS NOT NULL
/*Then it exists*/
    begin
       DROP TABLE ##Temp
    end 
 Declare @Dept as nvarchar(20) ='''47'''

 declare @OPENQUERY  as nvarchar(max)
set @OPENQUERY = 'Select ' + @Dept + ' AS Dept,  * into ##Temp from openquery(SQL_AWSPROD01,''' 

declare @sql nvarchar(max)= @openquery +  'SET FMTONLY OFF EXECUTE SalaryCompensation.dbo.spIncreaseTotalsRpts ' + '''' + @Dept + ''''  + ''')'
declare @parmdef nvarchar(25) 
DECLARE @param nvarchar(20) 

SET @parmdef = N'@Dept varchar(20)'
-- select @sql
-- Print @sql + @parmdef  + @dept
exec sp_executesql @sql,@parmdef, @Dept  
Select * from ##Temp

ผล

เพิ่มฝ่าย Cnt 0 1 2 3 4 5 6 0.0000 1.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000


0

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

ฉันสร้างตารางและเติมข้อมูลด้วยค่าที่ฉันต้องการจากนั้นฉันอ้างอิงตารางนั้นผ่านเซิร์ฟเวอร์ที่เชื่อมโยง

SELECT * 
FROM OPENQUERY(KHSSQLODSPRD,'SELECT *
  FROM ABC.dbo.CLAIM A WITH (NOLOCK)
  WHERE A.DOS >= (SELECT MAX(DATE) FROM KHSDASQL01.DA_MAIN.[dbo].[ALLFILENAMES]) ')

0
declare @p_Id varchar(10)
SET @p_Id = '40381'

EXECUTE ('BEGIN update TableName
                set     ColumnName1 = null,
                        ColumnName2 = null,
                        ColumnName3 = null,
                        ColumnName4 = null
                 where   PERSONID = '+ @p_Id +'; END;') AT [linked_Server_Name]

คีย์เวิร์ดคือ openquery dude :)
Christian

มันเป็นวิธีอื่น ใครopenqueryจะรู้ก็ควรรู้แนวทางนี้เช่นกัน นอกจากนี้ยังสะอาดกว่ามาก +1 จากด้านข้างของฉัน
Sheikh Abdul Wahid

0

เราสามารถใช้executeวิธีการแทนopenquery. รหัสมันสะอาดกว่ามาก ฉันต้องได้รับlinked serverผลลัพธ์แบบสอบถามในตัวแปร ฉันใช้รหัสต่อไปนี้

CREATE TABLE #selected_store
(
   code VARCHAR(250),
   id INT
)
declare @storeId as integer = 25
insert into #selected_store (id, code) execute('SELECT store_id, code from quickstartproductionnew.store where store_id = ?', @storeId) at [MYSQL]  

declare @code as varchar(100)
select @code = code from #selected_store
select @code
drop table #selected_store

บันทึก:

หากคำถามของคุณไม่ได้ผลโปรดตรวจสอบว่าremote proc transaction promotionได้ตั้งค่าไว้falseสำหรับlinked serverการเชื่อมต่อของคุณแล้ว

EXEC master.dbo.sp_serveroption
       @server = N'{linked server name}',
       @optname = N'remote proc transaction promotion',
       @optvalue = N'false';

-1

ตัวอย่างง่ายๆจากตัวอย่างของ @Tuan Zaidi ด้านบนซึ่งดูเหมือนง่ายที่สุด ไม่รู้ว่าคุณสามารถทำฟิลเตอร์ด้านนอกของ OPENQUERY ได้ ... ง่ายกว่ามาก!

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

SET @SFID = (SELECT T.Id FROM (SELECT Id,  Contact_ID_SQL__c  FROM OPENQUERY([TR-SF-PROD], 'SELECT Id,  Contact_ID_SQL__c FROM Contact') WHERE Contact_ID_SQL__c = @ContactID) T)

-1

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

SELECT * FROM OPENQUERY([NameOfLinkedSERVER], 'SELECT * FROM TABLENAME where field1=''your search value''') T1 INNER JOIN MYSQLSERVER.DATABASE.DBO.TABLENAME T2 ON T1.PK = T2.PK


คำตอบของคุณใช้ SQL ตามตัวอักษร แต่ OP ต้องการใช้พารามิเตอร์ (โปรดสังเกตการอ้างอิงของเขา / เธอfield1=@someParameter)
Jeff Mergler
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.