Update a table on a Linked Server using OPENQUERY - sql-server

I have two servers: SQLSERVER01 and SQLSERVER02 and I am trying to updated data on SERVER01 from SERVER02 (SERVER01 is a linked server).
My update query is currently dynamic which looks something like this
DECLARE #SQL NVARCHAR(MAX)
DECLARE #ID INT
SET #ID = 1
Set #SQL = 'Update SERVER01.MyDatbase.dbo.MyTable
set ModifiedDate = GetDate(), SomeOtherValue = ''xyz''
Where Id = ' Convert(varchar(10), #ID)
If I now call
EXEC(#SQL)
it will sometimes work, but other times it will just hang there for ages and when I run sp_active I see PREEMPTIVE_OLEDBOPS.
So, I then tried using open query as follows
Select *
From OpenQuery(SERVER01,
'Update SERVER01.MyDatbase.dbo.MyTable
set ModifiedDate = GetDate(), SomeOtherValue = ''xyz''
Where Id = 1')
but I get this error:
The OLE DB provider "SQLNCLI11" for linked server "SERVER01" indicates that either the object has no columns or the current user does not have permissions on that object.
When I run the following select query I successfully return the row I'm trying to update:
Select *
From OpenQuery(SERVER01,
'Select *
From SERVER01.MyDatbase.dbo.MyTable
Where Id = 1')
I know that the user that is running the query has permissions, but I really don't know where to go from here. I read somewhere this error has something to do with the update query not returning a row. Is there any truth to this?
How can I resolve this?
Thanks

Okay, I found the answer. This works successfully:
execute (#SQL) at SERVER01

Related

How to send an email on finding bad data in a table in sql server

I have below EmployeeData table and due to some known reasons BAD data is getting inserted and i am working on code fix, but meanwhile i want to check for bad data until the fix is deployed. If you observe John has 1 for both Active and IsEmpTermed columns.
Currently i am running the below query to check for Bad data manually in SQL Server. I want to automate this task and send an email to me if it finds bad data i.e. Active =1 and IsEmpTermed =1. What and How to do it?
select * from EmployeeData Where Active=1 and IsEmpTermed = 1;
Thanks in advance.
Quick way would be use the #query argument with sp_send_dbmail and put that code in a SQL Server Agent Job.
Here's a simple example of what that sp_send_dbmail looks like:
EXEC msdb.dbo.sp_send_dbmail
#profile_name = '' --This is specific to your setup
,#recipients = 'your#email.com'
,#subject = 'This is the subject'
,#query = 'select EmpID, FirstName, LastName from EmployeeData Where Active=1 and IsEmpTermed = 1' --The query
,#execute_query_database = '' --The name of your database goes here where the query should be executed
That will basically execute the query you specified in #query, dump the results into an email and send it to whomever is in #recipients
If you don't know what the #profile_name is for your server configuration you can run
EXEC msdb.dbo.sysmail_help_profileaccount_sp;
That will return a list of the mail profiles configured on your server. You will use the value from the "profile_name" column. If there are multiples, this is something I can't tell you as it is specific to your server configuration.
Once you have all that defined and working by manually running it in SSMS, go create a SQL Server Agent Job with a T-SQL step and add the "EXEC msdb.dbo.sp_send_dbmail" code. Defined a schedule at whatever frequency you would like it to run.
A lot of times I'll code it to check if the data issue exists before sending an email, so it will only send an email when the data issue exists so it won't keep spamming me if there are no issues, something like this
--The IF EXISTS checks if the data issue exists before sending an email
IF EXISTS(SELECT TOP 1 'x' FROM dbname.dbo.EmployeeData Where Active=1 and IsEmpTermed = 1)
BEGIN
EXEC msdb.dbo.sp_send_dbmail
#profile_name = '' --This is specifc to your setup
,#recipients = 'your#email.com'
,#subject = 'This is the subject'
,#query = 'select EmpID, FirstName, LastName from EmployeeData Where Active=1 and IsEmpTermed = 1' --The query
,#execute_query_database = '' --The name of your database goes here
END

How to use OPENQUERY to properly execute a Stored Procedure that updates a linked server table?

I'm having a hard time trying to figure this out. I'm using OPENQUERY to execute Stored Procedures on a linked server.
I managed to find a way to do it, by using the following:
SELECT *
FROM OPENQUERY(
[LINKEDSRV\SQLSRV],
'SET FMTONLY OFF; SET NOCOUNT ON; EXEC [Database_Name_Example].Data.usp_GetNames 5,''S''')
GO
The reason I use SET FMTONLY OFF and SET NOCOUNT ON is because when I tried the code above without them, this message appeared:
Msg 7357, Level 16, State 1, Line 1
Cannot process the object "EXEC [Database_Name_Example].Data.usp_GetNames 5,'S'". The OLE DB provider "SQLNCLI" for linked server "LINKEDSRV\SQLSRV" indicates that either the object has no columns or the current user does not have permissions on that object.
Reading a lot online helped me find the answer. This blog for example: http://www.sommarskog.se/share_data.html#OPENQUERY
So far so good, until I stomped into a Stored Procedure that use SELECT and UPDATE some tables on the linked server. When I execute the OPENQUERY I get the results from the SELECT statement but I noticed that it does not update the table.
The Stored Procedure goes like this:
CREATE PROCEDURE Data.usp_GetNames (#year tinyint=NULL, #online char(1)=NULL)
if #online = 'S'
BEGIN
select nam_codig, nam_desc from Names
where nam_status = 'P'
order by nam_date
UPDATE Names
SET nam_status = 'S'
where nam_status = 'P'
END
When I execute it directly in the linked server it updates the table, so the second time I execute it, there will be no more nam_status = 'P'
But if I execute it with OPENQUERY the results are the same, it doesn't UPDATE it just select the data.
What can I do to allow the permissions?
Thank you!!!

How to limit the rows on remote/linked server

I have a linked server that I have to fetch data from. I'm joining on a table that I expect very few rows from. The query is below, and seems to be returning all of the rows to the original server to do the sort there.
I'm looking for a way to tell the query to filter on the target machine, with a query hint or something else.
Query
INSERT INTO #DealerHierarchy(DealerId, Level)
SELECT cd.ParentId, cd.Level
FROM [dbo].[AssignedDealer] ad
JOIN [nlsdb].[nls].[dbo].[vw_parentDealers] cd ON cd.RootId = ad.DealerId
WHERE ad.UserId = #userId
AND ad.IsActive = 1
AND (#DealerId IS NULL OR ad.DealerId = #DealerId)
When I add the following line, it seems to change and only send back the needed rows
and cd.RootId = 72311
I have tried moving out the local query into a separate temp table, and then select from the view WHERE DealerId IN (select from temp table) but it still runs slowly. Adding the REMOTE hint in the JOIN also does nothing.
Query plan:
https://www.brentozar.com/pastetheplan/?id=r1iazaaFZ
Slow code executed on linked server
declare #p1 int
set #p1=7
exec sp_prepexec #p1 output,N'#P1 numeric(10)',N'SELECT "Tbl1007"."ParentId" "Col1010","Tbl1007"."Level" "Col1011" FROM "nls"."dbo"."vw_parentDealers" "Tbl1007" WHERE #P1="Tbl1007"."RootId"',72311
select #p1
Fast code executed on linked server
declare #p1 int
set #p1=10
exec sp_prepexec #p1 output,NULL,N'SELECT "Tbl1007"."ParentId" "Col1010","Tbl1007"."Level" "Col1011" FROM "nls"."dbo"."vw_parentDealers" "Tbl1007" WHERE "Tbl1007"."RootId"=(72311.)'
select #p1
You can force a specific query to be run on the remote database by using OPENQUERY. OPENQUERY doesn't accept a parameter, so you can make it dynamic by further wrapping it in EXEC.
Example
DECLARE #SearchString NVARCHAR = ...
DECLARE #OpenQueryString NVARCHAR = 'SELECT * FROM OPENQUERY(remotedb, ''' + #SearchString + ''')'
EXEC (#OpenQueryString)

Insert data into temporary table from dynamic query table output

I have the below dynamic query run in SQL Server, connecting to an OLAP server using a linked server, which returns a table as a result.
SET #nSQL = EXECUTE ('SELECT non empty {
[Coded Season].[Coded Season].[Coded Season] *
[Season].[Season].[Season] *
[Product].[Subclass].[Subclass] *
[Product].[Subclass Id].[Subclass Id]
} ON ROWS,{
[Measures].[Pl No of Range Opts]
} ON COLUMNS
FROM RP_C0') AT AS_T_RP_5900_Admin
I am executing it in SQL Server like this:
exec sp_executesql #nSQL;
It returns a table of values. Now I want to insert the data into a temporary table. I have tried the below code, but its not working.
INSERT INTO ##Subclass_Season_AS
exec sp_executesql #nSQL;
Also tried,
set #strNewQuery ='SELECT '+#nSQL+' INTO ##temptablename '
exec #strNewQuery
Could you please help on this? Thanks!
You may want to try to put the INTO statement in your dynamic query.
SET #nSQL = EXECUTE ('SELECT non empty {
[Coded Season].[Coded Season].[Coded Season] *
[Season].[Season].[Season] *
[Product].[Subclass].[Subclass] *
[Product].[Subclass Id].[Subclass Id]
} ON ROWS,{
[Measures].[Pl No of Range Opts]
} ON COLUMNS
INTO ##temptablename
FROM RP_C0') AT AS_T_RP_5900_Admin

is there any way to join sys,databases with sys.servername

I Edited my question so it can be clearer,
In sys.server there is a row that called server_id,
I wanted to know if there is any name to get the database name from this server id,
I am working with replication and linked server so i wanted to make a query that takes all the server and get database names,
I dont need all the database that on the server.
Thank you for your help.
Taking your edit into consideration and removing my previous answer, I don't have an exact answer, but some additional metadata that might help: sys.linked_logins, sp_linkedservers.
Perhaps you could just get each linked server name and use OPENQUERY to query the sys.databases catalog on the remote server?
-- this would be done in some kind of loop:
DECLARE #linkedSvrName sysname = (SELECT TOP 1 name FROM sys.servers WHERE server_id > 0);
DECLARE #sql nvarchar(4000) = 'SELECT * FROM OPENQUERY(' + #linkedSvrName + ', ''SELECT name FROM sys.databases'')';
EXEC sys.sp_executesql #sql
(I didn't have any linked server entries to test against to see if this would actually work as expected.)

Resources