Error message:
Warning : SQM1014: Unable to extract function 'dbo.ProductFamilyIndex_EN' from SqlServer. Null or empty full-text predicate.
function defined as:
CREATE FUNCTION [dbo].[ProductFamilyIndex_EN]
(
#topn int,
#keywords nvarchar(4000)
)
RETURNS TABLE
AS
RETURN
(
select top (#topn) ProductFamilyID
from (
select pf.ProductFamilyID, t.[RANK] as _rank
from containstable(ProductFamily, (Name_EN), #keywords, LANGUAGE 'English', #topn) t
inner join ProductFamily pf on(pf.ProductFamilyID=t.[KEY])
union all
select p.ProductID as ProductFamilyID, t.[RANK] as _rank
from containstable(Product, (LongDescription_EN, ShortDescription_EN), #keywords, LANGUAGE 'English', #topn) t
inner join Product p on(p.ProductID=t.[KEY] and p.ProductFamilyID is null and p.Deleted is null)
) t
group by ProductFamilyID
order by max(_rank) desc
)
don't get confused by the union inside - that just means that a product without a family is a family on its own.
tried to give default values to the parameters:
#topn int = 1000,
#keywords nvarchar(4000) = 'test'
with the same result.
Using .NET 3.5 and sql2008.
As you mentioned, SQLMetal needs a return type.
Another way to solve this, is to explicitly set your default inside the stored procedure:
SET #topn = COALESCE(#topn, 1000)
Throw that before the SELECT statement to insure that any NULL parameters will return a valid value.
This is useful not only for SQLMetal, but for anyone who uses this function.
Reposting my own answer properly so I can close this thread.
Problem solved.
Apparently sqlmetal runs the function to figure out the return type, but insists on supplying null parameter instead of default, which seems like sqlmetal's bug.
Way to work around it is to declare return type explicitly:
alter function [dbo].[ProductFamilyIndex_EN] (#topn int, #keywords nvarchar(4000))
returns #t table (ProductFamilyID int not null)
as begin
...
return end
Related
I have trouble using table type as parameter in stored procedure.
This is my table type:
CREATE TYPE [dbo].[ TableTypeMyValue] AS TABLE(
[MyValue] [nvarchar](50) NULL
)
GO
I need #MyValue to be passed from c#:
#BCID nvarchar(20),
#MyValue TableTypeMyValue READONLY
AS
BEGIN
SET NOCOUNT ON
SELECT IIF (EXISTS (SELECT bcc.BCID, ocv.CAID, ocv.CAValue
FROM BCTWO AS ocv
INNER JOIN BCONE AS bcc ON bcc.MyValue = ocv.MyValue
WHERE ocv.MyValue = #MyValue AND bcc.[Part of the key] = 1 AND bcc.BCID = #BCID), 1, NULL)
END
This is error that i got:
`Must declare the table variable #MyValue`.
I think that I have some problem with table type parameter. I'm not using it correctly. Can someone help ?
Thanks in advance!
You need to treat #CharacteristicValue as a table, not the same as a scalar variable. You should probably join to it and have ocv.CharacteristicValue = #CharacteristicValue instead be an ON condition that references the CharacteristicValue column within the table.
You're trying to use a table type parameter like a scalar parameter, specially:
ocv.CharacteristicValue = #CharacteristicValue
You need to either use a JOIN or an EXISTS clause. For example:
WHERE EXISTS (SELECT 1
FROM #CharacteristicValue CV
WHERE CV.CharacteristicValue = ocv.CharacteristicValue)
I'm writing a stored procedure with quite a lot of expensive work to do that may or may not take a filter parameter. Doing the filtering is itself quite expensive, and the table being filtered is large. I just tried to change the inner filtering function so throw an error if called with invalid parameters, as a warning to developers not to use it that way.
BUT - If I call my outer test function with NULL, it works as I'd expect, not calling the inner function and not throwing the error. If I call my outer test function with a variable with the VALUE of NULL, then it calls the filter function with a null parameter, and throws the error, even thought the code only says to call the function when the value is not null.
What's going on here?
Much simplified example:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[MyTable]') AND type in (N'U')) DROP TABLE MyTable
GO
CREATE TABLE MyTable (Pk int, Field int)
GO
INSERT INTO MyTable VALUES (1, 1)
INSERT INTO MyTable VALUES (2, 4)
INSERT INTO MyTable VALUES (3, 9)
INSERT INTO MyTable VALUES (4, 16)
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[FilterRows]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) DROP FUNCTION FilterRows
GO
CREATE FUNCTION FilterRows(#searchParameter int)
RETURNS #Pks TABLE
(
Pk int
)
AS
BEGIN
IF (#searchParameter IS null)
BEGIN
-- This is bad news. We don't want to be here with a null search, as the only thing we can do is return every row in the whole table
-- RAISERROR ('Avoid calling FilterRows with no search parameter', 16, 1)
-- we can't raise errors in functions!
-- Make it divide by zero instead then
INSERT INTO #Pks SELECT Pk FROM MyTable WHERE 1/0 = 1
END
ELSE
BEGIN
INSERT INTO #Pks SELECT Pk FROM MyTable WHERE Field > #searchParameter
END
RETURN
END
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[OuterFunction]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) DROP FUNCTION OuterFunction
GO
CREATE FUNCTION OuterFunction(#searchParameter int)
RETURNS TABLE AS
RETURN
SELECT *
FROM
MyTable
WHERE
(#SearchParameter IS NULL) OR (#searchParameter IS NOT NULL AND Pk IN (SELECT Pk FROM dbo.FilterRows(#searchParameter)))
GO
SELECT * FROM dbo.OuterFunction(2) -- Returns filtered values
SELECT * FROM dbo.OuterFunction(null) -- returns everything, doesn't call FilterRows
DECLARE #x int = null
SELECT * FROM dbo.OuterFunction(#x) -- WTF! Throws error!
The difference when a value null is passed than when constant null is passed is the same difference between using (is Null) and (= null)
#var = null -- considered as false
#var is null -- considered as unknown
for more details : SQL is null and = null
so if you want to make behavior of both (calling constant null & pass Null value) is the same, use the following tricky although I don't prefer this one.
Alter FilterRows function to be
IF (#searchParameter = null)
--IF (#searchParameter is null)
Note: sorry for typing this answer here, it is supposed to be comment instead of answer, the rule is "You must have 50 reputation to comment" and I have only 22 :(
I think what's going on is that in
SELECT * FROM MyTable WHERE (#SearchParameter IS NULL) OR
(#searchParameter IS NOT NULL AND Pk IN (SELECT Pk FROM dbo.FilterRows(#searchParameter)))
The query analyzer can see that the subquery
(SELECT Pk FROM dbo.FilterRows(#searchParameter))
does not depend on any values from MyTable. As it's constant for all rows, it runs that subquery first, in order to join MyTable to the results. So it executes it before evaluating the WHERE clause where it tests whether #searchParameter IS NULL or not.
When #searchParameter is just "NULL" and not a variable with value NULL, then the analyzer can short-circuit the whole where clause in the execution plan and so knows not to pre-calculate the subquery.
Or, something like that.
I created a user-defined function in SQL Server 2012 that returns XML. I would like to call the function in a SELECT statement. Is this possible?
When I try doing it, I get the error:
The FOR XML clause is not allowed in a ASSIGNMENT statement.
I want the SELECT statement to return a set of these named methods that have dependencies of other named methods within their logic.
In the main CTE, I get the latest versions of methods that have dependencies. The UDF goes thru the logic of each method and returns any methods called within it. So, I want to call the UDF in the SELECT statement and return XML of the dependent method names.
The function works and returns XML data. This is the function:
ALTER FUNCTION [dbo].[GetCalledMLMs]
(
-- Add the parameters for the function here
#MLM_Txt nvarchar(MAX)
)
RETURNS XML
AS
BEGIN
-- Declare the return variable here
DECLARE #CalledMLMs XML
Declare #MLMTbl table (pos int, endpos int, CalledMLM nvarchar(200))
--Logic to get the data...
Select #CalledMLMs = CalledMLM from #MLMTbl FOR XML PATH
-- Return the result of the function
RETURN #CalledMLMs
END
This is the CTE that calls the UDF.
;with cte as
(
select distinct Name, max(ID) as LatestVersion
from MLM_T
where Logic like '%:= MLM %' and Logic not like '%standard_libs := mlm%'
group by Name
)
select MLM2.Name, LatestVersion,
dbo.GetCalledMLMs(MLM2.Logic) as CalledMLMs
from cte join MLM_T MLM2 on cte.Name = MLM2.Name
and cte.LatestVersion = MLM2.ID
and MLM2.Active = 1 and MLM2.Status in (3, 4)
When running this query I get the error that XML is not allowed to be used in assignment statement.
Is there any way to call a function in the SELECT statment that returns an XML data type?
If you want to set a variable to a value you have to use SET and a scalar value on the right side.
The syntax SELECT #SomeVariable=SomeColumn FROM SomeTable is not possible with FOR XML (and rather dangerous anyway...), because the XML is not a column of the SELECT but something after the process of selecting.
Your problem is situated here:
Select #CalledMLMs = CalledMLM from #MLMTbl FOR XML PATH
Try to change this to
SET #CalledMLMs = (SELECT CalledMLM FROM #MLMTbl FRO XML PATH);
I solved the problem by changing the function to return a table, not XML.
So it looks like this:
FUNCTION [dbo].[GetCalledMLMsTbl]
(
-- Add the parameters for the function here
#MLM_Txt nvarchar(MAX)
)
--RETURNS XML
RETURNS #MLMTbl TABLE
(
pos int,
endpos int,
CalledMLM nvarchar(200)
)
AS
BEGIN
--logic here
insert into #MLMTbl (pos, endpos, CalledMLM) Values (#startpos, #endpos, #MLM_name)
RETURN
END
Then I called the function in the 'from' clause in the select
;with cte as
(
select distinct Name, max(ID) as LatestVersion
from CV3MLM
where Logic like '%:= MLM %' and Logic not like '%standard_libs := mlm%'
--and Name not like '%V61_CCC'
group by Name
)
select MLM2.Name, LatestVersion, C.CalledMLM
from cte join MLM_tbl MLM2 on cte.Name = MLM2.Name and cte.LatestVersion = MLM2.ID
and MLM2.Active = 1 and MLM2.Status in (3, 4)
cross apply dbo.GetCalledMLMsTbl(MLM2.Logic) C
order by MLM2.Name, LatestVersion
I Waint run code in SQL SERVER
ALTER FUNCTION return_table (#table nvarchar(250))
RETURNS TABLE
AS
RETURN
(
SELECT * FROM #table
)
none using PROCEDURE . THANK FOR HELP
The only way to make this work is truly horrible for both aesthetic and (probably) performance reasons. It also assumes that all tables that might be passed as parameters have the same structure:
ALTER FUNCTION return_table (#table sysname)
RETURNS TABLE
AS
RETURN
(
SELECT * FROM TableA where #table = 'TableA'
UNION ALL
SELECT * FROM TableB where #table = 'TableB'
UNION ALL
SELECT * FROM TableC where #table = 'TableC'
/* add more UNIONs and SELECTs for each table that you want to use */
)
A function requires an explicit definition of the return value type, e.g. the columns being returned. The SQL statement you provided will not work in such a manner for the following reasons:
ALTER FUNCTION return_table (#table nvarchar(250)) -- You have declared a parameter of type table, this is not the right way of doing this
RETURNS TABLE -- you haven't defined the structure of the table being returned, this needs explicitly defining
AS
RETURN
(
SELECT * FROM #table -- using SELECT * is bad practice as the structure of the table may change and then conflict with the structure of the table being returned
)
The first part of the problem is declaring a parameter of type TABLE; this question has good examples on how to get around doing this. Quick summary: you need to declare the table as a type before passing in the type as the parameter to your function:
CREATE TYPE MyTableParameterDefinition AS TABLE (
[ColumnName] NVARCHAR(250) NULL
)
This type can then be passed as a parameter to your function:
CREATE FUNCTION myFunctionName (
#TableVariable MyTableParameterDefinition READONLY
)...--INSERT CODE HERE--
I'm uncertain whether you can use a similar method for the return type and would suggest against this given the implication of the contract defined by the function. Better practice would be to define the structure of the table being returned and explicitly select the columns from the table:
ALTER FUNCTION return_table (
#table MyTableParameterDefinition
)
RETURNS TABLE
(
-- Explicitly define columns returned by the function
[ColumnName] NVARCHAR(250) NULL
)
AS
RETURN
(
SELECT
[ColumnName]
FROM
#table
)
I am a newbie in PostgreSQL (using v9.0) and wanted to know if using return next and Return query in the same function is possible without exiting the function before attaching the results in the resultset?
CREATE TYPE return_type AS
(paramname character varying,
value character varying);
CREATE OR REPLACE FUNCTION myfuntion(param1 character varying)
RETURNS SETOF return_type AS
declare
r return_type;
message varchar;
status integer;
$BODY$
BEGIN
o_call_status := 0;
o_call_message := '';
Return query Select 'mystatus' as paramName, status::varchar as value;
Return query Select 'mymessage' as paramName, message as value;
for r in SELECT 'mycolumnname1' as paramName,mycolumn1 as value FROM tb1
WHERE column1 = val
UNION ALL
SELECT 'mycolumnname2' as paramName,mycolumn2 as value FROM tb1
WHERE column1 = val
UNION ALL
SELECT 'mycolumnname3' as paramName,mycolumn3 as value FROM tb2
WHERE column1 = val1 AND
column4 = val4 loop
return next r;
end loop;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
Returning from a plpgsql function
You can mix RETURN NEXT and RETURN QUERY freely and repeatedly. The underlying principle is that plpgsql builds the table locally and does not return until the function is finished:
Per documentation:
Note: The current implementation of RETURN NEXT and RETURN QUERY
stores the entire result set before returning from the function, as discussed above.
That means you can even raise en exception to abort the operation if you are unsatisfied with the results so far and the client won't see a thing. We also included an example in the manual demonstrating this, just above said quote.
Cross tabulation
As for what you are trying do achieve, consider the crosstab() function for the tablefunc extension. Related answer with sample details:
PostgreSQL Crosstab Query
I'm unsure if you can mix the two (try it…).
That said, it'll be much more efficient in you use-case to write a single query with a union all clauses that returns all rows directly:
return query
select 'mystatus' as paramName, status::varchar as value
union all
select 'mymessage' as paramName, message as value
union all
SELECT 'mycolumnname1' as paramName,mycolumn1 as value
FROM tb1
WHERE column1 = val
union all
…
You might also find this contrib module helpful, btw:
http://www.postgresql.org/docs/current/static/tablefunc.html
And, if possible, revisit your schema or the way you use it, so you don't need this kind of function to begin with — set returning functions that return potentially large sets can be particularly inefficient. In your case, it seems like you want three columns. Why not simple use?
select col1, col2, col3 from tbl