multiple selects from a function into a temp table - sql

I have a function helper_function which I want to reuse
Passing a parameter to another function f_union_helper_functions where a string id_string with ids is passed
Split this string and iterate through all ids
calling the function and adding result to a temp table
return select from this table
Pseudo code:
create function f_union_helper_functions
(
#id_string varchar,
...
return table as
return
(
...
foreach id in #id_string
begin
select * from helper_function(id) into #tmp
end
select #tmp
)
This code above is far from being complete/correct. I just want to combine the concepts of iterating and union into a temp table and return it. How can I achieve that?

Here is the way I solved it:
ALTER FUNCTION [dbo].[f_union_helper_functions]
(
#id_string NVARCHAR(1024)
)
RETURNS TABLE
AS
RETURN
(
SELECT * FROM [dbo].[fnSplit](#id_string, ',') c CROSS APPLY [dbo].[helper_function](c.item)
)
If you got SQLServer >= 2016 you can use STRING_SPLIT instead of [fnSplit], which I got from here https://kishsharma.wordpress.com/2013/08/20/sql-server-user-defined-function-to-split-the-string-by-special-char/

Related

Checking if field contains multiple string in sql server

I am working on a sql database which will provide with data some grid. The grid will enable filtering, sorting and paging but also there is a strict requirement that users can enter free text to a text input above the grid for example
'Engine 1001 Requi' and that the result will contain only rows which in some columns contain all the pieces of the text. So one column may contain Engine, other column may contain 1001 and some other will contain Requi.
I created a technical column (let's call it myTechnicalColumn) in the table (let's call it myTable) which will be updated each time someone inserts or updates a row and it will contain all the values of all the columns combined and separated with space.
Now to use it with entity framework I decided to use a table valued function which accepts one parameter #searchQuery and it will handle it like this:
CREATE FUNCTION myFunctionName(#searchText NVARCHAR(MAX))
RETURNS #Result TABLE
( ... here come columns )
AS
BEGIN
DECLARE #searchToken TokenType
INSERT INTO #searchToken(token) SELECT value FROM STRING_SPLIT(#searchText,' ')
DECLARE #searchTextLength INT
SET #searchTextLength = (SELECT COUNT(*) FROM #searchToken)
INSERT INTO #Result
SELECT
... here come columns
FROM myTable
WHERE (SELECT COUNT(*) FROM #searchToken WHERE CHARINDEX(token, myTechnicalColumn) > 0) = #searchTextLength
RETURN;
END
Of course the solution works fine but it's kinda slow. Any hints how to improve its efficiency?
You can use an inline Table Valued Function, which should be quite a lot faster.
This would be a direct translation of your current code
CREATE FUNCTION myFunctionName(#searchText NVARCHAR(MAX))
RETURNS TABLE
AS RETURN
(
WITH searchText AS (
SELECT value token
FROM STRING_SPLIT(#searchText,' ') s(token)
)
SELECT
... here come columns
FROM myTable t
WHERE (
SELECT COUNT(*)
FROM searchText
WHERE CHARINDEX(s.token, t.myTechnicalColumn) > 0
) = (SELECT COUNT(*) FROM searchText)
);
GO
You are using a form of query called Relational Division Without Remainder and there are other ways to cut this cake:
CREATE FUNCTION myFunctionName(#searchText NVARCHAR(MAX))
RETURNS TABLE
AS RETURN
(
WITH searchText AS (
SELECT value token
FROM STRING_SPLIT(#searchText,' ') s(token)
)
SELECT
... here come columns
FROM myTable t
WHERE NOT EXISTS (
SELECT 1
FROM searchText
WHERE CHARINDEX(s.token, t.myTechnicalColumn) = 0
)
);
GO
This may be faster or slower depending on a number of factors, you need to test.
Since there is no data to test, i am not sure if the following will solve your issue:
-- Replace the last INSERT portion
INSERT INTO #Result
SELECT
... here come columns
FROM myTable T
JOIN #searchToken S ON CHARINDEX(S.token, T.myTechnicalColumn) > 0

Return table valued function return table structure like my actual table using Sql Server 2008?

I have a table named tableincentive like below
create table tableincentive (entry_date date,ag_id int,us_id int,lo_id int,loin_id int,de_id int,dest_id int,incentive_amt decimal(18,2),le_id int,lion_id int,etc...)
And i have a table valued function named [dbo].[func_getdetails]
Create FUNCTION [dbo].[func_getdetails]
(
-- Add the parameters for the function here
#ason_date date
)
RETURNS
#tableincentive TABLE
(
-- Add the column definitions for the TABLE variable here
entry_date date,ag_id int,us_id int,lo_id int,loin_id int,de_id int,dest_id int,incentive_amt decimal(18,2),le_id int,lion_id int,etc...
)
AS
BEGIN
-- some long process then
insert into #tableincentive
select * from tableincentive;
end
My problem is the table tableincentive has 43 columns so i create a table valued function return tableincentive structure.I need return like my actual table.
that means in function
RETURNS
#tableincentive TABLE
(
-- Add the column definitions for the TABLE variable here
entry_date date,ag_id int,us_id int,lo_id int,loin_id int,de_id int,dest_id int,incentive_amt decimal(18,2),le_id int,lion_id int,etc...
)
AS
begin end
changed to
RETURNS tableincentive
AS
begin
end
or
RETURNS #tableincentive table like tableincentive
AS
begin
end
User Defined Functions need Static results. That means you have to define the return type while creating the function. So If you want to have a static result set, then probably you should opt for a stores Procedure.

Passing multiple values to a parameter of a function in SQL

There is function Getfunctionname(userid, startdate, enddate) to return a table
My question is can I pass a variable with multiple values?
i.e.
getfunctionname(#userid, startdate, enddate)
Where the value of variable #userid is like
1
2
3
4
5
(actually using split function splitting the values from being 1,2,3,4,5 )
If I can please let me know
One way of doing that which I prefer is to make a new user-defined table data type.
CREATE TYPE [dbo].[IdList] AS TABLE(
[Id] [int] NULL
)
Then you can use that data type as one of the parameters
CREATE FUNCTION Getfunctionname
(
#UserIDs dbo.IdList READONLY,
#startdate INT,
#endtdate INT
)
RETURNS #ReturnTable TABLE
(
-- ReturnTable
)
AS
BEGIN
-- Query
RETURN
END
Use the concept of CSV
CREATE FUNCTION [dbo].[uspGetNumbers]
userid,startdate,enddate // define your paramters the way you want
AS
BEGIN
// your code
JOIN dbo.fnSplit(#UserIDs, ',')
END
GO
Example function:
SELECT [dbo].[uspGetNumbers] '1,2,3,4,5', '', ''
I just ran into this, and I used the CROSS APPLY solution from this post:
SQL Server: run function for each row based on the provided row value
To use CROSS APPLY, you would need to first select your values, and then CROSS APPLY. I have not used the split function before, so I don't have the exact syntax,
but if you use it something like:
select #userid, F1.* from split(1,2,3,4,5),
CROSS APPLY getfunctionname(#userid, startdate, enddate) F1

SQL - create function that will take a table variable as an input

I'm having trouble writing a function that will take a table variable as an input and return the total number of rows in that table.
Here is my try:
CREATE FUNCTION fTableRows( #table TABLE )
RETURNS INT AS
BEGIN
RETURN( SELECT COUNT(*) FROM #table )
END
If you do this in SQL server 2008 + you have use user defined data type - table.
Good explanation can be found here: Pass table as parameter into SQL Udf
CREATE FUNCTION getTableRows
(
#TableName VARCHAR(30)
)
RETURNS INT AS
BEGIN
RETURN( SELECT COUNT(*) FROM #TableName)
END

T-SQL Foreach Loop

Scenario
I have a stored procedure written in T-Sql using SQL Server 2005.
"SEL_ValuesByAssetName"
It accepts a unique string "AssetName".
It returns a table of values.
Question
Instead of calling the stored procedure multiple times and having to make a database call everytime I do this, I want to create another stored procedure that accepts a list of all the "AssetNames", and calls the stored procedure "SEL_ValueByAssetName" for each assetname in the list, and then returns the ENTIRE TABLE OF VALUES.
Pseudo Code
foreach(value in #AllAssetsList)
{
#AssetName = value
SEL_ValueByAssetName(#AssetName)
UPDATE #TempTable
}
How would I go about doing this?
It will look quite crippled with using Stored Procedures. But can you use Table-Valued Functions instead?
In case of Table-Valued functions it would look something like:
SELECT al.Value AS AssetName, av.* FROM #AllAssetsList AS al
CROSS APPLY SEL_ValuesByAssetName(al.Value) AS av
Sample implementation:
First of all, we need to create a Table-Valued Parameter type:
CREATE TYPE [dbo].[tvpStringTable] AS TABLE(Value varchar(max) NOT NULL)
Then, we need a function to get a value of a specific asset:
CREATE FUNCTION [dbo].[tvfGetAssetValue]
(
#assetName varchar(max)
)
RETURNS TABLE
AS
RETURN
(
-- Add the SELECT statement with parameter references here
SELECT 0 AS AssetValue
UNION
SELECT 5 AS AssetValue
UNION
SELECT 7 AS AssetValue
)
Next, a function to return a list AssetName, AssetValue for assets list:
CREATE FUNCTION [dbo].[tvfGetAllAssets]
(
#assetsList tvpStringTable READONLY
)
RETURNS TABLE
AS
RETURN
(
-- Add the SELECT statement with parameter references here
SELECT al.Value AS AssetName, av.AssetValue FROM #assetsList al
CROSS APPLY tvfGetAssetValue(al.Value) AS av
)
Finally, we can test it:
DECLARE #names tvpStringTable
INSERT INTO #names VALUES ('name1'), ('name2'), ('name3')
SELECT * FROM [Test].[dbo].[tvfGetAllAssets] (#names)
In MSSQL 2000 I would make #allAssetsList a Varchar comma separated values list. (and keep in mind that maximum length is 8000)
I would create a temporary table in the memory, parse this string and insert into that table, then do a simple query with the condition where assetName in (select assetName from #tempTable)
I wrote about MSSQL 2000 because I am not sure whether MSSQL 2005 has some new data type like an array that can be passed as a literal to the SP.