I have created a SQL Server function to return results based on two filter conditions. In TVF function I just have one select query where the results will be returned in table format. When I run the select query expected results were returned but when I run the same query through function then results are incorrect. Please advise as what is happening in SQL for TVF.
Function is not returning the expected results when we pass more values in parameters.
Sample table data:
answer_tag answer_text
-----------------------
TNBPDS1 250
TSMMBPDS 100
My code:
Create function [dbo].[OBSLookupByAnswerTextTagX]
(#pObsId int,
#pObsSeq tinyint,
#pAnswerTag varchar(10))
return #ReturnTable table
(
id int identity(1,1),
answer_text varchar(max),
answer_tag varchar(max)
)
begin
Insert into #ReturnTable (answer_text, answer_tag)
select distinct ansr.answer_text, ansr.answer_tag
from grp_bu_bs gbus
inner join benefit_summary bsum on (gbus.summary_id = bsum.summary_id)
inner join answer ansr on (gbus.summary_id = ansr.summary_id)
where
gbus.grp_prod_id = #pObsId
and gbus.grp_prod_seq = #pObsSeq
and #pAnswerTag like '%' + (ansr.answer_tag) + '%'
return
end
Below select query from function which gives expected result when we run the query as separate SELECT statement.
select distinct ANSR.answer_text,ANSR.answer_tag
from grp_bu_bs GBUS
inner join benefit_summary BSUM on (GBUS.summary_id = BSUM.summary_id)
inner join answer ANSR on (GBUS.summary_id = ANSR.summary_id)
where
GBUS.grp_prod_id = '189523'
and GBUS.grp_prod_seq = '31'
and 'TNBPDS1,TSMMBPDS,TNBPDS12,TNBPDSL3,TNBPDSD,TNBPDSAL' like '%' + ANSR.answer_tag) + '%'
If we have tag 'TNBPDS1' then 250 should be returned.
If we pass parameter as 'TNBPDS1,TSMMBPDS,TNBPDS12,TNBPDSL3,TNBPDSD,TNBPDSAL' results are returned as expected.
But if we pass parameter as 'TSMMBPDS,TNBPDS1,TNBPDS12,TNBPDSL3,TNBPDSD,TNBPDSAL' then no results are returned.
Related
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 am creating a report in SSRS that returns a bunch of data, one of the columns I need to return is a value from Active Directory. I have a query which gets a value that I want to pass into a subquery which looks a value up in Active Directory.
What I have so far looks something like this (simplified):
DECLARE #UserID int
SELECT #UserID = UserID FROM dbo.Users As TEST WHERE (RecordID = 123456)
(SELECT displayName FROM OpenQuery (
ADSI,
'SELECT displayName
FROM ''LDAP://DC=company,DC=local''
WHERE objectCategory=''user'' AND extensionattribute5='' +#UserID+ ''
') ) AS LoggedBy
FROM dbo.UserRecords WHERE UserID = 1
The problem is however when I use #UserID in the subquery the output alias "LoggedBy" always returns "Null". If I take the parameter out and manually type the value that the parameter has, it works fine.
It seems like I cannot get the #UserID paramter from the main query into the sub-query. ust wondering, what am I missing here?
You can't send variables into OpenQuery context in this way. Try to filter the query "outside":
DECLARE #UserID int
SELECT #UserID = UserID FROM dbo.Users As TEST WHERE (RecordID = 123456)
(SELECT t.displayName FROM OpenQuery (
ADSI,
'SELECT displayName, extensionattribute5
FROM ''LDAP://DC=company,DC=local''
WHERE objectCategory=''user''
') as t
WHERE t.extensionattribute5=#UserID ) AS LoggedBy
FROM dbo.UserRecords WHERE UserID = 1
From MSDN
OPENQUERY does not accept variables for its arguments.
I am wanting to build a query that looks at an existing Models table.
Table Structure:
ModelID | ManufacturerID | CategoryID | ModelName
What I want to do is pass two things to the query, ModelID and ModelName, so that it returns the specific model and also similar models.
ModelName could be made up of several words e.g iPhone 5s 16GB, so what I would like my query to do is:
SELECT
M.*
FROM
Models AS M
WHERE
(M.ModelID = 1840 OR M.ModelName LIKE '%iPhone%'
OR M.ModelName LIKE '%5s%' OR M.ModelName LIKE '%16GB%')
Is there a way that I can pass the ModelName to the query as a string and then have the query split the string to generate the OR statements?
Do a web search for T-SQL split function. There are loads out there. They take a string (comma-delimited or space delimited or whatever) and return a table of values. Then just do a JOIN against that result set.
SELECT DISTINCT M.*
FROM Models AS M
JOIN dbo.fn_split(#model_name, ' ') AS model_names
ON M.ModelID = #model_id OR m.ModelName LIKE '%' + model_names.value + '%';
OK, so I managed to get this working, following the advice given by Kevin Suchlicki re. fn_Split.
I have made this function even more complex than i intended to, but in order to help others out in a similar situation, here is my final solution:
DECLARE #CategoryID int = 1
DECLARE #ManufacturerID int = 3
DECLARE #ModelName varchar(100) = 'iPhone 5s 16GB'
DECLARE #ModelID int = 1840
DECLARE #Carrier varchar(10) = NULL
DECLARE #Colour varchar(10) = NULL
SELECT
I.*
FROM
(
SELECT
DISTINCT M.*
FROM
Models AS M
JOIN
dbo.fn_Split(#ModelName,' ') AS N
ON M.ModelID = #ModelID OR lower(M.ModelName) LIKE '%'+ Lower(N.value) + '%'
WHERE
M.CategoryID = #CategoryID AND M.ManufacturerID = #ManufacturerID
) AS A
LEFT OUTER JOIN
Items AS I ON A.ModelID = I.ModelID
WHERE
I.Barred <> 1
AND I.Locked <> 1
AND I.Ber <> 1
AND I.Condition = 'Working'
AND (LOWER(I.Colour) = LOWER(ISNULL(#Colour, I.Colour)) OR I.Colour IS NULL)
AND (LOWER(I.Carrier) = LOWER(ISNULL(#Carrier, I.Carrier)) OR I.Carrier IS NULL)
I will now create this as a stored procedure to complete the job.
For reference, HERE is a link to the fn_Split function.
I'm trying to output the query results from one table and using that same variable as a 'like' in another table.
The first part of the query is supposed to get me everything *. The variable (or array) will be used in the second part of the query as part of 'like'.
This is what I have:
DECLARE #UpnPref nvarchar(100), #WC nvarchar(10)
Set #UpnPref = 0
Set #WC = '*'
Select #upnpref = UpnPrefix from dbo.ADUsers where UPNPrefix = #WC
print #upnpref <-- returns a 0
Select * from dbo.UserMailbox where LinkedAccount like '%' + #UpnPref +'%'
If what you're trying to do is select everything from dbo.UserMailbox that has LinkedAccount matching UpnPrefix of every record in dbo.ADUsers, you can do it so:
SELECT U.*
FROM dbo.UserMailbox U
INNER JOIN dbo.ADUsers A ON U.LinkedAccount = A.UpnPrefix
I have created a query which performs with aprox 2 seconds with top 100. If i create a stored procedure of this exact query it takes 12-13 seconds to run.
Why would that be?
Elements table count = 2309015 (with userid specified = 326969)
Matches table count = 1290 (with userid specified = 498)
sites table count = 71 (with userid specified = 9)
code
with search (elementid, siteid, title, description, site, link, addeddate)
as
(
select top(#top)
elementid,
elements.siteid, title, elements.description,
site =
case sites.description
when '' then sites.name
when null then sites.name
else sites.name + ' (' + sites.description + ')'
end,
elements.link,
elements.addeddate
from elements
left join sites on elements.siteid = sites.siteid
where title like #search and sites.userid = #userid
order by addeddate desc
)
select search.*, isnull(matches.elementid,0) as ismatch
from search
left join matches on matches.elementid = search.elementid
When you create SP it is compiled and stored and when the SP has parameters, by which you filter your result, the optimizer don't know which value you will pass on execution, then he treat as 33% selection and by this creates plan. When you execute query, the values are provided and optimizer create the execution plan depended on this values. I sure, the the plans are different.
Without code, I can only guess. When writing a sample query, you first have a constant where clause and second a cache. The stored procedure has no chance of either caching or optimizing the query plan based on a constant in the where clause.
I can suggest two ways to try
First one, write your sp like this:
create procedure sp_search
(
#top int,
#search nvarchar(max),
#userid int
)
as
begin
declare #p_top int, #p_search nvarchar(max), #p_userid int
select #p_top = #top, #p_search = #search, #p_userid = #userid
with search (elementid, siteid, title, description, site, link, addeddate)
as
(
select top(#p_top)
elementid,
elements.siteid, title, elements.description,
site =
case sites.description
when '' then sites.name
when null then sites.name
else sites.name + ' (' + sites.description + ')'
end,
elements.link,
elements.addeddate
from elements
left join sites on elements.siteid = sites.siteid
where title like #p_search and sites.userid = #p_userid
order by addeddate desc
)
select search.*, isnull(matches.elementid,0) as ismatch
from search
left join matches on matches.elementid = search.elementid
end
Second one, use inline table function
create function sf_search
(
#top int,
#search nvarchar(max),
#userid int
)
returns table
as
return
(
with search (elementid, siteid, title, description, site, link, addeddate)
as
(
select top(#top)
elementid,
elements.siteid, title, elements.description,
site =
case sites.description
when '' then sites.name
when null then sites.name
else sites.name + ' (' + sites.description + ')'
end,
elements.link,
elements.addeddate
from elements
left join sites on elements.siteid = sites.siteid
where title like #search and sites.userid = #userid
order by addeddate desc
)
select search.*, isnull(matches.elementid,0) as ismatch
from search
left join matches on matches.elementid = search.elementid
)
There is a similar question here
The problem was the stored proc declaration SET ANSI_NULLS OFF