SQL Agent Job Modification Minimum Permission Requirement - sql-server-2016

I need permission to modify other users job in sql server 2016 in dev environment but DBA team is saying they can't give me sysadmin role. So is their any other option present without sysadmin so that I can modify jobs created by other users.

Directly, no. But you can ask your sysadmins to create a wrapper stored procedure, which calls msdb.dbo.sp_update_job, has the same parameters and executes as privileged user. And then to give you, or SQLAgentOperatorRole (if everybody should be able to modify not only his jobs, but all) rights to execute it. In the example below, the stored procedure is executed by owner. It assumes its owner will be sysadmin. If needed, you can change this and for example specify particular login instead.
USE msdb
GO
CREATE PROC [dbo].[sp_update_job_for_non_admins]
#job_id UNIQUEIDENTIFIER = NULL,
#job_name sysname = NULL,
#new_name sysname = NULL,
#enabled TINYINT = NULL,
#description NVARCHAR(512) = NULL,
#start_step_id INT = NULL,
#category_name sysname = NULL,
#owner_login_name sysname = NULL,
#notify_level_eventlog INT = NULL,
#notify_level_email INT = NULL,
#notify_level_netsend INT = NULL,
#notify_level_page INT = NULL,
#notify_email_operator_name sysname = NULL,
#notify_netsend_operator_name sysname = NULL,
#notify_page_operator_name sysname = NULL,
#delete_level INT = NULL,
#automatic_post BIT = 1
WITH EXECUTE AS OWNER
AS
BEGIN
EXEC dbo.sp_update_job
#job_id
,#job_name
,#new_name
,#enabled
,#description
,#start_step_id
,#category_name
,#owner_login_name
,#notify_level_eventlog
,#notify_level_email
,#notify_level_netsend
,#notify_level_page
,#notify_email_operator_name
,#notify_netsend_operator_name
,#notify_page_operator_name
,#delete_level
,#automatic_post
END
GO
GRANT EXECUTE ON [dbo].[sp_update_job_for_non_admins] TO [SQLAgentOperatorRole]
The credits for this solution are for Boris Hristov.

Related

Create SQL Server table name that includes a variable value

I'm creating a SQL Server table via a trigger, and I want the table name to be specific each time.
For the end result, I want the table name to be tblTEMP_INA_DATA_12345.
I could obviously, just type tblTEMP_INA_DATA_12345, but the #PlanID value will be different each time.
How could I modify the create table statement to do what I want? Is this possible?
I have searched, but I'm not sure what search terms to even use. I appreciate any and all responses even if the answer is no.
DECLARE #PlanID varchar(80)
SET #PlanID = 12345
CREATE TABLE [dbo].[tblTEMP_INA_DATA_]
(
[strQuestion] [varchar](max) NULL,
[strAnswer] [varchar](max) NULL
) ON [PRIMARY]
You can use dynamic sql to do this. Like below
Declare #PlanID varchar(80),#sql nvarchar(max);
Set #PlanID = 123456
set #sql= 'Create TABLE [dbo].' + QUOTENAME('tblTEMP_INA_DATA_' + #PlanID) + '
([strQuestion] [varchar](max) NULL,
[strAnswer] [varchar](max) NULL
) ON [PRIMARY]'
exec (#sql);

How to Pass API Response to Azure SQL Table via Stored Procedure

I have a Azure Data Factory pipeline that uploads data to Salesforce and then gets the response back to Azure Data Factory. I am trying to get the response from the failed job and store in my failed records table in Azure SQL. I am using Stored Procedure activity to pass on the values to the table. The issue I am having is that the stored Procedure activity is getting the correct input for response but when it passes the value to SQL it just insert ".
I previously had the failed_records_details column set as Varchar(max) and then tried to change it to NText and see if SQL would accept the string value passes on from the API response. But I haven't had any luck so far. Any help would be really appreciated.
Update
OP changed his table structure to varchar(max) and it worked.
After many tests, I found the answer. I used OPENJSON to process in stored procedure.
This is my table:
CREATE TABLE [dbo].[product](
[PRODUCT_ID] [int] NULL,
[PRODUCT_TYPE] [int] NULL,
[PRODUCT_NAME] [varchar](50) NULL,
[PRODUCT_TITLE] [varchar](255) NULL,
[PRODUCT_PIC] [varchar](255) NULL,
[CREATE_TIME] [datetime] NULL,
[UPDATE_TIME] [datetime] NULL,
[PRODUCT_INTRO] [text] NULL,
[PRODUCT_FEATURE_ID] [varchar](255) NULL,
[PRODUCT_PARAM] [varchar](255) NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
This is my API response:
{
"productId": 2,
"productTypeId": 2,
"productName": "AD2",
"productTitle": "TopAir AD215-1000A",
"productPic": "/img/productPic/1593684705859user.jpg",
"createTime": "2020-06-05 09:17:31",
"updateTime": "2020-06-05 09:17:31",
"productFeatureId": "",
"productParam": "/img/productPic/param/1593685548627a32415ca9a21f6f9a1d99b2731f224b5d319c424.jpg",
"productIntro": "",
"productType": null,
...
This is my stored procedure, the api request as a string type input parameter.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[uspProduct] #product NVARCHAR(MAX)
AS
BEGIN TRY
INSERT INTO dbo.product(PRODUCT_ID,PRODUCT_TYPE,PRODUCT_NAME,PRODUCT_TITLE,PRODUCT_PIC,CREATE_TIME,UPDATE_TIME,PRODUCT_INTRO,PRODUCT_FEATURE_ID,PRODUCT_PARAM)
SELECT * FROM OPENJSON(#product)
WITH(
PRODUCT_ID int '$.productId',
PRODUCT_TYPE int '$.productTypeId',
PRODUCT_NAME varchar(50) '$.productName',
PRODUCT_TITLE varchar(255) '$.productTitle',
PRODUCT_PIC varchar(255) '$.productPic',
CREATE_TIME datetime '$.createTime',
UPDATE_TIME datetime '$.updateTime',
PRODUCT_INTRO varchar(255) '$.productIntro',
PRODUCT_FEATURE_ID varchar(255) '$.productFeatureId',
PRODUCT_PARAM varchar(255) '$.productParam'
)
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE ( )
END CATCH
;
Add dynamic content #string(activity('Web1').output). After repeated testing, here we must convert API response from object type to string type.
This is the input of the Stored procedure1 activity.
The debugging results are as follows:
I can see the API response was inserted into the table.

Using ExecuteSqlCommand to create stored procedures from text file

I'm trying to execute an sql text file to create stored procedures on an sql server db. I'm also using this method to create user defined table types, which the stored procedure(s) will use.
The creation of the table types works perfectly. However, when I go to create the stored procedure I'm getting the error,
'CREATE/ALTER PROCEDURE' must be the first statement in a query batch.
Here is the code which reads the file and executes it against the db:
public static void LoadStoredProcedures()
{
const string procedureLocation = "C:\\StoredProcedures.txt";
var reader = File.ReadAllText(procedureLocation);
var context = new prismEntities();
context.Database.ExecuteSqlCommand(reader);
}
public static void CreateTables()
{
const string tableLocation = "C:\\CreateTables.txt";
var reader = File.ReadAllText(tableLocation);
var context = new prismEntities();
context.Database.ExecuteSqlCommand(reader);
}
an example of the user defined table:
if not exists (select * from sys.table_types
where name like 'TextbookTable')
create type [dbo].[TextbookTable] as table (
[TXUID] [int] NOT NULL,
[SKU] [int] NOT NULL,
[UsedSKU] [int] NOT NULL,
[BindingID] [int] NOT NULL,
[TextStatusID] [int] NOT NULL,
[StatusDate] [datetime] NULL,
[Author] [char](45) NOT NULL,
[Title] [char](80) NOT NULL,
[ISBN] [char](30) NULL,
[Imprint] [char](10) NULL,
[Edition] [char](2) NULL,
[Copyright] [char](2) NULL,
[Type] [char](10) NULL,
[Bookkey] [varchar](10) NULL,
[Weight] [decimal](10, 4) NULL,
[ImageURL] [char](128) NULL,
primary key clustered
(
[TXUID] ASC
) with (ignore_dup_key = on)
)
an example of the stored procedure I'm attempting to create :
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[AddTextbook]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[AddTextbook]
create procedure [dbo].[AddTextbook]
(
#textbook TextbookTable readonly
)
as
begin
set nocount on;
set identity_insert textbook on
begin try
merge Textbook txt
using (select * from #textbook) as source
on txt.TXUID = source.TXUID
when not matched then
insert (TXUID, SKU, UsedSKU, BindingID, TextStatusID, StatusDate, Author,
Title, ISBN, Imprint, Edition, Copyright, Type, Bookkey, Weight, ImageURL)
values ( source.TXUID, source.SKU, source.UsedSKU, source.BindingID, source.TextStatusID,
source.StatusDate, source.Author, source.Title, source.ISBN,
source.Imprint, source.Edition,
source.Copyright, source.Type, source.Bookkey, source.Weight, source.ImageURL);
set identity_insert textbook off
end try
begin catch
declare #message varchar(128) = error_message()
select
ERROR_NUMBER() as ErrorNumber,
ERROR_SEVERITY() as ErrorSeverity,
ERROR_STATE() as ErrorState,
ERROR_PROCEDURE() as ErrorProcedure,
ERROR_LINE() as ErrorLine,
ERROR_MESSAGE() as ErrorMessage;
raiserror(#message, 16, 10)
end catch
end
grant execute on [dbo].[AddTextbook] to [public]
Now, the order of the calls, is the CreateTables is called first then the LoadStoredProcedures. The tables get created with no problems. The stored procedures do not get created and generate the above mentioned error. I have removed the 'if exists...' line and the stored procedure will get created, however, if there are others that I'm trying to create in the same file, they will error out and not get created. I want to be able to manage this with one file, not multiple ones for each stored procedure.
Does anyone know a work around for this? Hopefully I have provided ample information. Thanks in advance.
Basically you're missing a bunch of GO statements between command such as:
You have to change
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[AddTextbook]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[AddTextbook]
create procedure [dbo].[AddTextbook]
To be
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[AddTextbook]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[AddTextbook]
GO
create procedure [dbo].[AddTextbook]

What is a better way to write this SQL Stored Procedure?

I am updating a Trades Transactions Log using a SQL Stored Procedure, and I am updating Current Order Table at the same time with the same sproc.
Because I had a serious problem where the Log table did not update and the Current Order table did... I added a (3rd) routine to the bottom which checks to see if the Log Table was updated referencing an ID (ClientID), then entering an error if not present to error table.
I am asking... how badly written is this sproc ?? Help or advice appreciated.
ALTER PROCEDURE dbo.sprocVT4_addTradeLong
#seqno varchar(35) = NULL,
#exctyp varchar(35) = NULL,
#ordstat varchar(35) = NULL,
#clid varchar(35) = NULL,
#exid varchar(35) = NULL,
#type varchar(35) = NULL,
#side varchar(35) = NULL,
#exch varchar(35) = NULL,
#sym varchar(35) = NULL,
#lstqty varchar(35) = NULL,
#lstpri varchar(35) = NULL,
#text varchar(35) = NULL,
#cumqty varchar(35) = NULL,
#lftqty varchar(35) = NULL,
#now varchar(35) = NULL
AS
BEGIN
-- NO EXISTS ------------
Declare #RC int
SELECT [Symbol] FROM TradesLongForex T WHERE T.ExecId = #exid
SELECT #RC = ##ROWCOUNT
IF #RC <= 0
INSERT INTO TradesLongForex ([SeqNo], [ExecType], [Status], [ClientId], [ExecId], [Type], [Side], [Exchange], [Symbol], [LastQty], [LastPrice], [Text], [CummQty], [LeftQty], [Date])
VALUES (#seqno, #exctyp, #ordstat, #clid, #exid, #type, #side, #exch, #sym, #lstqty, #lstpri, #text, #cumqty, #lftqty, #now)
UPDATE OrdersIdHoldForex SET [OrdExcType] = #exctyp, [OrdStatus] = #ordstat, [OrdType] = #type, [OrdSide] = #side, [OrdPrice] = #lstpri, [OrdQty] = #cumqty, [OrdRemain] = #lftqty
WHERE [Ticker] = #sym
DECLARE #RC2 int
SELECT #RC2 = ##ROWCOUNT
SELECT [ClientId] FROM TradesLongForex WHERE [ClientId] = #clid
if #RC2 <=0
INSERT INTO ERRLOG ([Date], [Message])
VALUES (GETDATE(), 'ERROR INSERTING TRADESLONGFOREX CLID = ' + CONVERT(varchar(10),#CLID))
END
Phil makes a good point about transactions. This concept is called "Atomicity" and basically means each transaction/process is atomic and self contained.
The general syntax for transactions in SQL server would be something like:
BEGIN TRY
BEGIN TRANSACTION
...
your code here
...
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 1 ROLLBACK
... error reporting code ...
END CATCH
The gist of this is, use TRY/CATCH blocks to trap the errors, and only commit the transaction if you get through the whole TRY block without issues. Any errors send you to the CATCH block, which rolls back the open transaction.
Here's a primer on error handling.
I'm not 100% sure what you are asking, but it seems that you need to read up a bit on database transactions. Essentially you can wrap the set of queries in a transaction, and it will ensure that either all of the operations are completed, or none of them are. So if an error occurs, the entire operation will be rolled back.
http://en.wikipedia.org/wiki/Database_transaction

Mirroring Table Modifications

I have a set of tables that are used to track bills. These tables are loaded from an SSIS process that runs weekly.
I am in the process of creating a second set of tables to track adjustments to the bills that are made via the web. Some of our clients hand key their bills and all of those entries need to be backed up on a more regular schedule (the SSIS fed data can always be imported again so it isn't backed up).
Is there a best practice for this type of behavior? I'm looking at implementing a DDL trigger that will parse the ALTER TABLE call and change the table being called. This is somewhat painful, and I'm curious if there is a better way.
I personally would have the SSIS-fed tables in one database (set to simple recovery mode) and the other tables in a separate database on the same server which is set to full recovery mode,. Then I would set up backups on the second datbase on a regular schedule. A typical backup schedule would be full backup once a week, differntials nightly and transaction backups every 15-30 minutes depending on how much data is being input.) Be sure to periodically test recovering the backups, learning how to do that when the customer is screaming becasue the datbase is down isn;t a good thing.
I ended up using a DDL trigger to make a copy of changes from one table to the other. The only problem is that if a table or column name contains part of a reserved word - ARCH for VARCHAR - it will cause problems with the modification script.
Thanks, once again, to Brent Ozar for error checking my thoughts before I blogged them.
-- Create pvt and pvtWeb as test tables
CREATE TABLE [dbo].[pvt](
[VendorID] [int] NULL,
[Emp1] [int] NULL,
[Emp2] [int] NULL,
[Emp3] [int] NULL,
[Emp4] [int] NULL,
[Emp5] [int] NULL
) ON [PRIMARY];
GO
CREATE TABLE [dbo].[pvtWeb](
[VendorID] [int] NULL,
[Emp1] [int] NULL,
[Emp2] [int] NULL,
[Emp3] [int] NULL,
[Emp4] [int] NULL,
[Emp5] [int] NULL
) ON [PRIMARY];
GO
IF EXISTS(SELECT * FROM sys.triggers WHERE name = ‘ddl_trigger_pvt_alter’)
DROP TRIGGER ddl_trigger_pvt_alter ON DATABASE;
GO
-- Create a trigger that will trap ALTER TABLE events
CREATE TRIGGER ddl_trigger_pvt_alter
ON DATABASE
FOR ALTER_TABLE
AS
DECLARE #data XML;
DECLARE #tableName NVARCHAR(255);
DECLARE #newTableName NVARCHAR(255);
DECLARE #sql NVARCHAR(MAX);
SET #sql = ”;
-- Store the event in an XML variable
SET #data = EVENTDATA();
-- Get the name of the table that is being modified
SELECT #tableName = #data.value(‘(/EVENT_INSTANCE/ObjectName)[1]‘, ‘NVARCHAR(255)’);
-- Get the actual SQL that was executed
SELECT #sql = #data.value(‘(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]‘, ‘NVARCHAR(MAX)’);
-- Figure out the name of the new table
SET #newTableName = #tableName + ‘Web’;
-- Replace the original table name with the new table name
-- str_replace is from Robyn Page and Phil Factor’s delighful post on
-- string arrays in SQL. The other posts on string functions are indispensible
-- to handling string input
--
-- http://www.simple-talk.com/sql/t-sql-programming/tsql-string-array-workbench/
-- http://www.simple-talk.com/sql/t-sql-programming/sql-string-user-function-workbench-part-1/
--http://www.simple-talk.com/sql/t-sql-programming/sql-string-user-function-workbench-part-2/
SET #sql = dbo.str_replace(#tableName, #newTableName, #sql);
-- Debug the SQL if needed.
--PRINT #sql;
IF OBJECT_ID(#newTableName, N’U’) IS NOT NULL
BEGIN
BEGIN TRY
-- Now that the table name has been changed, execute the new SQL
EXEC sp_executesql #sql;
END TRY
BEGIN CATCH
-- Rollback any existing transactions and report the full nasty
-- error back to the user.
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION;
DECLARE
#ERROR_SEVERITY INT,
#ERROR_STATE INT,
#ERROR_NUMBER INT,
#ERROR_LINE INT,
#ERROR_MESSAGE NVARCHAR(4000);
SELECT
#ERROR_SEVERITY = ERROR_SEVERITY(),
#ERROR_STATE = ERROR_STATE(),
#ERROR_NUMBER = ERROR_NUMBER(),
#ERROR_LINE = ERROR_LINE(),
#ERROR_MESSAGE = ERROR_MESSAGE();
RAISERROR(‘Msg %d, Line %d, :%s’,
#ERROR_SEVERITY,
#ERROR_STATE,
#ERROR_NUMBER,
#ERROR_LINE,
#ERROR_MESSAGE);
END CATCH
END
GO
ALTER TABLE pvt
ADD test INT NULL;
GO
EXEC sp_help pvt;
GO
ALTER TABLE pvt
DROP COLUMN test;
GO
EXEC sp_help pvt;
GO