I want to pass the table name as a parameter to table_valued function in MS SQL Server
CREATE FUNCTION maxid
(
-- Add the parameters for the function here
#tblname sysname,
#feild nvarchar(max),
)
RETURNS TABLE
AS
RETURN
(
-- Add the SELECT statement with parameter references here
select ISNULL(max(#feild),0)+1 from #tblname
)
You cannot use dynamic SQL in table valued or any other TSQL function. However, the code you provide seems to be used to obtain the next value of some identifier or counter field. The way you want to do it is highly deprecated and leads to concurrency problems.
Indeed, SQL Server can do it using at least two standard methods:
using a sequences
creating an identity column
I have done it but with store procedure
CREATE procedure [dbo].[get_maxid]
#tblname nvarchar(max),
#col nvarchar(max)
as
Begin
declare #sql nvarchar(max);
set #sql='select ISNULL(MAX('+#col+'),0)+1 as id from '+ QUOTENAME( #tblname)
execute sp_executesql #sql
End
Now Execute Store Procedure
exec get_maxid #col='col_name',#tblname='tbl_name'
Related
I am currently moving a SAS process to SQL. Within the SAS process, I leverage macros to create a multitude of tables.
I am trying to leverage the CREATE FUNCTION function within SQL to mimic this process, however I am stuck. I have three arguments, the server name, the name of the new table and the table where it should select from. I'm not sure what I should specify as what I am returning as I'm not looking to return anything, just create tables.
CREATE FUNCTION dbo.functionname (#server VARCHAR(250), #name VARCHAR(250), #table VARCHAR(250))
RETURN (???)
AS BEGIN
SELECT *
INTO #server.dbo.#nm
FROM #table
RETURN
END
This is what I have come up with so far. My SELECT statement wouldn't actually be *, I just put that for simplicity sake for this question.
UPDATE: In this instance, using a stored procedure is not an option as permissions have been limited.
You can create a dynamic SQL script as follows
declare #newtable sysname = 'T003',
#sourcetable sysname = 'sys.tables'
declare #sql nvarchar(max)
set #sql = N'select * into ' + #newtable + ' from ' + #sourcetable + ';'
set #sql = #sql + N'select * from ' + #newtable
exec sp_executesql #sql
Then you can use it in a stored procedure
To return data from new table, the table type must be known before. In this case it is not possible, so developer cannot create the function return type
Or create a function just to create the table and insert data into it. But return fail or success, etc
Then select from the new table using a dynamic SQL again
There is this query that I keep using over and over:
SELECT column_name, count(column_name) FROM table_name GROUP by column_name ORDER BY COUNT(column_name) DESC
I use this to check which different values there are in a column and how often they occur.
Because I use this query so often and it's repeating the same 4 times: column_name, I was like: why not make a stored procedure:
CREATE PROCEDURE countcv #table_name VARCHAR(50),#column_name VARCHAR(50)
AS
BEGIN
SELECT #column_name,COUNT(#column_name) FROM #table_name GROUP BY #column_name ORDER BY COUNT(#column_name)
END
Here is where I get stuck, I can not manage to get a variable tablename:
Must declare the table variable "#table_name"
I believe that #Julien Vavasseur and #Dark Knight has already addressed to your question.
However, I would like to add here that, Sql Server 2008 introduced Table-Valued Parameter by using which we can pass table type variable to the stored procedures. e.g.
Assuming you have a table by the name tblTest with the below columns
ID INT,
Name VARCHAR(50)
Step 1: Declare a new table User Defined Type
CREATE TYPE tblTestType AS TABLE
(
ID INT,
Name VARCHAR(50)
)
Step 2: Create a STORED PROCEDURE that has tblTestType as parameter
CREATE PROCEDURE countcv
(
#tblName tblTestType readonly
)
AS
INSERT INTO tblTest (ID, Name)
SELECT ID, Name
FROM
#tblName;
Then you can use DataTable (if you are using C#) and pass this data table as a parameter to the Stored Procedure.(you can find an example in the link I provided).
There is no way to do it directly. You need to use dynamicSQL approach. Assuming you pass correct table and column names. Below one should work.
CREATE PROCEDURE countcv #table_name VARCHAR(50),#column_name VARCHAR(50)
AS
BEGIN
declare #SQL nvarchar(max)
set #SQL = 'SELECT '+#column_name+',COUNT('+#column_name+')
FROM '+#table_name+'
GROUP BY '+#column_name+'
ORDER BY COUNT('+#column_name+')'
EXEC sp_executesql #SQL
END
If you want to do something like this, you must use dynamic SQL:
CREATE PROCEDURE countcv #table_name sysname, #column_name sysname
AS
BEGIN
Declare #sql nvarchar(max)
Set #sql = 'SELECT ' + QUOTENAME(#column_name)+', COUNT(' + QUOTENAME(#column_name)+')
FROM ' + QUOTENAME(#table_name)+'
GROUP BY ' + QUOTENAME(#column_name)+' ORDER BY COUNT(' + QUOTENAME(#column_name)+')'
EXEC sp_executesql #sql
END
Use sysname for data type for column and table names (buitin datatype for object names, alias to nvarchar(128))
Use QUOTENAME to add delimeter to column and table names
I want to create a SQL tabled-value function that will receive a query as n parameter through my API. In my function I want execute that query. The query will be a SELECT statement.
This is what I have done so far and what to achieve but it is not the correct way to do so.
CREATE FUNCTION CUSTOM_EXPORT_RESULTS (
#query varchar(max),
#guid uniqueidentifier,
#tableName varchar(200))
RETURNS TABLE
AS
RETURN
(
-- Execute query into a table
SELECT *
INTO #tableName
FROM (
EXEC(#query)
)
)
GO
Please suggest the correct way!
Try this one -
CREATE PROCEDURE dbo.sp_CUSTOM_EXPORT_RESULTS
#query NVARCHAR(MAX) = 'SELECT * FROM dbo.test'
, #guid UNIQUEIDENTIFIER
, #tableName VARCHAR(200) = 'test2'
AS BEGIN
SELECT #query =
REPLACE(#query,
'FROM',
'INTO [' + #tableName + '] FROM')
DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL = '
IF OBJECT_ID (N''' + #tableName + ''') IS NOT NULL
DROP TABLE [' + #tableName + ']
' + #query
PRINT #SQL
EXEC sys.sp_executesql #SQL
RETURN 0
END
GO
Output -
IF OBJECT_ID (N'test2') IS NOT NULL
DROP TABLE [test2]
SELECT * INTO [test2] FROM dbo.test
What I see in your question is encapsulation of:
taking a dynamic SQL expression
executing it to fill a parametrized table
Why do you want to have such an encapsulation?
First, this can have a negative impact on your database performance. Please read this on EXEC() and sp_executesql() . I hope your SP won't be called from multiple parts of your application, because this WILL get you into trouble, at least performance-wise.
Another thing is - how and where are you constructing your SQL? Obviously you do it somewhere else and it seems its manually created. If we're talking about a contemporary application, there are lot of OR/M solutions for this and manual construction of TSQL in runtime should be always avoided if possible. Not to mention EXEC is not guarding you against any form of SQL injection attacks. However, if all of this is a part of some database administration TSQL bundle, forget his paragraph.
At the end, if you want to simply load a new table from some existing table (or part of it) as a part of some administration task in TSQL, consider issuing a SELECT ... INTO ... This will create a new target table structure for you (omitting indexes and constraints) and copy the data. SELECT INTO will outperform INSERT INTO SELECT because SELECT INTO gets minimally logged.
I hope this will get you (and others) at least a bit on the right track.
You can use stored procedure as well, here is the code that you can try.
CREATE FUNCTION CUSTOM_EXPORT_RESULTS
(
#query varchar(max),
#guid uniqueidentifier,
#tableName varchar(200)
)
RETURNS TABLE
AS
RETURN
(
declare #strQuery nvarchar(max)
-- Execute query into a table
SET #strQuery = REPLACE(#query,'FROM', 'INTO '+#tableName+' FROM')
exec sp_executesql #strQuery
)
GO
Not sure how to implement this, but I need a way to get the current list of parameters for a stored procedure as well as their passed in values (this code will be executed in the stored procedure itself).
I know I can use sys.parameters to get the parameter names, but how to get the actual values?
What I need to do with this is to make a char string of the form
#param_name1=#value1,#param_name2=#value2,...,#param_namen=#valuen
I have tried to use dynamic sql, but not having much joy with that.
Any ideas??
Edit:
Currently I am just going through all the parameters one-by-one to build the string. However I want a "better" way to do it, since there are quite a few parameters. And incase parameters are added later on (but the code to generate the string is not updated).
I tried using dynamic sql but gave up, since the sp_executesql sp requires parameters be passed into it...
You state '(this code will be executed in the stored procedure itself).' so assuming you are in the procedure you will already know the parameter names as you have to declare them when creating your procedure. Just do a select and put the names inside text fields
ALTER PROCEDURE procname
(
#param1 NVARCHAR(255)
,#param2 INT
...
)
SELECT [Parameters] = '#param1=' + #param1
+ ',#param2=' + CONVERT(NVARCHAR(MAX),#param2)...
The CONVERT is there as an example for non-char datatypes.
update
You will need to create a linked server that points to itself to use the OPENQUERY function.
USE [master]
GO
/****** Object: LinkedServer [.] Script Date: 04/03/2013 16:22:13 ******/
EXEC master.dbo.sp_addlinkedserver #server = N'.', #srvproduct=N'', #provider=N'SQLNCLI', #datasrc=N'.', #provstr=N'Integrated Security=SSPI'
/* For security reasons the linked server remote logins password is changed with ######## */
EXEC master.dbo.sp_addlinkedsrvlogin #rmtsrvname=N'.',#useself=N'True',#locallogin=NULL,#rmtuser=NULL,#rmtpassword=NULL
GO
Now you can do something like this cursor to get each parameter name and then use dynamic sql in OPENQUERY to get the value:
DECLARE curParms CURSOR FOR
SELECT
name
FROM sys.parameters
WHERE OBJECT_ID = OBJECT_ID('schema.procedurename')
ORDER BY parameter_id
OPEN curParms
FETCH curParms INTO #parmName
WHILE ##FETCH_STATUS <> -1
BEGIN
SELECT #parmName + '=' + (SELECT * FROM OPENQUERY('linkedservername','SELECT ' + #parmName))
FETCH curParms INTO #parmName
END
CLOSE curParms
DEALLOCATE curParms
Since SQL Server 2014 we have sys.dm_exec_input_buffer, it is a table valued function with an output column event_info that gives the full execution statement (including parameters).
We can parse the param values from sys.dm_exec_input_buffer and get the param names from sys.parameters and join them together to get the string you want.
For example:
create procedure [dbo].[get_proc_params_demo]
(
#number1 int,
#string1 varchar(50),
#calendar datetime,
#number2 int,
#string2 nvarchar(max)
)
as
begin
-- get the full execution statement
declare #statement nvarchar(max)
select #statement = event_info
from sys.dm_exec_input_buffer(##spid, current_request_id())
-- parse param values from the statement
declare #proc_name varchar(128) = object_name(##procid)
declare #param_idx int = charindex(#proc_name, #statement) + len(#proc_name)
declare #param_len int = len(#statement) - #param_idx
declare #params nvarchar(max) = right(#statement, #param_len)
-- create param values table
select value, row_number() over (order by current_timestamp) seq
into #params
from string_split(#params, ',')
-- get final string
declare #final nvarchar(max)
select #final = isnull(#final + ',','') + p1.name + '=' + ltrim(p2.value)
from sys.parameters p1
left join #params p2 on p2.seq = parameter_id
where object_id = ##procid
select #final params
end
To test it:
exec get_proc_params_demo 42, 'is the answer', '2019-06-19', 123456789, 'another string'
Returns the string you want:
#number1=42,#string1='is the answer',#calendar='2019-06-19',#number2=123456789,#string2='another string'
I have something similar wrapped as a UDF. I use it for error logging in catch blocks.
I have a Microsoft SQL stored procedure whose column name I want to set via a variable that is passed into it:
CREATE PROCEDURE [My_Procedure]
#myDynamicColumn varchar(50)
AS BEGIN
SELECT 'value' AS #myDynamicColumn
END
This does not work ("Incorrect syntax"). If I wrap the column name with [ ]:
SELECT 'value' AS [#myDynamicColumn]
The column name literally outputs as '#myDynamicColumn' instead of the actual value. Is there any way to do this? I've looked into dynamic SQL articles but nothing is quite what I'm asking for.
EXEC ('SELECT ''value'' AS ' + #myDynamicColumn)
You could build your query into a string and use exec
CREATE PROCEDURE [My_Procedure]
#myDynamicColumn varchar(50)
AS BEGIN
EXEC('SELECT ''value'' AS ' + #myDynamicColumn)
END
Both the upvoted answers are very dangerous here, both are wide open to injection attacks and should not be used.
When injecting dynamic object names you must ensure you properly quote your object names. SQL Server has a built in function for that, QUOTENAME. Thus what you should actually be doing is the following:
CREATE PROCEDURE [My_Procedure] #myDynamicColumn sysname
AS BEGIN
DECLARE #SQL nvarchar(MAX) = N'SELECT ''value'' AS ' + QUOTENAME(#myDynamicColumn) + N';';
EXEC sys.sp_executesql #SQL;
END
You'll note I also change the data type of the parameter to sysname, a synonym for nvarchar(128) NOT NULL, which is the data type SQL Server uses internally for object names.