включаючи параметри в 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

Обхідним шляхом є створення представлення за допомогою відкритого запиту, а потім використання подання в об'єднанні
Ісмаель

Відповіді:


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

Щоб уникнути багатошарових лапок, використовуйте код, подібний до наведеного нижче зразка:

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
Брет,

6
@JamesChen, про це найлегше думати, коли ти працюєш назад. Почніть із запитом на OpenQuery в: SELECT * FROM tab WHERE col = 'Y'. Для того, щоб передати цю заяву у вигляді рядка в 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'''' '')'. Сподіваюся, це допомагає!
Брет

(не можна використовувати @ у коментарях, тому використовуючи 'at') Динамічний DAX із параметрами ... Я підготував рядок DAX, але Openquery не працював. Передайте спрацьований метод Whole Query. Провів мій DAX через це: SET atDAX = REPLACE (atDAX, '' '', '' '' '') і в кінці мені довелося закрити рядок і додати заключну дужку ... EXEC (atOPENQUERY + atDAX + '' '' + ')')
TDP

@TDP ви могли б, якби відформатували вбудований код як вбудований код (використовуючи зворотні SET @DAX = REPLACE(@DAX, '''', '''''')
позначки

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 та вставити результати у тимчасову таблицю. Звідси ваші варіанти, звичайно, відкриваються багато.
Ягд,

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можуть знадобитися для підходу.


це вказало мені на правильний шлях до успіху в тому, що я намагався досягти! Дякую! це повинно мати більше голосів
Малахія

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.

Ми протестували процедуру Store у MSSQL 2012, але повинні працювати з MSSQL 2008+.

Microsoft каже, що sp_executesql (Transact-SQL): Застосовується до: SQL Server (SQL Server 2008 до поточної версії), Бази даних SQL Windows Azure (Початковий випуск через поточний випуск). ( http://msdn.microsoft.com/en-us/library/ms188001.aspx )


Це працює лише для скалярного виводу. Спробуйте 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 буде передано через зв'язаний сервер - потенційно дуже дорога операція. Б) ВНУТРІШНЄ
ПРИЄДНАННЯ

2

Поєднуйте динамічний 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 (##), щоб на неї можна було посилатися за межами її інстанції. За допомогою 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 чувак :)
Крістіан

це альтернативний підхід. Хто знає, openqueryповинен знати і цей підхід. Це також набагато чистіше. Тож +1 з мого боку.
Шейх Абдул Вахід

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.