Як я можу використовувати параметр всередині 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
Як я можу використовувати параметр всередині 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 зазначено, що:
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
INSERT INTO @TableVariable EXEC sp_executeSql @TSQL
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'''' '')'
. Сподіваюся, це допомагає!
SET @DAX = REPLACE(@DAX, '''', '''''')
Ви можете виконати рядок за допомогою 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)
if
, наприклад, уif (SELECT Col1 FROM OPENQUERY('Select ...') > 0 ) BEGIN ... END
На сторінці 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
можуть знадобитися для підходу.
Власне, ми знайшли спосіб зробити це:
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 )
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.
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
Поєднуйте динамічний 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;
У наступному прикладі я передаю параметр відділу збереженій процедурі (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
Я придумав спосіб, який працює для мене. Це вимагає використання скретч-таблиці, до якої пов'язаний сервер має доступ.
Я створив таблицю і заповнив її значеннями, які мені потрібні, тоді я посилаюся на цю таблицю через пов'язаний сервер.
SELECT *
FROM OPENQUERY(KHSSQLODSPRD,'SELECT *
FROM ABC.dbo.CLAIM A WITH (NOLOCK)
WHERE A.DOS >= (SELECT MAX(DATE) FROM KHSDASQL01.DA_MAIN.[dbo].[ALLFILENAMES]) ')
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
повинен знати і цей підхід. Це також набагато чистіше. Тож +1 з мого боку.
Ми можемо використовувати 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';
Простий приклад, заснований на прикладі @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)