I am writing a stored procedure which will compare two similar tables under two different database. Here I am using the keyword USE [dbname].
DECLARE
--INPUT
#BASE_DATABASE_NAME NVARCHAR(50),
#TARGET_DATABASE_NAME NVARCHAR(50),
#TARGET_PRODUCT_ID NVARCHAR(50),
#TARGET_PRODUCT_CODE NVARCHAR(50)
SET #BASE_DATABASE_NAME='USE [DB1]'
SET #TARGET_DATABASE_NAME='USE [DB2]'
SET #TARGET_PRODUCT_ID=4194
SET #TARGET_PRODUCT_CODE = #BASE_DATABASE_NAME ( SELECT PRODUCT_CODE FROM T_PRODUCT_MST WHERE PROD_ID = #TARGET_PRODUCT_ID)
print #TARGET_PRODUCT_CODE.
Error--
It's not working...
Can anybody help me out with this? I need to pass the database name dynamically to the sql query.
Thanks in advance..
You cannot use USE this way. USE sets the database against which all the statements are executed and cannot be used inside another query.
You can use dynamic SQL though to specify your query:
DECLARE
--INPUT
#BASE_DATABASE_NAME NVARCHAR(50),
#TARGET_PRODUCT_ID INT,
#TARGET_PRODUCT_CODE NVARCHAR(50)
SET #BASE_DATABASE_NAME='[DB1]'
SET #TARGET_PRODUCT_ID=1
DECLARE #SQL NVARCHAR(MAX) = N'SELECT #TARGET_PRODUCT_CODE = PRODUCT_CODE FROM '
+ #BASE_DATABASE_NAME
+ N'..T_PRODUCT_MST WHERE PROD_ID = #TARGET_PRODUCT_ID'
exec sp_executesql #SQL, N'#TARGET_PRODUCT_ID INT, #TARGET_PRODUCT_CODE NVARCHAR(50) OUTPUT',
#TARGET_PRODUCT_ID, #TARGET_PRODUCT_CODE OUTPUT
print #TARGET_PRODUCT_CODE
Another option to Szymon's answer is to use synonyms. First create your synonyms in the DB:
CREATE SYNONYM [dbo].[TargetProductCode] FOR [DB2].[dbo].[T_Product_MST]
And then your sql syntax becomes:
SET #TARGET_PRODUCT_CODE = SELECT PRODUCT_CODE
FROM dbo.TargetProductCod WHERE PROD_ID = #TARGET_PRODUCT_ID
If this doesn't need to be dynamic, this can be a good solution, and can also make for easier testing, if for some reason you need to point a test DB to a different target (can just update the synonym).
Related
I have a bunch of simple expressions, such as:
c=a+b
c=a*b
...
I would like to pass them as parameter to a stored procedure, which is going to perform an update using them.
CREATE TABLE t(
a int,
b int,
c int
);
INSERT INTO t VALUES (1,2,3),(4,5,6);
CREATE PROCEDURE sp #left_member varchar(50), #right_member
AS
BEGIN
UPDATE t
SET #left_member = #right_member
END
EXEC sp 'c', 'a+b'
EXEC sp 'c', 'a*b'
Is there a way of doing something like that ? I would like to possibly avoid dynamic SQL. In my target design, the expressions will be stored in their own table (editable online).
I generally don't recommend doing this, but dynamic SQL is pretty much the solution:
CREATE PROCEDURE usp_exec_dangerous_update (
#left_member nvarchar(50),
#right_member nvarchar(50)
)
AS
BEGIN
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'
UPDATE t
SET [left_member] = [right_member]
';
SET #sql = REPLACE(REPLACE(#sql, '[left_member]', #left_member), '[right_member]', #right_member);
EXEC sp_executesql #sql;
END;
Although such code can be useful in a thoughtful, well-designed system, in general it is not needed:
It exposes the system to SQL injection attacks. Running "generic" code is just dangerous.
It does not handle errors, which are easy to occur with this method.
Assume I have the following SQL snippet in SQL Server 2012:
DECLARE #fname varchar(20), #strVarName varchar(50)
SET #fname = 'cronus'
SET #strVarName = COVERT_VARIABLE_TO_STRING_NAME ( #fname)
--this should return '#fname'. this is not a value conversion this is converting a variable name to a string name
SELECT #strVarName
How do I do this?
SQL Server does not support reflection. You may be able to retrieve column or table names from its catalog views but with variables you're out of luck. Maybe you'll find another way to solve this issue with dynamic SQL.
Use dynamic sql query
DECLARE #fname varchar(20), #sql varchar(MAX)
SET #fname = 'cronus'
SET #sql = 'SELECT ' + #fname
EXEC (#sql)
There are following Character data types used to store character strings:
char,
varchar,
nvarchar,
text,
If u already used variable as String then why need to convert as a string
DECLARE #fname varchar(20), #strVarName varchar(50)
SET #fname = 'cronus'
SET #strVarName = #fname
SELECT #strVarName
if needed use CAST and CONVERT function
This is such a bizarre question, sounds like something I'd try to do.
Hmm, SQL is not supposed to do this but I guess, it doesn't mean you can't make it.
I think you would effectively have to write your own process to pull this off, something along the lines of:
Create dbo.sProcInserts stored procedure to insert values into a table:
Takes VariableName, Value and possibly table name to insert into as parameters
Create dbo.sProcExec stored procedure to execute stored procedure:
Before execute, read stored procedure into a variable
Find all variables that are SET (i.e. they have a SET #Var = OR SELECT #Var =)
After each variable set, add to your string a line that calls dbo.sProcInserts with the name of the variable and a select #Variable
Execute your newly written stored procedure
That way you don't have to actually make any modifications to your sProcs and it should catch the flow of variables and their changes through your procedure
However the requirement itself is a bit strange for me, but here is a way that could be a good start point for you:
declare #var1 int
Set #var1= 1
--some code here
declare #var2 nvarchar(max)
set #var2 = 10
--some other code here
declare #var3 bit
print ##VERSION
print 'this is fake #value inside a string'
--$ This is a Hint to help me find the Query that should parsed
declare #sql varbinary(max)
select #sql=sql_handle
from sys.sysprocesses
where spid=56
declare #q nvarchar(max)
select #q= substring(text,1,charindex('$',text)-3) from sys.dm_exec_sql_text(#sql)
Select distinct rtrim(ltrim(substring(Name,1,charindex(' ',Name)))) as Name from(
Select substring(replace(Name,'=',' '),8, Len(Name)) as Name from dbo.SplitString(#q,'declare ')
) as K
where Name like '#[^#]%'
By running the above query you will get the variables name.
Output:
#var1
#var2
#var3
You can find the source code for SplitString function Here
Note: If you are using SQL Server 2016 and your database's compatibility level is equal or greater than 130, you can also use SPLIT_STRING introduced by Microsoft it self. Learn more Here
Thank you for helping me out through my previous tides.. I am currently working on SQL Server 2008 for one of my project, a part of which needs to use 22 columns for a set of similar operations.
The column names only differ by the number, e.g.
C1_1,C1_2,c1_3
Is there any way to loop through the column names? I tried out the following code, but it throws out an error
DECLARE #i INT
SET #i=2
while (#i<=22)
begin
SELECT [DEF].[CONCATENATE], SUM(DEF.[C1_+#i+_PREV]) as
[C1_#i_prev]
INTO #TMP_C1_#i_CONCATENATE_PREV
FROM DEF
GROUP BY DEF.[CONCATENATE]
SELECT [ABC].[CONCATENATE], SUM(ABC.[C1_#i_CURR]) as
[c1_#i_curr]
INTO #TMP_C1_#i_CONCATENATE_CURR
FROM ABC
GROUP BY ABC.[CONCATENATE]
UPDATE #tmp_var_concatenate_c1_#i
SET [Amount] = #TMP_C1_#i_CONCATENATE_PREV.[C1_#i_PREV]
FROM #tmp_var_concatenate_c1_#i
INNER JOIN
#TMP_C1_#i_CONCATENATE_PREV ON
#tmp_var_concatenate_c1_#i.[CONCATENATE] = #TMP_C1_#i_CONCATENATE_PREV.
[CONCATENATE]
Please forgive my ignorance, if I am doing something idiotic.
Thanks
This is part of the code in which the code is burping.
alter table #tmp_var_concatenate_C1_'+cast(#i as varchar)+'
add [ColA] varchar(255),
[ColB] Varchar(255),
[ColC] Varchar(255),
[ColD] VARCHAR(50),
[ColE] MONEY,
[ColF] MONEY
Is it because of the #tables that I am using ?? but, ideally, it shouldnt be an issue whether am using a Temp table or a reg. one..
You can use dynamic sql:
DECLARE #SQL varchar(max), #i INT
SET #i=2
while (#i<=22)
begin
/* Then cover all calculations with this one: */
SET #SQL='SELECT [DEF].[CONCATENATE], SUM(DEF.[C1_'+cast(#i as varchar)+'_PREV]) as
[C1_'+cast(#i as varchar)+'_prev]
INTO #TMP_C1_'+cast(#i as varchar)+'_CONCATENATE_PREV
FROM DEF
GROUP BY DEF.[CONCATENATE]
/* and all your code with the same trick in #i to the END */
'
--PRINT (#SQL) -- print it before use to see the result script
EXEC (#SQL)
/* Than do your iterations etc. */
set #i+=1
end
And don't forget to substitute all ' inside #SQL with ''.
Also you need to do all manipulations with temp tables inside #SQL, if you want to do final update outside the dynamic sql, just make tables real and then delete them.
[UPDATE]
As far as you faced with problem of altering temp tables, I tried to reproduce this error, but nothing happens, everything works fine. Please use this code as an example.
declare #sql varchar(max),#i int
set #i=2
while #i<=22
begin
set #sql='
select ID,Code into #TMP_C1_'+cast(#i as varchar)+'_CONCATENATE_PREV from (select 0 as ID, ''a'' as Code) t1
alter table #TMP_C1_'+cast(#i as varchar)+'_CONCATENATE_PREV add [Col1] varchar(255), [Col2] Varchar(255), [Col3] Money
select * from #TMP_C1_'+cast(#i as varchar)+'_CONCATENATE_PREV'
--print (#sql)
exec (#sql)
set #i+=1
end
First, I create temp table with dynamic name. Second, add new columns. The last is successful verifying. Did you execute all creations/alters in the same #sql-batch? If no, this won't work, because this tables are available only inside this batch (that's why we used varchar(max) when declared). Please describe your actions in details, maybe there is a mistake somewhere.
I'm currently having trouble writing a stored procedure and setting the value of a variable of type int to the results of a select statement with a variable as the tablename. I've looked at old threads and tried multiple methods, but no luck. If I'm not getting an error regarding the tablename, I end up getting an error with a variable conversion issue. I've been working on this for too long and any help would be appreciated. Below is a portion of my code. Thanks
DECLARE #BATCHNUMBER VARCHAR --value set in earlier code
DECLARE #ETABLE VARCHAR(50); --the table name
DECLARE #FIRSTDOCID INT;
SET #ETABLE = 'tablename_' + #BATCHNUMBER; --CREATE FIRST TABLE NAME
SELECT #FIRSTDOCID = MIN(D0CID) FROM #ETABLE
The error I get is: Must declare the table variable "#ETABLE"
You are trying to select from a VARCHAR, not a table. The only way to make this work is by using Dynamic SQL.
DECLARE #SQL NVARCHAR(250);
SET #SQL = 'SELECT #OUTPUT = MIN(D0CID) FROM ' + QuoteName(#ETABLE);
EXEC sp_executeSql #SQL, N'#output INT OUTPUT', #FIRSTDOCID OUTPUT;
SELECT #FIRSTDOCID;
However, I would not suggest using Dynamic SQL as this often leads to SQL injection.
You'll probably have to do something like use exec if you're dynamically building the query:
SET #QUERY = "SELECT" + ...etc.
exec(#QUERY)
Since ETABLE is a varchar, and not, as expected, a 'table variable'.
I need to retrieve change-data-capture rows for several tables, and I'm required (by company IT policy) to access the database via stored procedures. I would rather create a single stored procedure with the table name as a parameter, rather than one stored procedure for each table I'm monitoring. Where I get hung up is that CDC defines a separate table-valued function name for each table monitored, and I'm not sure how best to generalize around that.
Is it possible to modify the following example code so that it invokes cdc.fn_cdc_get_net_changes_dbo_ + #Table instead of cdc.fn_cdc_get_net_changes_dbo_TABLE ?
Is there another approach I should use?
create proc [dbo].GetChangesForTable
#Table varchar(50),
#BeginTime datetime,
#EndTime datetime
as
begin
DECLARE #begin_lsn binary(10), #end_lsn binary(10);
DECLARE #func nvarchar(128)
if #EndTime is null select #EndTime=GETDATE()
SELECT #begin_lsn = sys.fn_cdc_map_time_to_lsn('smallest greater than', #BeginTime);
SELECT #end_lsn = sys.fn_cdc_map_time_to_lsn('largest less than or equal', #EndTime);
-- HOW TO GET THE CORRECT FUNCTION CALLED HERE?
SELECT * FROM cdc.fn_cdc_get_net_changes_dbo_TABLE(#begin_lsn, #end_lsn, 'all')
end
GO
I think that's possible with sp_executesql like that :
DECLARE #sql nvarchar(4000)
SET #sql = N'SELECT * FROM cdc.fn_cdc_get_net_changes_dbo_'+cast(#TABLE as varchar)+'(#begin_lsn, #end_lsn, 'all')'
EXEC sp_executesql #sql, N'#Table varchar(50), #BeginTime datetime, #EndTime datetime',#Table,#BeginTime,#EndTime