2010-07-31 6 views
55

Как я могу использовать параметр внутри SQL OPENQUERY, такие как:включая параметры в 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

Обойти это создать представление с OPENQUERY, а затем с помощью представления в соединении – Ismael

ответ

98

Из документации OPENQUERY говорится, что:

OPENQUERY не принимает переменные для своих аргументов.

См. Это article для обходного пути.

UPDATE:

Как было предложено, я в том числе рекомендации из статьи ниже.

Pass Основные ценности

Когда основной оператор 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 (@[email protected]) 

Используйте sp_executesql хранимую процедуру

Чтобы избежать многоуровневые кавычки, используйте код, который похож на следующий пример:

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

Используя любой из этих примеров, как вы извлекаете записи, возвращаемые командой exec? – philreed

+1

Чтобы получить записи, я всегда создавал таблицу или таблицу temp моего результирующего набора, затем использовал 'INSERT INTO @TableVariable EXEC sp_executeSql @ TSQL' – Bret

+0

Могу ли я спросить, почему вам нужно использовать столько одиночных кавычек? –

13

Вы можете выполнить строку с 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) 
+0

К сожалению, это не работает, если вы хотите использовать OpenQuery в контексте с 'если ', например, в' if (SELECT Col1 FROM OPENQUERY ('Select ...')> 0) BEGIN ... END' –

+0

@Stefan - вы можете выбрать из openquery и вставить результаты во временную таблицу. Разумеется, ваши варианты открываются очень много. – Jagd

11

От MSDN page:

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.

+0

это указывало мне на правильный путь, чтобы добиться успеха в том, что я пытался достичь! Спасибо! это должно иметь больше приоритетов – 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

В следующем примере я передаю параметр отдела хранимой процедуре (spIncreaseTotalsRpt), и в то же время я создаю приходя во временную таблицу из OPENQUERY. Таблица Temp должна быть глобальной Temp (##), поэтому на нее можно ссылаться вне ее. С помощью 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

6

На самом деле, мы нашли способ сделать это:

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.

Мы протестировали процедуру Store в MSSQL 2012, но должны работать с MSSQL 2008+.

Microsoft Говорит, что sp_executesql (Transact-SQL): Применяется к: SQL Server (SQL Server 2008 через текущую версию), SQL Azure SQL Database (начальная версия в текущей версии). (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

Этот код нуждается в предупреждении, что A) поле1 для * ВСЕ * строк TABLENAME будет передано через связанный сервер - потенциально очень дорогостоящая операция. B) INNER JOIN также может быть «очень дорогим», – brewmanz

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]) ') 
-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

Простой пример, основанный от примера @Tuan Зайди в выше которой, казалось самым простым. Не знал, что вы можете сделать фильтр снаружи OPENQUERY ... намного проще!

Однако в моем случае мне нужно было записать его в переменную, поэтому я создал дополнительный уровень Sub Query, чтобы вернуть одно значение.

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

Объединить динамический 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; 
Смежные вопросы