I have a table called Std_Components which acts like an index for list of components with associated tables. The column AssociatedTable holds the name of table that actually contains the component data.
Please check images below -
Here is table data for Std_SteeringPumps
I am trying to create a stored procedure that will copy Std_Components table as well as all associated tables with new name. For ex. Lets say if i provided 001 as a parameter to this stored procedure i should be able create new tables like C001_Components, C001_SteeringPumps and so on.
This is what I have done so far:
ALTER PROCEDURE [dbo].[sgi_sp_CreateTablesForNewCompany]
-- Add the parameters for the stored procedure here
#CompanyId varchar(5)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- declare variables
declare #qry as varchar(2000)
declare #compTblName as varchar(100)
set #compTblName = 'C'+#companyId +'_Components'
-- Check if table already exists
IF object_id(#compTblName) is not null
return
-- Create main component index table by copying standard component table --
set #qry = 'Select * into '+#compTblName+' From Std_Components;';
--print #qry
--execute (#qry)
set #qry =#qry + 'Update C'+#companyId +'_Components Set AssociatedTable=''C'+#companyId +'''+substring(AssociatedTable,4,200);';
--print #qry
--exec #qry
-- Create all child tables --
Select * Into #TempTbl From dbo.Std_Components
Declare #Id int
While (Select Count(*) From #TempTbl) > 0
Begin
declare #rowTableName as varchar(50)
declare #compNewTbl as varchar(50)
Select Top 1 #rowTableName=AssociatedTable, #Id = Id From #TempTbl
set #compNewTbl = 'C'+#companyId + substring(#rowTableName,4,200);
set #qry = #qry + 'Select * into '+#compNewTbl+' From ' + #rowTableName + ';'
--print #qry
--exec #qry
Delete #TempTbl Where Id = #Id
End
print #qry
exec #qry
END
Here is the output of the print statement for the query it generates -
Select * into C001_Components From Std_Components;
Update C001_Components Set AssociatedTable='C001'+substring(AssociatedTable,4,200);
Select * into C001_SteeringPumps From Std_SteeringPumps;
But when the stored procedure is executed, I get the following error -
Msg 203, Level 16, State 2, Procedure sgi_sp_CreateTablesForNewCompany, Line 56
The name 'Select * into C001_Components From Std_Components;Update C001_Components Set AssociatedTable='C001'+substring(AssociatedTable,4,200);Select * into C001_SteeringPumps From Std_SteeringPumps;' is not a valid identifier.
Can anybody help me out resolve this issue.
Thanks for sharing your time and wisdom.
The error you're getting is because the EXEC statement (the last line of the stored procedure) needs to have brackets around the #qry variable so that it becomes
exec(#qry)
Without the brackets it's treating the entire SQL string as stored procedure name.
The non valid indentifier is around the AssociatedTable part
Set AssociatedTable='C001'+substring(AssociatedTable,4,200); will not run as there is no scope for AssociatedTable to substring - the string needs to contain the name of the table completely to be able to be executed
Instead of
exec #qry;
You need
exec sp_executesql #qry;
You'll also need to change the type of #qry to NVARCHAR. Note that because of the dynamic sql, the proc is prone to SQL Injection and other escaping issues (i.e. ensure that #CompanyId is validated)
Related
I had defined a variable Test which is #[User::Test] with string value "abc".
My question is can I set the sql command that update my table where my column value = my variable ?
example.
update tableA set ValueB = '1pm' where ValueA = '" + #[User::Test] + "'
But this is not working for me. How to solve it ?
You have to use a question mark in the SQL text and bind the variable using the query configuration (IIRC, it was the Parameter Mapping tab). For example, I used to do stuff like this:
-- declare vars
declare #table varchar(256);
declare #sql varchar(max);
-- get the table as a parameter
set #table = ?;
-- drop the table if it already exists
if (object_id(#table) is not null) begin;
set #sql = 'drop table '+#table+';';
exec(#sql);
end;
-- create the table
set #sql = '
create table '+#table+' (
IPID int,
...
_rn int
);
';
exec(#sql);
Here, I found a screenshot in Google: https://www.simple-talk.com/iwritefor/articlefiles/1455-SsisVariables_Fig11-620x524.jpg
I have a stored procedure
create PROCEDURE [dbo].[SP]
(
#OrderList varchar(500)
)
AS
Begin
select *
from table
where id in ('+ #OrderList +')
Here I am passing orderlist....
When I execute like this
exec sp 'iss005,iss006'
I am not getting data
but when I hardcode in sp like this ...
select * from table where id in ('iss005','iss006')
then am getting data...
Thank you
Unfortunately it won't work that way. If you change your procedure to something like the following, this will work:
Create Procedure dbo.SP
#OrderList varchar(500)
AS
Declare #SQL VarChar(1000)
Select #SQL = 'SELECT * FROM table '
Select #SQL = #SQL + 'WHERE id in (' + #OrderList +')'
Exec ( #SQL)
GO
Looking more into your query, your ID's value varchar, so the procedure will fail as you'll still be getting :
WHERE id in (iss005,iss006)
when you want :
WHERE id in ('iss005','iss006')
You would need to either pass in the quote values, e.g. :
#OrderList = 'iss005','iss006'
Or work out some SQL to split the #OrderList by comma and use the QUOTENAME() function to add the quotes to the new variable.
I strongly recommend in this case the use of XML parameters, will give you a lot of flexibility.
Your XML might be something like
<ids>
<id>iss006</id>
<id>iss005</id>
</ids>
Your procedure should be something like this:
create PROCEDURE [dbo].[SP]
(
#OrderList XML
)
AS
Begin
select * from table
where id in (
select ParamValues.ID.value('.','VARCHAR(50)')
FROM #OrderList.nodes('/ids/id') as ParamValues(id)
)
Besides the use of store procedures outputs I also would recommend the use of functions but that is up to you.
Regards.
I had the same kind of requirement. i was getting list of user in a int list variable and i need to get all the order of those user. I have use a very simple trick which had solve my issue. please find the code.
public DataTable GetAllOrderData(List<int> UserID)
{
try
{
string listofuser = String.Join(",", UserID.ToArray());
SqlParameter[] parameters = new SqlParameter[]
{
new SqlParameter("#USERID", listofuser)
};
return SqlDBHelper.ExecuteParamerizedSelectCommand("GetOrderByUserID", System.Data.CommandType.StoredProcedure, parameters);
}
finally { UserID = null; }
}
And this is the stored procedure
CREATE PROCEDURE [dbo].[GetOrderByUserID] (#USERID varchar(700))
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
Declare #SQL VarChar(1000)
Select #SQL = 'SELECT *,ORM.OrganisationName FROM OrderTransaction ORT LEFT JOIN OrganisationMaster ORM ON (ORT.OrganisationID=ORM.OrganisationID) '
Select #SQL = #SQL + 'WHERE ORT.CreatedBy IN (' + #USERID +')'
Exec ( #SQL)
END
I am trying to create a stored procedure to achieve the following in the stored procedure
DECLARE #TBLNAME VARCHAR(128)
SET #TBLNAME = SELECT PAR_VALUE FROM DBO.PARAMETERS WHERE PAR_NAME='SALES_ORDERS_TABLE';
The PAR_VALUE column in the PARAMETERS table contains the name of the table of the Sales Order table.
I now want to use this table name in the stored procedure, and count the number of rows in this table.
DECLARE #SQL NVARCHAR(4000)
# SQL = SELECT COUNT(*) FROM '[#TBLNAME]'
However, when I try to run this, there are multiple errors.
Can you please help me by guiding on how to do this?
I just now tried this code:
CREATE PROCEDURE JCOUNT_SO
AS
DECLARE #TBLNAME NVARCHAR(512)
SELECT #TBLNAME=(SELECT PAR_VALUE FROM DBO.PARAMETERS WHERE PAR_NAME='SALES_ORDERS_TABLE')
DECLARE #SQL NVARCHAR(4000)
SELECT #SQL='SELECT COUNT(*) AS #_OF_RECORDS INTO SO_COUNT
FROM' '+QUOTENAME(#TBLNAME)''
EXEC SP_EXECUTESQL #SQL;
Error Message: Invalid Object Name: 'TEST.DBO.SO_MASTER'
Please help on this code.
Please read defination of sp_executesql for reference. Procedure below returns value as an output parameter from dynamic query rather than inserting into a table. You can manipulate query as per your requirement.
CREATE PROCEDURE JCOUNT_SO
AS
DECLARE #TBLNAME nvarchar(512)
--Obtain table name. Top 1 is used to pick first record rather than last record in case query returns more than one record.
SELECT TOP 1 #TBLNAME = PAR_VALUE FROM DBO.PARAMETERS WHERE PAR_NAME='SALES_ORDERS_TABLE'
DECLARE #SQL NVARCHAR(4000)
DECLARE #Count int
SET #SQL ='SELECT #Count = COUNT(*) FROM ' + #TBLNAME
--Define parameters to be passed i.e. #Count is being passed as output parameter
EXEC SP_EXECUTESQL #SQL, N'#Count int OUTPUT', #Count output
select #Count
end
Notes from MSDN
Query i.e. #SQL can contain parameters having the same form as a variable name and each parameter included in #SQLmust have a corresponding entry in both the #params parameter definition list and the parameter values list
1 point I can say is ... it should be SET #SQL = SELECT ...
and one more thing is you can try converting the result to NVARCHAR.
use SELECT Convert(NVARCHAR(4000),Count(*)) FROM ...
I have a view that returns users projects and also their windows login. An example of the data is below:
project | Login
------------------
project 1 | richab
project 2 | stevej
I need to append the domain to the login. I could put this in the code but i dont want to do that in every view I ever create that pulls users logins.
Can I create a global variable that I can reference in the views code. How can I achieve this? What's best practice for this?
I don't know if the SQL Server has global variables, but you can use a user defined function as follows:
CREATE FUNCTION dbo.fn_GetDomainName()
RETURNS STRING
AS
BEGIN
RETURN 'domain_name\\'
END
and do a SELECT dbo.fn_GetDomainName() + Login FROM table WHERE ... at the corresponding locations in your views.
There's no such thing as a global variable in SQL Server.
You can't just do:
DECLARE ##GlobalVar int
You can fake it with CONTEXT_INFO but to use something that would last beyond a session or restart you need to do something like this:
USE master
IF OBJECT_ID('dbo.sp_GlobalVariables') IS NOT NULL
DROP TABLE dbo.sp_GlobalVariables
GO
CREATE TABLE dbo.sp_GlobalVariables
(
varName NVARCHAR(100) COLLATE Latin1_General_CS_AI,
varValue SQL_VARIANT
)
GO
IF OBJECT_ID('dbo.sp_GetGlobalVariableValue') IS NOT NULL
DROP PROC dbo.sp_GetGlobalVariableValue
GO
CREATE PROC dbo.sp_GetGlobalVariableValue
(
#varName NVARCHAR(100),
#varValue SQL_VARIANT = NULL OUTPUT
)
AS
SET NOCOUNT ON
-- set the output parameter
SELECT #varValue = varValue
FROM sp_globalVariables
WHERE varName = #varName
-- also return it as a resultset
SELECT varName, varValue
FROM sp_globalVariables
WHERE varName = #varName
SET NOCOUNT OFF
GO
IF OBJECT_ID('dbo.sp_SetGlobalVariableValue') IS NOT NULL
DROP PROC dbo.sp_SetGlobalVariableValue
GO
CREATE PROC dbo.sp_SetGlobalVariableValue
(
#varName NVARCHAR(100),
#varValue SQL_VARIANT,
#result CHAR(1) = NULL OUTPUT
)
AS
SET NOCOUNT ON
UPDATE dbo.sp_GlobalVariables
SET varValue = #varValue
WHERE varName = #varName;
-- if it doesn't exist yet add it
IF ##rowcount = 0
BEGIN
INSERT INTO dbo.sp_GlobalVariables(varName, varValue)
SELECT #varName, #varValue
-- return it as inserted
SELECT #result = 'I'
END
-- return it as updated
SELECT #result = 'U'
SET NOCOUNT OFF
GO
DECLARE #dt DATETIME
SELECT #dt = GETDATE()
EXEC sp_SetGlobalVariableValue 'GlobalDate', #dt;
EXEC sp_SetGlobalVariableValue 'GlobalInt', 5;
EXEC sp_SetGlobalVariableValue 'GlobalVarchar', 'This is a very good global variable'
EXEC sp_SetGlobalVariableValue 'GlobalBinary', 0x0012314;
GO
EXEC sp_GetGlobalVariableValue 'GlobalDate'
EXEC sp_GetGlobalVariableValue 'GlobalInt'
EXEC sp_GetGlobalVariableValue 'GlobalVarchar'
EXEC sp_GetGlobalVariableValue 'GlobalBinary'
GO
-- update value in master
EXEC sp_SetGlobalVariableValue 'GlobalVarchar', 'New varchar value'
USE AdventureWorks
EXEC sp_GetGlobalVariableValue 'GlobalDate'
EXEC sp_GetGlobalVariableValue 'GlobalInt'
EXEC sp_GetGlobalVariableValue 'GlobalVarchar'
EXEC sp_GetGlobalVariableValue 'GlobalBinary'
-- update value in AdventureWorks
EXEC sp_SetGlobalVariableValue 'GlobalInt', 6
EXEC sp_GetGlobalVariableValue 'GlobalDate'
EXEC sp_GetGlobalVariableValue 'GlobalInt'
EXEC sp_GetGlobalVariableValue 'GlobalVarchar'
EXEC sp_GetGlobalVariableValue 'GlobalBinary'
You can use a temp table.
My scenario is that when data is updated via a known process, it adds a note to the audit table stating that 'this was done on purpose'.
When that proc fires, it inserts a single value into #auditnote (which is a temp table I create on the fly).
The trigger checks for that table. If it exists, it pulls off the note and puts it on the audit table.
If it doesn't, then it goes about it's business.
I looked at using an ## variable, but the trick there is determining if the variable exists. I don't see a way.
EXAMPLE:
Stored Procedure:
SELECT 'Alrighty Then' AS NOTE INTO #AuditNote
Trigger:
DECLARE #noteExists BIT
DECLARE #note NVARCHAR(500)
IF OBJECT_ID('tempdb..#auditnote') IS NOT NULL
BEGIN
SELECT
TOP (1)
#noteExists = 1,
#note = Note
FROM
#auditNote
END ELSE BEGIN
SELECT #noteExists = 0
END
-- do something with the note
IF #noteExists = 1
BEGIN
DROP TABLE #AuditNotes
END
I'm using #noteExists rather than a null check because someone could insert a null as the note, so we don't know if null means TABLE DOESN'T EXIST or NOTE IS NULL.
I have a variable table:
DECLARE #A_Table TABLE(ID INT, att1 VARCHAR(100), att2 nvarchar(200))
I want to make dynamic sql, so I insert into this table some data (all inside a loop):
WHILE (#i <= 100) BEGIN
SELECT #other_att = NAME FROM #other_Table where ID = #i;
SET #sql = 'INSERT ' + #A_Table+ '(ID,att1,att2) SELECT '+CAST(#i AS VARCHAR)+' , '''+ #other_att+''', SUM('+ #other_att') FROM '+ #EVEN_OTHER_Table;
EXEC (#sql);
END
sql every time would look like:
INSERT INTO #A_Table SELECT 1 , 'subject', SUM(subject)
INSERT INTO #A_Table SELECT 2 , 'age', SUM(age)
INSERT INTO #A_Table SELECT 3 , 'sex', SUM(sex)....
AND after executing this :
SO I will get:
#A_Table:
id att1 att2
1 subject 4.3
2 age 4.5
3 sex 4.1
but I get an error:
Msg 137, Level 16, State 1, Line 48
Must declare the scalar variable "#A_Table".
SO what is it the syntax to insert dynamically into a variable table?
Ok I have understood it.
You could use the INSERT ... EXEC syntax to insert the data returned by the dynamic SELECT. Of course, you would then need to remove the INSERT part from the dynamic statement.
WHILE (#i <= 100) BEGIN
SELECT #other_att = NAME FROM #other_Table where ID = #i;
SET #sql = 'SELECT '+CAST(#i AS VARCHAR)+' , ''' + #other_att+''', SUM('+ #other_att + ') FROM '+ #EVEN_OTHER_Table;
INSERT INTO #A_Table (ID,att1,att2)
EXEC (#sql);
END
You have a table variable, not a variable that contains the table name.
So you would need the following.
WHILE (#i <= 100) BEGIN
SELECT #other_att = NAME FROM #other_Table where ID = #i;
SET #sql = 'INSERT INTO #A_Table (ID,att1,att2) SELECT '+CAST(#i AS VARCHAR)+' , '''+ #other_att+''', SUM('+ #other_att') FROM #EVEN_OTHER_Table';
EXEC (#sql);
END
You would also need to declare the table variable as a statement inside the #sql variable, and execute your declare table and inserts together, or use a local/global temporary table.
With a local temporary table (stored in the tempdb) you could do something like this.
CREATE TABLE #testtbl (ID INT);
EXEC ('INSERT INTO #testtbl VALUES (1)');
SELECT * FROM #testtbl
DROP TABLE #testtbl
Some good info about temporary tables in BOL
http://msdn.microsoft.com/en-us/library/ms174979.aspx - quite far down the page
And the table type.
http://msdn.microsoft.com/en-us/library/ms175010.aspx
Your EXEC statement occurs in a different context and is therefore unaware of any variables created in your original context.
To create dynamic insert query it is really a task, I also struggle to find it ,finally I have tried in the following way and it's successfully working. Please find the code below.
CREATE PROCEDURE [dbo].[InsertTodaysData] (#tbl varchar(50),#Days int,
#MDate varchar(50), #EValue varchar(50), #Speed varchar(50),
#Totalreturn varchar(50),#Closingv varchar(50), #TotalReturnV varchar(50))
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE #SQLQuery varchar(2000)
-- Insert statements for procedure here
set #SQLQuery = 'INSERT INTO '+#tbl+' (ID,MDate,EValue,Speed,TotalReturnRatio,ClosingValue,
TotalReturnValue) VALUES ('+#Days+','''+#MDate+''', '+#EValue+', '+#Speed+',
'+#Totalreturn+', '+#Closingv+', '+#TotalReturnV+')'
EXECUTE(#SQLQuery)
END
Hope this will help you..