2010-07-31 27 views
55

Làm thế nào tôi có thể sử dụng một tham số bên trong sql OPENQUERY, chẳng hạn như:bao gồm các thông số trong OPENQUERY

SELECT * FROM OPENQUERY([NameOfLinkedSERVER], 'SELECT * FROM TABLENAME 
where [email protected]') T1 INNER JOIN MYSQLSERVER.DATABASE.DBO.TABLENAME 
T2 ON T1.PK = T2.PK 
+0

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

Trả lời

98

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

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

Xem điều này article để giải quyết sự cố.

UPDATE:

Như đã đề cập, tôi bao gồm các khuyến nghị từ các bài viết dưới đây.

đèo Basic Values ​​

Khi tuyên bố Transact-SQL cơ bản được biết đến, nhưng bạn phải vượt qua trong một hoặc các giá trị cụ thể hơn, sử dụng mã tương tự như các 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 Tổng Query

Khi bạn phải vượt qua trong truy vấn Transact-SQL toàn bộ hoặc tên của máy chủ liên kết (hoặc cả hai), sử dụng cá tuyết e rằng cũng tương tự như các 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 (@[email protected]) 

Sử dụng sp_executesql Stored Procedure

Để tránh các dấu ngoặc kép nhiều lớp, sử dụng mã tương tự như các 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 
+7

Sử dụng bất kỳ ví dụ nào trong số này, làm thế nào để bạn lấy lại các bản ghi được trả về từ lệnh exec? – philreed

+1

Để lấy các bản ghi, tôi luôn xây dựng một biến bảng hoặc bảng tạm thời của tập kết quả, sau đó sử dụng 'INSERT INTO @TableVariable EXEC sp_executeSql @ TSQL' – Bret

+0

Tôi có thể hỏi tại sao bạn cần sử dụng nhiều dấu nháy đơn? –

13

Bạn có thể thực hiện một chuỗi với OPENQUERY khi bạn xây dựng nó lên. Nếu bạn đi tuyến đường này suy nghĩ về an ninh và chăm sóc không để ghép nối văn bản 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) 
+0

Đáng tiếc là nó không hoạt động, nếu bạn muốn sử dụng OpenQuery trong bối cảnh với 'nếu ', ví dụ trong' if (SELECT Col1 FROM OPENQUERY ('Chọn ...')> 0) BEGIN ... END' –

+0

@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ừ đó các tùy chọn của bạn mở ra rất nhiều, tất nhiên. – Jagd

11

Từ MSDN page:

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 bạn không thể đưa ra một truy vấn năng động. Để đạt được những gì mẫu của bạn đang cố gắng, cố gắng 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 có chứa một lượng lớn dữ liệu, điều này sẽ đi qua mạng quá và hiệu suất có thể là người nghèo. 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 chi phí xây dựng sql động (phun sql, dấu ngoặc kép thoát) mà phương pháp exec có thể yêu cầu.

+0

điều này đã chỉ cho tôi xuống con đường bên phải để thành công trong những gì tôi đã cố gắng để hoàn thành! Cảm ơn bạn! điều này cần phải có nhiều upvotes hơn – Malachi

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 !$#@[email protected] thing in a string. 
1

Trong ví dụ sau, tôi chuyển một tham số bộ phận đến một thủ tục được lưu trữ (spIncreaseTotalsRpt) và đồng thời tôi là cre ating một bảng tạm thời tất cả từ một OPENQUERY. Bảng Temp cần phải là một Temp (##) toàn cầu để nó có thể được tham chiếu bên ngoài nó. Bằng cách sử dụng exec sp_executesql bạn có thể vượt qua 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 

Kết quả

Dept tăng Cnt 0 1 2 3 4 5 6 0,0000 1,0000 0,0000 0,0000 0,0000 0,0000 0,0000 0,0000

6

Trên thực tế, chúng tôi tìm thấy một 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ẽ gán kết quả của việc thực thi OpenQuery, trong biến @Output.

Chúng tôi đã thử nghiệm quy trình Lưu trữ trong MSSQL 2012, nhưng phải hoạt động với MSSQL 2008+.

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

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

Mã này cần cảnh báo rằng A) trường1 cho * ALL * hàng của TABLENAME sẽ được chuyển qua từ máy chủ được liên kết - một hoạt động có khả năng rất tốn kém. B) INNER JOIN cũng có thể là 'rất tốn kém' – brewmanz

0

Tôi đã tìm ra cách phù hợp với mình. Nó đòi hỏi việc sử dụng một bảng cào mà một máy chủ được liên kết đượ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 khảo 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]) ') 
-2
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] 
-1

dụ đơn giản dựa tắt của dụ @Tuan Zaidi của trên mà dường như dễ nhất. Không biết bạn có thể làm 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 nhồi nhét nó trong một biến vì vậy tôi đã tạo một Cấp truy vấn phụ bổ sung để trả lại 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) 
0

Kết hợp SQL động với OpenQuery. (Điều này đi đến một 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; 
Các vấn đề liên quan