Why function won't call in SQL Server - sql

I'm trying to create / call a function on the Stack Exchange Data Explorer - I haven't done much SQL Server before but only MySQL.
Why won't it let me call this function I've just made?
-- Get Post with Best Comment on Site
-- Gets the Post with the Best Comment on the Site and Associated Data
CREATE FUNCTION typeOfPost
(#PostId int(11))
RETURNS varchar(30)
AS
BEGIN
declare #PostTypeId int(3)
select #PostTypeId = (SELECT PostTypeId FROM posts WHERE PostId = #PostId)
return (SELECT Name FROM PostTypes WHERE Id = #PostTypeId)
end
SELECT PostId, typeOfPost(PostId) AS [Post Type]
FROM comments
WHERE Score = (
SELECT max(Score)
FROM comments
);​​​​​​​​​​​
It gives:
"SELECT"."typeOfPost" is not a recognized built in function name.
So I looked at examples of function calls in SQL Server and I saw a lot had ".dbo" on the front. If I put that on I get this:
Incorrect syntax near the keyword 'SELECT'.
Can anyone explain what's wrong with my function?

I cleaned up your code so it's valid (no size on ints, correct column name for Posts.Id):
CREATE FUNCTION typeOfPost
(#PostId int)
RETURNS varchar(30)
AS
BEGIN
declare #PostTypeId int
select #PostTypeId = (SELECT PostTypeId FROM posts WHERE Id = #PostId)
return (SELECT Name FROM PostTypes WHERE Id = #PostTypeId)
end
​
And then you get:
Error: CREATE FUNCTION permission denied in database 'StackOverflow.Exported'.\
You can't create objects in StackOverflow Data Explorer. The best object creation you can hope for is table variables.
In addition, the reason you get the error is that when the batch is syntax checked, the function doesn't exist, so the second statement won't work. In traditional SSMS environment, you would create the function in a batch and then execute using the function in another batch. This can be done in a single file using the GO batch separator. This is a feature of SSMS and some other tools (and can be overridden in the options).
In addition, the problem you are trying to solve is not normally one solved with scalar functions (and certainly not ones which individually make trips to tables to retrieve data). Normally you would handle this very simply with a JOIN, which is a lot more accessible to the optimizer than a scalar function, which tend to be treated as block boxes.

Prefix the function with the function's schema name. Probably just dbo., unless you specified something different.
SELECT PostId, dbo.typeOfPost(PostId) AS [Post Type]
FROM comments
WHERE Score = (
SELECT max(Score)
FROM comments
);​​​​​​​​​​​

You can get the same results without the function call like this:
SELECT TOP 1 c.postId,Pt.Name as [Post Type]
FROM comments c
JOIN posts p ON c.PostID=p.postID
JOIN PostTypes Pt on Pt.postTypeID=p.PostTypeId
ORDER BY c.Score DESC
I think your error message is caused by the INT(xx) syntax, which SQL doesn't accept. Also, add the GO keyword between your function definition and your SELECT clause that uses the function...

Related

Select entries that fulfil requirements laid out by dynamic table

I'm attempting to do a keyword search which requires all conditions to be met for a result to be shown. I've created a method of making a custom table from a string which stores all of the keywords which are currently required for this search.
I've been able to get it to happen for 'or' using the following
dbo.MultipleTextSearchValuesOR - Is used to make the table of keywords
select Title from vwIncidentSearchView inner join dbo.MultipleTextSearchValuesOR('Testing|Check') on Title Like id
This works great but can't seem to work it out for 'and' (e.g. result must have 'Testing' and 'Check').
Any help would be appreciated
I've figured it out. The way it had to be done was using a cross apply with a function splitting the string into a table.
Then the entries must be grouped by all the fields. Finally they must be checked to see if it meets all the required keywords.
I do realise this is quite a messy method along with it not beingthe most optimized method so if anyone else has any suggestions to improving performance it would be appreciated
Example:
declare #where nvarchar(max)
declare #TitleLikeClause nvarchar(max)
declare #Title nvarchar(max)
set #Title = 'Down&Email'
SELECT
Referenceid,
Title
FROM vwIncidentSearchView
cross apply
(select
Data
from Split(#Title, '&') candidate
where
Title Like candidate.Data) t2o group by Referenceid, Title having count(*) = (select max(ID) from Split(#Title, '&'))

Semantic Search example from AdventureWorks database of SQL Server

I'm trying to implement semantic search of SQL Server. To see an example, I downloaded AdventureWorks2012 (An 'almost' step by step guide is here: https://learn.microsoft.com/en-us/sql/relational-databases/search/find-similar-and-related-documents-with-semantic-search?view=sql-server-2017). The error says that #CandidateID and #MatchedID have to be declared
I tried to declare those ID, instead of getting the error or a result I get an empty table.
SELECT TOP(10) KEY_TBL.matched_document_key AS Candidate_ID
FROM SEMANTICSIMILARITYTABLE
(
HumanResources.JobCandidate,
Resume,
#CandidateID
) AS KEY_TBL
ORDER BY KEY_TBL.score DESC;
GO
SELECT TOP(5) KEY_TBL.keyphrase, KEY_TBL.score
FROM SEMANTICSIMILARITYDETAILSTABLE
(
HumanResources.JobCandidate,
Resume, #CandidateID,
Resume, #MatchedID
) AS KEY_TBL
ORDER BY KEY_TBL.score DESC;
GO
You need to declare these variables and assign values. Only you know the data type of these keys.For example:
declare #CandidateID int = 6754,
#MatchedID int = 4321
IDs contain real ids from your indexed table.

SQL Variable not getting set

I might be missing something easy here, as someone relatively new to SQL, I am trying the following query:
DECLARE #uidd VARCHAR
SELECT #uidd = UID
FROM PRODUCTOBJECT WHERE EMAIL='abc#gmail.com';
SELECT ProductID FROM PRODUCTUIDMAPPING WHERE UID= #uidd;
And it is returning zero rows. What's puzzling is that if do not use variables and run both selects separately, I do get the right row back.
SELECT UID FROM TABLEA WHERE EMAIL='abc#gmail.com';
returns the UID, and then
SELECT ProductID FROM TABLEB WHERE UID='123456';
will return the ProductID value, where '123456' would be the value returned from the first query.
The problem is when combined together, using the variable for some reason it seems #uidd is not being set. Any reason why ?
I am running this on SQL Server 2008.
Presumably, uuid is longer than one character. Never use character types in SQL Server without a length. The default varies by context and may not be correct.
So, this should work:
DECLARE #uidd VARCHAR(255);
SELECT #uidd = UID
FROM PRODUCTOBJECT
WHERE EMAIL='abc#gmail.com';
SELECT ProductID
FROM PRODUCTUIDMAPPING
WHERE UID= #uidd;

SQL conversion from varchar to uniqueidentifier fails in view

I'm stuck on the following scenario.
I have a database with a table with customer data and a table where I put records for monitoring what is happening on our B2B site.
The customer table is as follow:
ID, int, not null
GUID, uniqueidentfier, not null, primary key
Other stuff...
The monitoring table:
ID, int, not null
USERGUID, uniqueidentifier, null
PARAMETER2, varchar(50), null
Other stuff...
In PARAMETER1 are customer guids as wel as other data types stored.
Now the question came to order our customers according their last visit date, the most recent visited customers must come on the top of a grid.
I'm using Entity Framework and I had problems of comparing the string and the guid type, so I decided to make a view on top of my monitoring table:
SELECT
ID,
CONVERT(uniqueidentifier, parameter2) AS customerguid,
USERguid,
CreationDate
FROM
MONITORING
WHERE
(dbo.isuniqueidentifier(parameter2) = 1)
AND
(parameter1 LIKE 'Customers_%' OR parameter1 LIKE 'Customer_%')
I imported the view in EF and made my Linq query. It returned nothing, so I extracted the generated SQL query. When testing the query in SQL Management Studio I got the following error:
Conversion failed when converting from a character string to uniqueidentifier.
The problem lies in the following snippet (simplified for this question, but also gives an error:
SELECT *,
(
SELECT
[v_LastViewDateCustomer].[customerguid] AS [customerguid]
FROM [dbo].[v_LastViewDateCustomer] AS [v_LastViewDateCustomer]
WHERE c.GUID = [v_LastViewDateCustomer].[customerguid]
)
FROM CM_CUSTOMER c
But when I do a join, I get my results:
SELECT *
FROM CM_CUSTOMER c
LEFT JOIN
[v_LastViewDateCustomer] v
on c.GUID = v.customerguid
I tried to make a SQL fiddle, but it is working on that site. http://sqlfiddle.com/#!3/66d68/3
Anyone who can point me in the right direction?
Use
TRY_CONVERT(UNIQUEIDENTIFIER, parameter2) AS customerguid
instead of
CONVERT(UNIQUEIDENTIFIER, parameter2) AS customerguid
Views are inlined into the query and the CONVERT can run before the WHERE.
For some additional discussion see SQL Server should not raise illogical errors

Is there any way of improving the performance of this SQL Function?

I have a table which looks something like
Event ID Date Instructor
1 1/1/2000 Person 1
1 1/1/2000 Person 2
Now what I want to do is return this data so that each event is on one row and the Instructors are all in one column split with a <br> tag like 'Person 1 <br> Person 2'
Currently the way I have done this is to use a function
CREATE FUNCTION fnReturnInstructorNamesAsHTML
(
#EventID INT
)
RETURNS VARCHAR(max)
BEGIN
DECLARE #Result VARCHAR(MAX)
SELECT
#result = coalesce(#result + '<br>', '') + inst.InstructorName
FROM
[OpsInstructorEventsView] inst
WHERE
inst.EventID = #EventID
RETURN #result
END
Then my main stored procedure calls it like
SELECT
ev.[BGcolour],
ev.[Event] AS name,
ev.[eventid] AS ID,
ev.[eventstart],
ev.[CourseType],
ev.[Type],
ev.[OtherType],
ev.[OtherTypeDesc],
ev.[eventend],
ev.[CourseNo],
ev.[Confirmed],
ev.[Cancelled],
ev.[DeviceID] AS resource_id,
ev.Crew,
ev.CompanyName ,
ev.Notes,
dbo.fnReturnInstructorNamesAsHTML(ev.EventID) as Names
FROM
[OpsSimEventsView] ev
JOIN
[OpsInstructorEventsView] inst
ON
ev.EventID = inst.EventID
This is very slow, im looking at 4seconds per call to the DB. Is there a way for me to improve the performance of the function? Its a fairly small function so im not sure what I can do here, and I couldnt see a way to work the COALESCE into the SELECT of the main procedure.
Any help would be really appreciated, thanks.
You could try something like this.
SELECT
ev.[BGcolour],
ev.[Event] AS name,
ev.[eventid] AS ID,
ev.[eventstart],
ev.[CourseType],
ev.[Type],
ev.[OtherType],
ev.[OtherTypeDesc],
ev.[eventend],
ev.[CourseNo],
ev.[Confirmed],
ev.[Cancelled],
ev.[DeviceID] AS resource_id,
ev.Crew,
ev.CompanyName ,
ev.Notes,
STUFF((SELECT '<br>'+inst.InstructorName
FROM [OpsInstructorEventsView] inst
WHERE ev.EventID = inst.EventID
FOR XML PATH(''), TYPE).value('.', 'nvarchar(max)'), 1, 4, '') as Names
FROM
[OpsSimEventsView] ev
Not sure why you have joined OpsInstructorEventsView in the main query. I removed it here but if you needed you can just add it again.
A few things to look at:
1) The overhead of functions makes them expensive to call, especially in the select statement of a query that could potentially be returning thousands of rows. It will have to execute that function for every one of them. Consider merging the behavior of the function into your main stored procedure, where the SQL Server can make better use of its optimizer.
2) Since you are joining on event id in both tables, make sure you have an index on those two columns. I would expect that you do, given that those both appear to be primary key columns, but make sure. An index can make a huge difference.
3) Convert your coalesce call into its equivalent case statements to remove the overhead of calling that function.
Yes make it an INLINE Table-Valued SQL function:
CREATE FUNCTION fnReturnInstructorNamesAsHTML
( #EventID INT )
RETURNS Table
As
Return
SELECT InstructorName + '<br>' result
FROM OpsInstructorEventsView
WHERE EventID = #EventID
Go
Then, in your SQL Statement, use it like this
SELECT ]Other stuff],
(Select result from dbo.fnReturnInstructorNamesAsHTML(ev.EventID)) as Names
FROM OpsSimEventsView ev
JOIN OpsInstructorEventsView inst
ON ev.EventID = inst.EventID
I'm not exactly clear how the query you show in your question is concatenating data from multiple rows in one row of the result, but the problem is that ordinary UDFs are compiled on use, on EVERY use, so for each row in your output result the Query processopr has to recompile the UDF again. THis is NOT True for an "inline table valued" UDF, as it's sql is folded into the outer sql before it is passed to the SQL optimizer, (the subsystem that generates the statement cache plan) and so the UDF is only compiled once.