SQL Server : how to declare a variable in stored procedure - sql

I need help for developing a stored procedure which has a select query joining tables from two different server.
Example:
CREATE PROCEDURE [dbo].[test proc]
DECLARE #customerid INT
SELECT OL.CUSTOMER_ID
FROM CUSTOMERS C
JOIN SERVER2.ORDER.ORDERLIST OL ON C.ID = OL.CUSTOMER_ID
WHERE OL.CUSTOMER_ID = #customerid
CUSTOMERS table is on Server1, CUSTOMER database
ORDERLIST table is on Server 2, ORDER database
They are linked servers.
This stored procedure will be in the Customer database on Server1.
Can I make server2 a variable? As I need the user to specify the server name and customerid when running the stored procedure. I need the stored procedure to be able to execute in production and test environment. Or how should I do it?

You need to use dynamic SQL:
create PROCEDURE [dbo].[test proc] (
#customerid int,
#server2 sysname -- or you can use nvarchar(255)
)
begin
declare #sql nvarchar(max);
set #sql = '
SELECT OL.CUSTOMER_ID
FROM CUSTOMERS C JOIN
#SERVER2.ORDER.ORDERLIST OL
ON C.ID = OL.CUSTOMER_ID
WHERE OL.CUSTOMER_ID = #customerid';
set #sql = replace(#sql '#SERVER2', #server2);
exec sp_executesql #sql,
N'#customerid int',
#customerid=#customerid;
end;
sp_executesql allows you to replace constant values in the dynamic SQL. However, you are not permitted to change identifiers, such as server names, which is why this uses replace().

Related

SQL - Dynamically selecting information into a variable from different databases

i have 3 databases, db 1 and db 2 are for jobs and db 3 is for workflows.
both db 1 and 2 have a process to create workflows directly into db 3.
i have a trigger in db 3 that activates when there is a workflow creation because i need to add more information into the workflow from the jobs.
so what i did was create a variable that i set with a normal select from db 1 (select job from db1.dbo.job)
i want to know if there is a way to do the select statement dynamically
for example:
if db1 then #db = db1
if db2 then #db = db2
then in the #variable = select job from #db
right now what i have is the following query but i am simple adding all my logic to another IF and then changing the selects statements... i dont want to do that becuase i will be adding more data bases to integrate with db3
Declare
#flag int,
#variable varchar(10)
set #flag = 1
if #flag = 1
begin
set #variable = (select job from db1.dbo.job)
end
else if #flag = 2
begin
set #variable = (select job from db2.dbo.job)
end
update db3 set job = #variable
is it even possible to do what im trying to do????
You need dynamic SQL for this.
Note:
Always use QUOTENAME to correctly escape names
Use sp_executesql to pass data in parameters all the way through. You can use OUTPUT parameters too
Declare
#flag int
set #flag = 1
DECLARE #sql nvarchar(max) = N'
UPDATE db3
SET job = (
SELECT TOP 1 job
FROM ' + QUOTENAME(CASE WHEN #flag = 1 THEN 'db1' ELSE 'db2' END) + '.dbo.job
);
';
EXEC sp_executesql #sql;
This kind of thing is possible using techniques like dynamic sql and sp_executesql, or by creating synonyms on the fly.
HOWEVER, you have a more fundamental issue with the approach.
When a trigger is called, you cannot make the assumption that only a single row will be in the inserted table, because it is possible to insert multiple rows into a table all at once.
To demonstrate the problem, here is a "solution" to your problem which "works" if only one row is inserted into table T. But think about what would happen if more than one row was inserted. What would the "correct" flag value be if multiple rows are inserted with different flag values? How can we update the row in T based on the value of the #id variable if multiple rows are affected?
You could use this pattern and iterate over the rows in the inserted table using a cursor, but it would be a bit ugly.
create table T(id int, flag int, extraData nvarchar(128));
create or alter trigger T_ins on T for insert as
begin
declare #sql nvarchar(max) = 'update T set extraData = (select top 1 name from {otherDB}.sys.tables) where id = #id'
declare #db sysname, #id int;
-- this select doesn't make any sense if more than one row exists in the inserted table!
select #id = id,
#db = iif(inserted.flag = 1, 'master', 'msdb')
from inserted;
set #sql = replace(#sql, '{otherDB}', #db);
exec sp_executesql #sql, N'#id int', #id;
end
A way to do it without a cursor is to left join onto all of the possible sources of additional data in the other databases, including the mutually exclusive flag value in the join condition, and coalesce the columns into the update statement. I think the code is also much cleaner:
create or alter trigger jobs_ins on jobs for insert as
begin
update j
set extraData = coalesce(j1.somedata, j2.somedata)
from jobs j
join inserted ins on ins.id = j.id
left join otherDB1.dbo.jobdata j1 on i.flag = 1 and j1.id = ins.id
left join otherDB2.dbo.jobdata j2 on i.flag = 2 and j2.id = ins.id;
end

Stored procedure not return data while where condition as parameter

ALTER PROCEDURE procGetData
#companyid INT,
#condition NVARCHAR(MAX)
AS
BEGIN
DECLARE #cond NVARCHAR
DECLARE #sql NVARCHAR
SELECT #sql = 'select e.id, e.code, e.firstname + isnull('' ''+e.lastname,'''') [name],
from hrm_employee e
inner join (select max(id) [id], hrm_employeeid from hrm_employeeservice group by hrm_employeeid) ser on ser.hrm_employeeid = e.id
inner join hrm_gender g on e.genderid = g.id and g.companyid = '+CONVERT(NVARCHAR,#companyid)+'inner join hrm_maritalstatus m on e.maritalstatusid=m.id and m.companyid='+CONVERT(NVARCHAR,#companyid)+'
where '+#cond
EXEC (#sql)
END
The selected values(id,code,name) are not get while exec this stored procedure from C# using sqlAdapter
#cond variable is declared but never assigned and this means that the supplied parameter "#condition" is not used in the stored procedure at all.
It is bad bad practice (SQL Injection) to have where clause passed in as parameter. I completely agree with Larnu & marc_s on the coding practice.

Converting a Static SQL Query into Dynamic SQL on a Stored Procedure

I have written a Stored Procedure like this,
Alter PROCEDURE sp_bestReviewer_report
(
#StartingLetter AS VARCHAR(1),
#CountsReq AS DECIMAL(3),
#ReqRev AS INT
#RequiredColumn AS SYSNAME
)
AS
BEGIN
SELECT TOP(#ReqRev) #RequiredColumn, Count(Reviewer) AS Total FROM [reviews_not_sent] WHERE LEFT(Reviewer,1) = #StartingLetter
GROUP BY (Reviewer)
HAVING (Count(Reviewer) > #CountsReq OR Count(Reviewer) < #CountsReq)
ORDER BY
Total DESC;
END;
I am trying to pass #RequiredColumn (the required column) dynamically as a parameter to the stored procedure.
How Can I do this.
You need dynamic SQL to pass in a column name:
BEGIN
DECLARE #sql NVARCHAR(MAX);
SET #sql = '
SELECT TOP (#ReqRev) [RequiredColumn], Count(Reviewer) AS Total
FROM [reviews_not_sent]
WHERE LEFT(Reviewer, 1) = #StartingLetter
GROUP BY Reviewer
HAVING Count(Reviewer) <> #CountsReq
ORDER BY Total DESC
';
SET #sql = REPLACE(#sql, '[RequiredColumn]', QUOTENAME(#RequiredColumn));
EXEC sp_executesql #sql,
N'#ReqRev int, #StartingLetter VARCHAR(1), #CountsReq DECIMAL(3)',
#ReqRev=#ReqRev, #StartingLetter=#StartingLetter, #CountsReq=#CountsReq;
END;

Stored procedure with a parameter in FROM clause

Is it possible to create a stored procedure that uses a parameter in the FROM clause?
For example:
CREATE PROCEDURE [dbo].[GetMaxId]
#id varchar(50)
#table varchar(50)
AS
BEGIN
SELECT MAX(#id)
FROM #table
END
You cannot pass identifiers as parameters into a query (neither table names nor column names). The solution is to use dynamic SQL. Your syntax suggests SQL Server, so this would look like:
CREATE PROCEDURE [dbo].[GetMaxId] (
#id varchar(50)
#table varchar(50)
)
AS
BEGIN
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'SELECT MAX(#id) FROM #table';
SET #sql = REPLACE(REPLACE(#sql, '#id', QUOTENAME(#id)), '#table', QUOTENAME(#table));
EXEC sp_executesql #sql;
END; -- GetMaxId

Selecting a database from a variable

So I have two databases that have no relationship between them. The first one is where my dbo.Clients exists and has a column of the database name of the second db . My thought was to select the dbName from the Clients then use that variable to select data from the second database.
The query doesnt run can some one shed a little light? Thanks.
#dbName varchar(50) OUTPUT,
#clientID varchar(50)
AS
BEGIN
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT * FROM sql02.iproconfig4.dbo.Clients
SET #dbName = (SELECT Clients.ClientDatabase FROM sql02.iproconfig4.dbo.Clients WHERE ClientID = #clientID)
SELECT * FROM sql02.#dbName.dbo.Discovery
END
You will need to use dynamic SQL to accomplish this:
DECLARE #sql nvarchar(max)
SET #sql = 'SELECT * FROM sql02.' + #dbName + '.dbo.Discovery'
EXEC sp_executesql #sql