How can I declare a variable in a table valued function? (like in my title)
There are two flavors of table valued functions. One that is just a select statement and one that can have more rows than just a select statement.
This can not have a variable:
create function Func() returns table
as
return
select 10 as ColName
You have to do like this instead:
create function Func()
returns #T table(ColName int)
as
begin
declare #Var int
set #Var = 10
insert into #T(ColName) values (#Var)
return
end
In SQL Server:
It's not a very nice solution, but if you have a valid reason for needing to use an inline TVF instead of a MSTVF and cannot pass the variable into the TVF as a parameter, but can obtain it using a SELECT statement, you can use a CTE to access the value as follows:
CREATE FUNCTION func()
RETURNS TABLE
AS
RETURN
(
-- Create a CTE table containing just the required value
WITH cte_myVar AS
(SELECT <statement to select variable>) col1
-- Use the value by selecting it from the CTE table
SELECT * FROM table1 t1
WHERE t1.col1 = (SELECT col1 FROM cte_myVar)
)
Related
I'm using NEWID() inside a function.
this is my function:
CREATE FUNCTION dbo.myFunc (#newid NVARCHAR(50))
RETURNS int
AS BEGIN
declare #rndValue int
set #rndValue = (SELECT top 1 * FROM #temp ORDER BY lower(#newid))
RETURN #rndValue
END
i have #temp with values: '1','2','3'
i want to random from this table using this function.
i called this function like this:
dbo.myFunc (NEWID())
but i'm getting every time the same value ('1')
where is my error?
The function isn't using NEWID(). It's using the value of a string parameter named #newid
If you want to select a random row, use ORDER BY NEWID(). This generates a new non-sequential GUID for every row and orders the rows by it, effectively producing a random ordering, eg :
set #rndValue = (SELECT top 1 * FROM #temp ORDER BY newid())
The original query orders rows by a constant and thus has no effect. It's no different than using
set #rndValue = (SELECT top 1 * FROM #temp ORDER BY 'potato')
If you check the query's execution plan you'll see there's no SORT operation. The query optimizer simply removes the ineffective ORDER BY clause.
Finally, NEWID() produces a GUID, not a string. That's a 128-bit binary value. Using LOWER() serves no purpose
What you are doing looks strange, but if you need it in this format use the code below:
CREATE VIEW dbo.GetGUID
AS
SELECT NEWID() AS [NewID]
GO
CREATE TABLE dbo.temp
(
id int
);
GO
INSERT INTO dbo.temp
VALUES (1), (2), (3);
GO
CREATE FUNCTION dbo.myFunc ()
RETURNS int
AS BEGIN
declare #rndValue int
set #rndValue = (SELECT top 1 id FROM dbo.temp CROSS APPLY dbo.GetGUID ORDER BY [NewID])
RETURN #rndValue
END
GO
SELECT dbo.myFunc()
SELECT dbo.myFunc()
SELECT dbo.myFunc()
SELECT dbo.myFunc()
SELECT dbo.myFunc()
SELECT dbo.myFunc()
Basically, if you need to have NEWID() in function you need to use the view hack.
CREATE FUNCTION dbo.myFunc (#newid NVARCHAR(50))
RETURNS int
AS BEGIN
declare #rndValue int
set #rndValue = (SELECT top 1 * FROM #temp ORDER BY #newid)
RETURN #rndValue
END
I have a Table function which returns a set of rows based on a parameter, which looks like this example:
CREATE FUNCTION fn_get_records
(#PARENTID INT)
RETURNS #returnTable TABLE
(
-- columns returned by the function
Id INT NOT NULL,
Parent_Id INT NOT NULL,
Name VARCHAR(255) NOT NULL
)
AS
BEGIN
-- This select returns data
INSERT INTO #returnTable (Id, ParentId, Name)
SELECT Id, ParentId, Name FROM [whatever] where Parent_Id = #PARENTID
RETURN
END
I have another table which contains a list of those "Parent Ids" and I should call the previous function for each Parent_Id that matches a specific query and aggregate all results.
The parent Ids are retrieved using something like
SELECT Parent_Id
FROM Parent_Records
WHERE Country = 'USA'
This select returns, for example, 4 rows. For each row I have to execute my function fn_get_records and aggregate all results together in a view or in another function.
Is it even possible?
Of course I don't want to use cursors because I need something fast
Just a quick FYI: A more efficient TVF would be a single statement
CREATE FUNCTION fn_get_records (#PARENTID INT)
RETURNS TABLE
AS
RETURN (
SELECT Id, ParentId, Name FROM [whatever] where Parent_Id = #PARENTID
)
END
Then you can call your function via a CROSS Apply. For Example:
Select A.*
,B.*
From YourTable A
Cross Apply dbo.fn_get_records (A.SomeIntValue) B
Can anybody briefly explain why it is not possible to use values of the table as parameters for the joined function?
;CREATE FUNCTION "foo" ( #id INT )
RETURNS #result TABLE
(
"value" INT
)
AS
BEGIN
INSERT INTO #result SELECT #id * 2
RETURN
END;
;WITH "cte" AS
(
SELECT "id" = 1
UNION ALL
SELECT 2
)
SELECT
*
FROM
cte
, "foo"(cte."id")
The last line throws an error ( ~ cte."id" can not be bound ).
It doesn't matter if it's a cte or table.
Joining result sets coming out from table valued function is done by using CROSS APPLY.
I have a multi statement table valued function which I would like to change to an inline table valued function for optimization purposes ( I don't really know if that will be an optimization but I want to try that anyway ). My problem is that I have a scalar variable which I don't know how to put in my WITH statement.
Code example:
CREATE FUNCTION [dbo].[function]()
RETURNS
#return_table table (id INT,value NVARCHAR(500))
AS
BEGIN
DECLARE #tmp_table TABLE(id INT, value VARCHAR(500))
DECLARE #variable BIGINT
INSERT INTO #tmp_table [...insert code...]
SET #variable = (SELECT MAX(id) FROM #tmp_table)
INSERT INTO #return_table SELECT id,value FROM #tmp_table WHERE id = #variable
RETURN
This code is an example, the actual function is more complex but the problem is exactly the same
I could easily change this to a single WITH statement like this:
CREATE FUNCTION [dbo].[function]()
RETURNS TABLE
AS
RETURN
(
WITH tmp_table AS (
SELECT [...Select Code...]
)
SELECT id,value FROM tmp_table
WHERE id = [variable]
);
GO
My problem lies into the [variable] which I don't know how to put into the query. Also, the variable is used more than once in my function so I'd rather not just replace it with the query.
I also tried this approach:
CREATE FUNCTION [dbo].[function]()
RETURNS TABLE
AS
RETURN
(
WITH tmp_table AS (
SELECT [...Select Code...]
), variable = (SELECT MAX(id) value FROM tmp_table)
SELECT id,value FROM tmp_table
WHERE id = (SELECT TOP 1 value FROM variable)
);
GO
But is seems like it made the function way slower.
Thank you.
Just try
WITH tmp_table AS (
SELECT [...Select Code...]
)
SELECT id,value FROM tmp_table WHERE id = (SELECT MAX(id) FROM tmp_table)
I would actually just change it to
SELECT TOP 1 *
FROm [whatever]
ORDER BY id DESC
I am trying to achieve:
declare #TEMP table (ID int, Name varchar(max))
insert into #temp SELECT ID, Name FROM Table
SELECT * FROM #TEMP
WHERE #TEMP.ID = 1 <--- ERROR AT #TEMP.ID
But I'm getting the following error:
Must declare the scalar variable "#temp".
What am I doing wrong?
A table alias cannot start with a #. So, give #Temp another alias (or leave out the two-part naming altogether):
SELECT *
FROM #TEMP t
WHERE t.ID = 1;
Also, a single equals sign is traditionally used in SQL for a comparison.
Either use an Allias in the table like T and use T.ID, or use just the column name.
declare #TEMP table (ID int, Name varchar(max))
insert into #temp SELECT ID, Name FROM Table
SELECT * FROM #TEMP
WHERE ID = 1
There is one another method of temp table
create table #TempTable (
ID int,
name varchar(max)
)
insert into #TempTable (ID,name)
Select ID,Name
from Table
SELECT *
FROM #TempTable
WHERE ID = 1
Make Sure You are selecting the right database.
If you bracket the # you can use it directly
declare #TEMP table (ID int, Name varchar(max))
insert into #temp values (1,'one'), (2,'two')
SELECT * FROM #TEMP
WHERE [#TEMP].[ID] = 1
You should use hash (#) tables, That you actually looking for because variables value will remain till that execution only.
e.g. -
declare #TEMP table (ID int, Name varchar(max))
insert into #temp SELECT ID, Name FROM Table
When above two and below two statements execute separately.
SELECT * FROM #TEMP
WHERE #TEMP.ID = 1
The error will show because the value of variable lost when you execute the batch of query second time.
It definitely gives o/p when you run an entire block of code.
The hash table is the best possible option for storing and retrieving the temporary value. It last long till the parent session is alive.
try the following query:
SELECT ID,
Name
INTO #tempTable
FROM Table
SELECT *
FROM #tempTable
WHERE ID = 1
It doesn't need to declare table.
You could stil use #TEMP if you quote the identifier "#TEMP":
declare #TEMP table (ID int, Name varchar(max));
insert into #temp SELECT 1 AS ID, 'a' Name;
SELECT * FROM #TEMP WHERE "#TEMP".ID = 1 ;
db<>fiddle demo
You've declared #TEMP but in your insert statement used #temp. Case sensitive variable names.
Change #temp to #TEMP