Exec stored procedure with table variable as parameter - sql

Does anyone know why this query doesn't work? How to add the table variable itemId as parameter to the exec statement? Thanks
DECLARE #test TABLE
(
itemId UNIQUEIDENTIFIER,
finalAmount DECIMAL
);
INSERT INTO #test EXEC [GetItems]
DECLARE #sql NVARCHAR(max)
DECLARE #param NVARCHAR(max)
SET #param = N'select itemId from #test'
SELECT #sql = 'EXEC [InsertTestItem]'+' ' + #param;
SELECT #sql
EXEC(#sql)

See a full working example in SQL Server, you should be able to run each block one after the other to see that its selected everything from your table type that you pass in to the stored proc
-- Create the Table type that we will use in the stored proc------------------------
IF NOT EXISTS (SELECT * FROM sys.types WHERE is_table_type = 1 AND name = 'MyIdTableType')
BEGIN
PRINT 'Creating type [dbo].[MyIdTableType]'
CREATE TYPE [dbo].MyIdTableType AS TABLE (
Id BIGINT
)
END
GO
-- Create a stored proc that uses it ------------------------
CREATE PROCEDURE [dbo].[UsMyTabelType]
#IdsTable AS [dbo].MyIdTableType Readonly
AS
BEGIN
-- Now you have the data you can use it like any normal table and join on it
SELECT * FROM #IdsTable
END
GO
-- Lets test it out ------------------------
DECLARE #myIds AS MyIdTableType
INSERT INTO #myIds (Id)
VALUES
(1),
(2),
(3)
EXEC dbo.UsMyTabelType #IdsTable = #myIds

Related

How to fetch the code comments from a stored procedure / function and populate to a table?

How to fetch the code comments from a stored procedure / function and populate to a table?
/*
Author : Test
Comment : Test
*/
I am working on a user defined function by passing either a stored procedure or function as input parameter to read the code history comments and store it in a table. Having the detail in a table to maintain the version notes for the input.
Check this, there are different ways to get the definition, I prefer sp_helptext because it's already splitted in lines
DECLARE #Objects TABLE(name varchar(100))
DECLARE #Lines TABLE(id int identity, line varchar(maX))
INSERT #Objects
SELECT name FROM sys.objects WHERE Type in ('FN', 'IF', 'P', 'TR', 'TF')
DECLARE #ObjectName VARCHAR(100)
WHILE EXISTS (SELECT 1 FROM #Objects)
BEGIN
SELECT TOP 1 #ObjectName = name FROM #Objects
DELETE #Lines
INSERT #Lines (line)
exec sp_helptext #ObjectName
DECLARE #Linestart INT, #LineEnd INT
WHILE EXISTS(SELECT 1 FROM #Lines WHERE charindex('/*', line) > 0)
BEGIN
SELECT TOP 1 #Linestart = id
FROM #Lines WHERE charindex('/*', line) > 0
ORDER BY id
SELECT TOP 1 #LineEnd = id
FROM #Lines WHERE charindex('*/', line) > 0
ORDER BY id
DECLARE #comment VARCHAR(MAX) = ''
SELECT #Coment = #coment + char(13) + char(10) + line
FROM #Lines
WHERE id between #LineStart and #lineEnd
INSERT INTO yourtable (#objectName, #Comment)
DELETE #Lines WHERE id between #LineStart and #lineEnd
END
DELETE #Objects WHERE name = #ObjectName
END
You can create a function/stored procedure to achieve this:
CREATE FUNCTION InsertCommentIntoTable
(
#Param1 VARCHAR(200)
)
RETURNS int
AS
BEGIN
-- Declare the return variable here
DECLARE #str VARCHAR(max)
SELECT #str = definition
FROM sys.sql_modules
WHERE object_id = (OBJECT_ID(N'dbo.CustOrderHist'));
--parse #str string value and do your stuffs: #str has the function and stored procedure codes.
RETURN 0;
END
GO

How to run string math expression in SQL Server (trigger or function)

I want to run:
select Mycalculatefunction('((3*4)-3)*5')
select ('((3*4)-3)*5')
OUTPUT: ((3*4)-3)*5 wrong (not int value)
My desired output is: 45
I defined a stored procedure:
create PROCEDURE dbo.Eval
(#exp varchar(MAX))
AS
SET NOCOUNT ON
DECLARE #SQLString NVARCHAR(MAX)
SET #SQLString = 'SELECT '+#exp
EXEC sp_executesql #SQLString
I call it:
exec dbo.Eval '((3*4)-3)*5'
How can I do in this process is the trigger?
Your SP is vulnerable to injection. F.e. I pass exec dbo.Eval '1;DROP TABLE some_table;'. Better use xml.query:
CREATE PROCEDURE dbo.Eval
#formula nvarchar(max)
AS
DECLARE #sql nvarchar(max)
SELECT #sql = N'
DECLARE #x xml = ''''
SELECT CAST(#x.query('''+#formula+''') as nvarchar(max))'
EXEC sp_executesql #sql
Then
EXEC dbo.Eval '((3*4)-3)*5'
Output:
45
Triggers part (as there were no info about your tables, just general explanation, I add full batch with comments):
--Create table that will store Formulas
CREATE TABLE Formulas (
ID int IDENTITY(1,1) NOT NULL,
Formula nvarchar(max) NULL,
CONSTRAINT PK_ID PRIMARY KEY (ID)
)
GO
--Create table to store results of the formulas
CREATE TABLE Results (
T1_ID int NOT NULL,
Result int NULL
)
GO
--Linked by ID
ALTER TABLE Results ADD CONSTRAINT FK_Formulas_Results FOREIGN KEY (T1_ID)
REFERENCES Formulas (ID)
GO
--Create a Table Valued Parameter
CREATE TYPE FormulaResults AS TABLE (
ID int NOT NULL,
Formula nvarchar(max) NULL
)
GO
--Create a procedure to do the count
CREATE PROCEDURE dbo.GetResults
#TVP FormulaResults READONLY
AS
DECLARE #sql nvarchar(max)
SELECT #sql = N'DECLARE #x xml = '''' '
SELECT #sql = #sql + 'SELECT '+CAST(ID as nvarchar(max))+' as ID, CAST(#x.query('''+Formula+''') as nvarchar(max)) UNION ALL '
FROM #TVP
SELECT #sql = LEFT(#sql,LEN(#sql)-LEN('UNION ALL '))
EXEC sp_executesql #sql
GO
--Create a trigger that will count formula after insert and update
CREATE TRIGGER GetResultsTrigger
ON Formulas
AFTER INSERT, UPDATE
AS
DECLARE #FormulaTVP AS FormulaResults
DECLARE #Results TABLE(
T1_ID int NOT NULL,
Result int NULL
)
INSERT INTO #FormulaTVP
SELECT *
FROM inserted
INSERT INTO #Results
EXEC dbo.GetResults #FormulaTVP
MERGE Results r
USING #Results s
ON r.T1_ID = s.T1_ID
WHEN NOT MATCHED THEN
INSERT VALUES (s.T1_ID, s.Result)
WHEN MATCHED THEN
UPDATE SET Result = s.Result;
After that run:
INSERT INTO [Formulas] VALUES
('1+3'),('2+2*8')
SELECT [ID],
[Formula]
FROM [Test].[dbo].[Formulas]
SELECT [T1_ID],
[Result]
FROM [Test].[dbo].[Results]
Output:
ID Formula
1 1+3
2 2+2*8
T1_ID Result
1 4
2 18
image 1
ALTER FUNCTION [dbo].[Calculate]
( #expression AS VARCHAR(MAX)
)
RETURNS xml
AS
BEGIN
-- routine body goes here, e.g.
-- SELECT 'Navicat for SQL Server'
DECLARE #result xml
declare #x xml=''
--I can not pass as a parameter
select #result=#x.query(('(4*3)*5-10'))
return #result;
END
i call this function:
SELECT CAST(CAST(CAST(dbo.[Calculate]('How do I pass parameters') AS XML) AS VARCHAR(100)) AS DECIMAL(4,2))
Output:
50.00
select #result=#x.query('sql:variable("#expression")')
result: '2+2' :(((

SQL: How to make table name in stored procedure dynamic

I am pretty new to SQL Server and hope someone here can help me with this (I'm using QL Server 2008).
The following is a small procedure that works as intended.
Now I would like to use the same procedure to update multiple tables as all these tables have exactly the same column names and column formatting, the only difference is the 2nd part of the table name for which I added XXX below.
Can someone tell me how this could be made dynamic and also provide me some explanations on this ?
I cannot provide much more here as I wasn't sure about how to approach this - other than probably declaring #sql nvarchar(max) and wrapping the whole query in SET #sql = N'...' before executing it.
My stored procedure:
CREATE PROCEDURE [dbo].[Cal_UpdateTeam]
#team nvarchar(100),
#teamID int,
#notes nvarchar(1000),
#log nvarchar(100),
#admin varchar(50)
AS
BEGIN
SET NOCOUNT ON;
BEGIN
IF NOT EXISTS
(
SELECT *
FROM Cal_XXX
WHERE teamID = #teamID
)
INSERT INTO Cal_XXX
(
team,
teamID,
notes,
log,
admin
)
SELECT #team,
#teamID,
#notes,
#log,
#admin
ELSE
UPDATE Cal_XXX
SET team = #team,
teamID = #teamID,
notes = #notes,
log = #log,
admin = #admin
WHERE teamID = #teamID
END
END
Many thanks for any tips and advise on this, Mike.
you should wrap your sql query in an nvarchar and then execute that query as in the below example :
declare #sql nvarchar(max)
declare #TableName nvarchar(max)
set #TableName = 'mytable'
set #sql = 'Select * from ' + #TableName
Exec sp_executesql #sql
in SP you can use Temporary Tables fro example:
CREATE PROCEDURE SELECT_TABLE
#REQUEST_ID INT
AS
BEGIN
/*************************************
** Temporary table **
*************************************/
CREATE TABLE #SOURCE (
ID INT
, ID_PARENT INT
, NAME VARCHAR(200)
, SORT INT
..
..
)
IF #REQUEST_ID = 'YES' BEGIN
INSERT INTO #SOURCE SELECT * FROM SOURCE_A
END
ELSE BEGIN
INSERT INTO #SOURCE SELECT * FROM SOURCE_B
END
SELECT * FROM #SOURCE
.....
END
GO
in SP you can encapsulate other SPs with different table names like parameter:
CREATE PROCEDURE SELECT_FROM_TABLE_A
AS
BEGIN
SELECT * FROM SOURCE_A
END
GO
CREATE PROCEDURE SELECT_FROM_TABLE_B
AS
BEGIN
SELECT * FROM SOURCE_B
END
GO
CREATE PROCEDURE SELECT_TABLE
#REQUEST_ID INT
AS
BEGIN
/**********************************************
** Subrequest select **
**********************************************/
IF #REQUEST_ID = 'YES' BEGIN
-- Request SP fro Source A
EXEC SELECT_FROM_TABLE_A
END
ELSE
BEGIN
-- Request SP fro Source B
EXEC SELECT_FROM_TABLE_B
END
END
GO

sp_executesql and table output

I'm writing a stored procedure in SQL Server 2005, at given point I need to execute another stored procedure. This invocation is dynamic, and so i've used sp_executesql command as usual:
DECLARE #DBName varchar(255)
DECLARE #q varchar(max)
DECLARE #tempTable table(myParam1 int, -- other params)
SET #DBName = 'my_db_name'
SET q = 'insert into #tempTable exec ['+#DBName+'].[dbo].[my_procedure]'
EXEC sp_executesql #q, '#tempTable table OUTPUT', #tempTable OUTPUT
SELECT * FROM #tempTable
But I get this error:
Must declare the scalar variable "#tempTable".
As you can see that variable is declared. I've read the documentation and seems that only parameters allowed are text, ntext and image. How can I have what I need?
PS: I've found many tips for 2008 and further version, any for 2005.
Resolved, thanks to all for tips:
DECLARE #DBName varchar(255)
DECLARE #q varchar(max)
CREATE table #tempTable(myParam1 int, -- other params)
SET #DBName = 'my_db_name'
SET #q = 'insert into #tempTable exec ['+#DBName+'].[dbo].[my_procedure]'
EXEC(#q)
SELECT * FROM #tempTable
drop table #tempTable
SQL Server 2005 allows to use INSERT INTO EXEC operation (https://learn.microsoft.com/en-us/sql/t-sql/statements/insert-transact-sql?view=sqlallproducts-allversions).
You might create a table valued variable and insert result of stored procedure into this table:
DECLARE #tempTable table(myParam1 int, myParam2 int);
DECLARE #statement nvarchar(max) = 'SELECT 1,2';
INSERT INTO #tempTable EXEC sp_executesql #statement;
SELECT * FROM #tempTable;
Result:
myParam1 myParam2
----------- -----------
1 2
or you can use any other your own stored procedure:
DECLARE #tempTable table(myParam1 int, myParam2 int);
INSERT INTO #tempTable EXEC [dbo].[my_procedure];
SELECT * FROM #tempTable;
#tempTable's scope is limited to the current procedure.
You could replace the #tempTable with a global temporary table (i.e. ## table), but be very careful with the scope of that table and be sure to drop it when the procedure ends

Using a variable to represent multiple values

I have the following part of a query:
Where id in (1,2,3) And country in('France','Italy','Spain')
I want to declare 2 variables and do it like:
Where id in (idsVaraible) And country in(countriesVriable)
It is more like substituting sql code in sql code to make my long query readable and more useful, is there any way to do this?
I think it's more like eval in java script.
Well if you need to pass these sets in as strings, one way would be dynamic SQL:
DECLARE #ids VARCHAR(32) = '1,2,3';
DECLARE #countries VARCHAR(2000) = 'France,Italy,Spain';
DECLARE #sql NVARCHAR(MAX) = N'SELECT ... FROM ...
WHERE id IN (' + #ids + ') AND country IN ('''
+ REPLACE(#countries, ',',''',''') + ''');';
PRINT #sql;
-- EXEC sp_executesql #sql;
Another way would be table-valued parameters. First create these types in your database:
CREATE TYPE dbo.TVPids AS TABLE(ID INT);
CREATE TYPE dbo.TVPcountries AS TABLE(Country VARCHAR(255));
Now your stored procedure can take these types as input:
CREATE PROCEDURE dbo.whatever
#i dbo.TVPids READONLY,
#c dbo.TVPcountries READONLY
AS
BEGIN
SET NOCOUNT ON;
SELECT ... FROM dbo.yourtable AS t
INNER JOIN #i AS i ON i.ID = t.ID
INNER JOIN #c AS c ON c.country = t.country;
END
GO
Now your app can pass these two parameters in as sets (e.g. from a DataTable) instead of building a comma-separated string or handling multiple parameters.
Please try using temp table variables:
DECLARE #tblID as TABLE(ID INT)
DECLARE #tblCountry as TABLE(Country NVARCHAR(50))
INSERT INTO #tblID VALUES (1),(2),(3)
INSERT INTO #tblCountry VALUES ('France'),('Italy'),('Spain')
WHERE id in (select ID from #tblID) And country in(select Country from #tblCountry)