bao gồm các thông số trong OPENQUERY


86

Làm cách nào để sử dụng một tham số bên trong sql openquery, chẳng hạn như:

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

Một cách giải quyết là tạo ra một cái nhìn với OPENQUERY và sau đó sử dụng giao diện trong tham gia
Ismael

Câu trả lời:


156

Từ tài liệu OPENQUERY, nó nói rằng:

OPENQUERY không chấp nhận các biến cho các đối số của nó.

Xem bài viết này để biết cách giải quyết.

CẬP NHẬT:

Như được đề xuất, tôi bao gồm các đề xuất từ ​​bài viết bên dưới.

Vượt qua các giá trị cơ bản

Khi đã biết câu lệnh Transact-SQL cơ bản, nhưng bạn phải chuyển một hoặc nhiều giá trị cụ thể, hãy sử dụng mã tương tự như mẫu sau:

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)

Vượt qua toàn bộ truy vấn

Khi bạn phải chuyển toàn bộ truy vấn Transact-SQL hoặc tên của máy chủ được liên kết (hoặc cả hai), hãy sử dụng mã tương tự như mẫu sau:

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) 

Sử dụng thủ tục lưu trữ Sp_executesql

Để tránh các dấu ngoặc kép nhiều lớp, hãy sử dụng mã tương tự như mẫu sau:

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
Sử dụng bất kỳ ví dụ nào trong số này, làm cách nào để bạn truy xuất các bản ghi được trả về từ lệnh thi hành?
philreed

2
Để truy xuất các bản ghi, tôi luôn tạo một biến bảng hoặc bảng tạm thời của tập kết quả của mình, sau đó được sử dụngINSERT INTO @TableVariable EXEC sp_executeSql @TSQL
Bret

6
@JamesChen, dễ dàng nhất để nghĩ đến khi bạn làm việc ngược lại. Bắt đầu với truy vấn của người OpenQuery: SELECT * FROM tab WHERE col = 'Y'. Để vượt qua rằng tuyên bố như là một chuỗi để OpenQuery, tất cả các dấu nháy đơn cần thoát: SELECT * FROM OPENQUERY(Server, 'SELECT * FROM tab WHERE col = ''Y'' '). Sau đó, để vượt qua SELECT sử dụng OpenQuery Dynamic SQL, CÁC VẤN ĐỀ dấu ngoặc kép phải được thoát ra: EXEC sp_executeSQL 'SELECT * FROM OPENQUERY(Server, ''SELECT * FROM tab WHERE col = ''''Y'''' '')'. Hi vọng điêu nay co ich!
Bret

(không thể sử dụng @ trong các nhận xét nên sử dụng 'at') DAX động với các tham số ... Tôi đã chuẩn bị chuỗi DAX nhưng Openquery không hoạt động. Vượt qua phương thức Truy vấn Toàn bộ đã hoạt động. Xếp hạng DAX của tôi thông qua điều này: SET atDAX = REPLACE (atDAX, '' '', '' '' '') và cuối cùng tôi phải đóng chuỗi và thêm dấu ngoặc cuối cùng ... EXEC (atOPENQUERY + atDAX + '' '' + ')')
TDP

@TDP bạn có thể, nếu bạn định dạng mã nội tuyến là mã nội tuyến (sử dụng dấu gạch ngược):SET @DAX = REPLACE(@DAX, '''', '''''')
Mathieu Guindon

15

Bạn có thể thực thi một chuỗi với OPENQUERY sau khi bạn xây dựng nó. Nếu bạn đi theo con đường này, hãy nghĩ về bảo mật và cẩn thận không nối văn bản do người dùng nhập vào SQL của bạn!

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)

Đáng tiếc là nó không làm việc, nếu bạn muốn sử dụng OpenQuery trong bối cảnh với if, ví dụ như trongif (SELECT Col1 FROM OPENQUERY('Select ...') > 0 ) BEGIN ... END
Stefan Brendle

@Stefan - bạn có thể chọn từ openquery và chèn kết quả vào một bảng tạm thời. Tất nhiên, từ đó các tùy chọn của bạn sẽ mở ra rất nhiều.
Jagd

13

Từ trang MSDN :

OPENQUERY không chấp nhận các biến cho các đối số của nó

Về cơ bản, điều này có nghĩa là bạn không thể đưa ra một truy vấn động. Để đạt được những gì mẫu của bạn đang cố gắng, hãy thử cách này:

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

Rõ ràng nếu bảng TABLENAME của bạn chứa một lượng lớn dữ liệu, thì dữ liệu này cũng sẽ đi qua mạng và hiệu suất có thể kém. Mặt khác, đối với một lượng nhỏ dữ liệu, điều này hoạt động tốt và tránh các chi phí xây dựng sql động (chèn sql, dấu ngoặc kép) mà một execphương pháp có thể yêu cầu.


điều này đã chỉ cho tôi con đường đúng đắn để thành công trong những gì tôi đang cố gắng hoàn thành! Cảm ơn bạn! điều này sẽ có nhiều phiếu ủng hộ hơn
Malachi

7

Trên thực tế, chúng tôi đã tìm ra cách để làm điều này:

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

Điều này sẽ chỉ định kết quả của việc thực thi OpenQuery, trong biến @Output.

Chúng tôi đã thử nghiệm thủ tục Store trong MSSQL 2012, nhưng sẽ hoạt động với MSSQL 2008+.

Microsoft nói rằng sp_executesql (Transact-SQL): Áp dụng cho: SQL Server (SQL Server 2008 đến phiên bản hiện tại), Cơ sở dữ liệu Windows Azure SQL (Bản phát hành ban đầu cho đến bản phát hành hiện tại). ( http://msdn.microsoft.com/en-us/library/ms188001.aspx )


Điều này chỉ hoạt động đối với đầu ra Vô hướng. Hãy thử Xml hoặc một biến Bảng và điều này sẽ không hoạt động.
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
Mã này cần cảnh báo rằng A) field1 cho TẤT CẢ các hàng TABLENAME sẽ được chuyển qua máy chủ được liên kết - một hoạt động có thể rất tốn kém. B) THAM GIA INNER cũng có thể 'rất đắt'
brewmanz

2

Kết hợp SQL động với OpenQuery. (Điều này đến máy chủ 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

Trong ví dụ sau, tôi đang chuyển một tham số của bộ phận tới một thủ tục được lưu trữ (spIncreaseTotalsRpt) và đồng thời tôi đang tạo một bảng tạm thời từ một OPENQUERY. Bảng Temp cần phải là một Temp toàn cục (##) để nó có thể được tham chiếu bên ngoài nội dung của nó. Bằng cách sử dụng execute sp_executesql, bạn có thể chuyển tham số bộ phận.

Lưu ý: hãy cẩn thận khi sử dụng sp_executeSQL. Ngoài ra, quản trị viên của bạn có thể không có tùy chọn này cho bạn.

Hy vọng điều này sẽ giúp ai đó.

 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

Các kết quả

Tăng tiền gửi 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

Tôi đã tìm ra một cách phù hợp với tôi. Nó yêu cầu sử dụng một bảng cào mà một máy chủ được liên kết có quyền truy cập.

Tôi đã tạo một bảng và điền nó với các giá trị tôi cần, sau đó tôi tham chiếu bảng đó thông qua một máy chủ được liên kết.

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]

Từ khóa là openquery dude :)
Christian

nó là một cách tiếp cận thay thế. Ai biết openquerycũng nên biết cách làm này. Nó cũng sạch hơn nhiều. Vì vậy, +1 từ phía tôi.
Sheikh Abdul Wahid

0

Chúng ta có thể sử dụng executephương pháp thay vì openquery. Mã của nó sạch hơn nhiều. Tôi phải nhận linked serverkết quả truy vấn trong một biến. Tôi đã sử dụng mã sau đây.

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

Ghi chú:

nếu truy vấn của bạn không hoạt động, hãy đảm bảo rằng bạn remote proc transaction promotionđược đặt như falsecho linked serverkết nối của mình .

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

-1

Ví dụ đơn giản dựa trên ví dụ của @Tuan Zaidi ở trên, có vẻ dễ nhất. Không biết rằng bạn có thể thực hiện bộ lọc bên ngoài OPENQUERY ... dễ dàng hơn nhiều!

Tuy nhiên, trong trường hợp của tôi, tôi cần phải đưa nó vào một biến, vì vậy tôi đã tạo thêm Cấp truy vấn phụ để trả về một giá trị duy nhất.

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