Subquery as parameter into SQL Server UDF - sql

My question is similar to this.
I made a scalar function like follows:
CREATE FUNCTION [dbo].[MyFunction](#table [TableModel] READONLY)
RETURNS DECIMAL(18, 6) AS
BEGIN
DECLARE #sql NVARCHAR(MAX), #params NVARHCAR(MAX), #value DECIMAL(16, 8);
SELECT #sql = formula, #params = params FROM formulas WHERE id = (SELECT TOP 1 id_formula FROM #table)
EXEC sp_executesql #sql, #params, #table=#table, #value=#value OUTPUT;
RETURN #value;
END
Where a formula SQL could be something like:
SELECT #value = SUM(value) / AVG(value) FROM #table
And it could have more columns if needed.
The model table looks like so:
CREATE TYPE [dbo].[TableModel] AS TABLE(
[formula] INT NOT NULL,
[value] DECIMAL(16, 8) NOT NULL
)
And I want to use it like so:
SELECT od.id, od.col1, od.col2,
dbo.MyFunction((SELECT id.formula, id.value FROM #raw_data id WHERE id.id = od.id)) as result
FROM #data od
GROUP BY od.id, od.col1, od.col2
Where the ID unique and multiple rows will have the same id.
Basically, what I'm trying to do in a single query I want to call a function that has a table parameter. But I want this table to be a subquery.
I'm aware that you can call the function with a table variable as mentioned in this answer.
Is this possible in any way? I'm having this error at executing:
Msg 116, Level 16, State 1, Line 30
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.

Related

Executing select statement as variable from TVF

I have to get a list of results from a Table value function from a variable. I have done something like this:
DECLARE #Date char(8) = '20200508'
DECLARE #Type varchar(100) = 'Inbound'
DECLARE #Offset INT = 3600
DECLARE #EmployeeID INT = null
DECLARE #TypeFunc as varchar(max)
SET #TypeFunc= N'select EmpID, Callcount from dbo.fn_' + #Type + '('''+ #Date +''','+ CAST(#Offset as Varchar(100))+','+ CAST(#EmployeeID as varchar(100))+')';
EXEC (#TypeFunc)
I expect to see a list of results as if I'm doing a normal select query, however, it is just coming back with 'Commands completed successfully.' in the results grid, which doesn't seem like its doing it correctly.
The query it should run should look like
Select EmpID, Callcount From dbo.fn_Inbound('20200508', 3600, null)
Anything I'm missing here?
I found 2 mistakes in your Query:
1.) Use CONCAT instead of + because if any of your concatenating string is null it makes the whole Concatenation as NULL (For your case EmpID is null it will makes the Whole Query as null by using +)
2.)ISNULL(CAST(#EmployeeID as varchar(100)),'NULL') Use ISNULL fn to pass as null for that Parameter in your function
SET #TypeFunc= CONCAT(N'select EmpID, Callcount from dbo.fn_' , #Type , '(''', #Date
,''',', CAST(#Offset as Varchar(100)),',',ISNULL(CAST(#EmployeeID as
varchar(100)),'NULL'),')');

Passing a table variable to a stored procedure

I am seeking to create a procedure that I can pass a one column table, and the procedure will output the median. Right now I have a procedure that will determine the median; however, I am getting errors that my #table table variable has not been declared and that the stored procedure could not be found.
My median procedure:
CREATE OR ALTER PROCEDURE dbo.median
(#table NUMERIC,
#median FLOAT OUTPUT)
AS
DECLARE #size AS NUMERIC
SET #size = (SELECT COUNT(*) FROM #table)
SET #median = (SELECT AVG(1) FROM (SELECT *, ROW_NUMBER() OVER(ORDER BY 1) AS ROW FROM #table) AS subquery
WHERE subquery.ROW = ROUND(#size / 2, 0) OR subquery.ROW = ROUND(#size / 2, 0, 1))
RETURN
GO
Calling the procedure:
DECLARE #Arsenic TABLE(Ar FLOAT)
INSERT INTO #Arsenic SELECT Arsenic from dbo.HubspotWaterTestAverages
EXEC dbo.median (SELECT Arsenic FROM dbo.HubspotWaterTestAverages)
NOTE: Arsenic represents the Arsenic level results from water tests, and the values range from null to 10
The working procedure is expected to just return the median value for the column, and later on I am planning on cross joining that to a master table. Thank you for any help!
This may help. Except table variable have to use Table Type.
CREATE TABLE Employee
(
EmpId int NOT NULL,
EmployeeName nvarchar(MAX),
)
GO
CREATE TYPE EmployeeType AS TABLE
(
EmpId int NOT NULL,
EmployeeName nvarchar(MAX)
)
GO
CREATE PROCEDURE PassTableTypeIntoProcedure(#EmployeeType EmployeeType READONLY)
AS
BEGIN
INSERT INTO Employee
SELECT * FROM #EmployeeType
END
GO
DECLARE #EmployeeTypeVariable AS EmployeeType
INSERT INTO #EmployeeTypeVariable VALUES
(1,'A'),
( 2,'B')
EXEC PassTableTypeIntoProcedure #EmployeeTypeVariable
GO
SELECT * FROM Employee

Stored procedure returning result even thought it's not supposed to

I have a stored procedure which looks like following:
ALTER PROCDURE [dbo].[zsp_selectallupceans_listProduction]
(#UPCList NVARCHAR(4000),
#EANList NVARCHAR(4000),
#Type TINYINT)
AS
SELECT
dd.UPC, dd.EAN, dd.EBAYID AS ItemID
FROM
ThirdPartyData AS dd
WHERE
EXISTS (SELECT 1 FROM dbo.SplitStringProduction(#UPCList,',') S1
WHERE dd.UPC = S1.val)
OR EXISTS (SELECT 1 FROM dbo.SplitStringProduction(#EANList,',') S2
WHERE dd.EAN = S2.val)
AND dd.Type = #Type
The parameters are passed like following:
#UPCList='709127309019',
#EanList='0709127309019',
#Type=4
The "SplitStringProduction" function looks like this:
ALTER FUNCTION [dbo].[SplitStringProduction]
(#string NVARCHAR(MAX),
#delimiter NVARCHAR(5))
RETURNS #t TABLE
(
val NVARCHAR(500)
)
AS
BEGIN
DECLARE #xml XML
SET #xml = N'<root><r>' + replace(#string,#delimiter,'</r><r>') + '</r></root>'
INSERT INTO #t(val)
SELECT
r.value('.','varchar(500)') AS item
FROM
#xml.nodes('//root/r') as records(r)
RETURN
END
Now when I do a simple select from my table like following:
select *
from thirdpartydata dd
where dd.UPC = '709127309019' -- note this is one of the parameters passed to the stored procedure...
I will get only 1 result with a column Type set to "1"....
Now when I try out my stored procedure:
exec zsp_selectallupceans_listProduction '709127309019','0709127309019',4
I still get 1 result, even though I'm not supposed to get any result, because if you can see the "Type" parameter is set to 4, thus no matching records should be found....
What am I doing wrong here, I can't seem to figure it out ??
You need to enclose the OR condition inside parenthesis:
WHERE (
EXISTS (SELECT 1 FROM dbo.SplitStringProduction(#UPCList,',') S1 WHERE dd.UPC=S1.val)
OR EXISTS (SELECT 1 FROM dbo.SplitStringProduction(#EANList,',') S2 WHERE dd.EAN=S2.val)
) AND dd.Type = #Type
Without them your query like this:
WHERE EXISTS (...)
OR (EXISTS (...) AND dd.Type = #Type)
And the result your get is because the first OR condition matches.

different results of IN Condition

Don't quite understand IN statement. First variant works fine:
select manufacturers.id
from manufacturers
where manufacturers.id in (select manufacturerId
from pcs group by manufacturerId
having count(manufacturerId) > 1)
But when I make subquery a procedure:
CREATE PROCEDURE [dbo].Get_manufacturers #productType varchar(50)
as
begin
declare #query varchar(500)
set #query='select manufacturerId from ' + QuoteName(#productType) + '
group by manufacturerId having count(manufacturerId) > 1'
declare #t table (manufacturerId int)
insert into #t exec(#query)
select manufacturerId from #t;
end
select manufacturers.id
from manufacturers
where manufacturers.id in (Get_manufacturers 'pcs')
I get an error: Msg 102, Level 15, State 1, Line 4
Incorrect syntax near 'pcs'
Get_manufacturers 'pcs' works properly. Where am I wrong?
Don't quite understand IN statement
...
Get_manufacturers 'pcs' works properly - it returns a table
You misunderstand both stored procedures and IN condition.
From IN (Transact-SQL):
test_expression [ NOT ] IN
( subquery | expression [ ,...n ]
)
What the stored procedure returns is not a subquery, neither it's an expression.
Here is a link to understand what subquery is Using a Subquery in a T-SQL Statement
A subquery is a SELECT statement that is nested within another T-SQL
statement
So stored procedure is not a subquery, it's just not a SELECT statement.
But even when you say that stored procedures returns a table it's wrong: you can JOIN a table to another table but you cannot join the result of stored procedure.
And even if you "see" the result set returned by a procedure as a "table" it's not a table.
Based on Rokuto and Gordon Linoff suggestions, Alter the procedure by omitting the table declaration:
ALTER PROCEDURE [dbo].Get_manufacturers #productType nvarchar(50)
as
begin
declare #query nvarchar(500)
set #query= N'select manufacturerId from ' + QuoteName(#productType) + '
group by manufacturerId having count(manufacturerId) > 1'
---declare #t table (manufacturerId int)
---insert into #t exec(#query)
---select manufacturerId from #t;
exec(#query)
end
GO
Then, Use a temporary table to fill in the results of the stored procedure.
IF(OBJECT_ID('tempdb..#tmp_manufacturers') IS NOT NULL)
BEGIN
DROP TABLE #tmp_manufacturers
END
CREATE TABLE #tmp_manufacturers
(
manufacturerId int
)
INSERT INTO #tmp_manufacturers (manufacturerId)
EXEC dbo.Get_manufacturers 'pcs'
lastly, add it to your IN condition.
select m.id
from manufacturers M
where m.id IN (select t.manufacturerId From #tmp_manufacturers T)
As #Gordon Linoff said, procedures does not return tables.
But, if you want to store output data from stored procedures, you need to put it into table e. g.:
DECLARE #manufactures TABLE (Id int)
INSERT INTO #manufactures
exec Get_manufacturers 'pcs'
select manufacturers.id
from manufacturers
where manufacturers.id IN (SELECT Id FROM #manufactures)
Instead of table variable, you can use temporary table.

Removing ' ' from sql query and stored proc

I have a table in which a column named data is of type varbinary . If I do a simple query
select * from tab where data = 1 then it works but if I do select * from tab where data = '1' then it does not return any row. The issue comes when I create a stored proc to retrieve data from this table and it converts the query and adds ' ' in the parameter when querying and so I am not able to retrieve any data. Can some one please tell me how to get around this issue.
Parameters
#ID INT = NULL
,#Data varchar(100) = NULL
CREATE TABLE #Results (
ID INT
,Data varchar(100)
)
BEGIN
INSERT #Results (
ID
,Data
)
SELECT
SK.ID
,SK.Data
FROM dbo.tab SK
where SK.ID = #ID And SK.data = #data
END
SELECT #TotalRows = COUNT(*)
FROM #Results
SELECT #TotalRows TotalRows
Now from the code when I execute this statement
oReader = ExecuteReader(oConn, CommandType.StoredProcedure, "Proc", New SqlParameter("#ID", Request.ID), _
New SqlParameter("#Data", Request.Data))
I see in SQL Profiler that it runs the query as 'data'
which does not return any rows
Thanks
Since you have said that you have written an SP, I think the inpput parameter is specified as NVARCHAR or VARCHAR
Below is one way of doing but i'm guessing that the column called data will only have integer values in the first solution.
DECLARE #X VARCHAR(5)
SET #X = '1 '
SELECT CAST(#X AS INT)
The above is only if the Data column specified above is Integer.
If the same is string (VARCHAR) you can write a User defined function to do the same.
CREATE FUNCTION dbo.TRIM(#string VARCHAR(8000))
RETURNS VARCHAR(8000)
BEGIN
RETURN LTRIM(RTRIM(#string))
END
SELECT dbo.TRIM('1 ')
I hope the above was useful, I did get the idea rather copied the function from here
http://blog.sqlauthority.com/2007/04/24/sql-server-trim-function-udf-trim/