T-SQL : CREATE FUNCTION must be the only statement in batch [duplicate] - sql-server

I'm getting this error from the function:
CREATE FUNCTION getLavel(#id int ,#lavel char)
RETURNS date
BEGIN
DECLARE #date date
select #date = (select authorization_date from Authorized WHERE diver_number = #id and #lavel =level_name)
return #date
END
GO
What can be the reason?
Ty very much.

The function needs to be either the only function in the query window OR the only statement in the batch. If there are more statements in the query window, you can make it the only one "in the batch" by surrounding it with GO's.
e.g.
GO
CREATE FUNCTION getLavel(#id int ,#lavel char)
RETURNS date
BEGIN
DECLARE #date date
select #date = (select authorization_date from Authorized WHERE diver_number = #id and #lavel =level_name)
return #date
END
GO

Turn this into an inline table valued function. This will perform better than the scalar function. Also, you should NOT use the default sizes for character datatypes. Do you know what the default length for a char is? Did you know that it can vary based on usage?
CREATE FUNCTION getLavel
(
#id int
, #lavel char --You need to define the length instead of the default length
)
RETURNS table
return
select authorization_date
from Authorized
WHERE diver_number = #id
and #lavel = level_name
GO

You need to add RETURN before the END statement
That should fix your issue, that's what fixed mine. :D

Make sure that this statement is the only the only sql in your query window before you execute it.
Or you can highlight the function declaration and execute

What solved it for me, was that I was trying to create the function inside of a transaction context - that doesn't make sense from a SQL Server point of view. Transactions are for data, not functions.
Take the CREATE FUNCTION statement out of the transaction, then wrap it in GO's

CREATE FUNCTION CalculateAge(#DOB DATE)
RETURNS INT
AS
BEGIN
DECLARE #Age INT
SET #DOB='08/12/1990'
SET #Age =DATEDIFF(YEAR,#DOB,GETDATE()) -
CASE
WHEN (MONTH (#DOB)> MONTH (GETDATE ())) OR
(MONTH (#DOB)= MONTH (GETDATE ()) AND DAY (#DOB) >DAY (GETDATE ()))
THEN 1
ELSE 0
END
SELECT #Age
END

The Error is given to you in only query Page But if you execute the query then it will successfully execute.
CREATE FUNCTION getLavel(#id int ,#lavel char)
RETURNS date
BEGIN
DECLARE #date date
select #date = (select authorization_date from Authorized WHERE diver_number = #id and #lavel = level_name)
return #date
END
GO

Related

SQL server Function - Take column name as input parameter

I need to write a SQL function to return column specific values, so I am passing the column name as a parameter to SQL-function to return its corresponding value. Here is the sample function
CREATE FUNCTION GETDATETIME(#columnName VARCHAR(100))
RETURNS DATETIME
AS
BEGIN
RETURN (SELECT TOP 1.#columnName FROM TEST_TABLE )
END
GO
The above function seems to be straight forward, but it not working as expected.
And when I execute the function
SELECT dbo.GETDATETIME('DATETIMECOLUMNNAME')
I am getting this error:
Conversion failed when converting date and/or time from character string.
Can someone help me to identify the issue?
For that you need to write dynamic sql. But Functions won't support execute statement.
So you need to write multiple If conditions for each column.
CREATE FUNCTION GETDATETIME(#columnName VARCHAR(100))
RETURNS DATETIME
AS
BEGIN
DECLARE #RESULT DATETIME;
IF (#columnName = 'ABC')
Begin
SELECT TOP 1 #RESULT = [ABC] FROM TEST_TABLE
END
ELSE IF (#columnName = 'DEF')
Begin
SELECT TOP 1 #RESULT = [DEF] FROM TEST_TABLE
END
ELSE IF (#columnName = 'GHI')
Begin
SELECT TOP 1 #RESULT = [GHI] FROM TEST_TABLE
END
RETURN #RESULT
END
GO
Edit 2:
If your column always return Datetime, then you can do like below.
CREATE TABLE A_DUM (ID INT, STARTDATE DATETIME, ENDDATE DATETIME, MIDDLEDATE DATETIME)
INSERT INTO A_DUM
SELECT 1, '2019-07-24 11:35:58.910', '2019-07-28 11:35:58.910', '2019-07-26 11:35:58.910'
UNION ALL
SELECT 2, '2019-07-29 11:35:58.910', '2019-08-01 11:35:58.910', '2019-07-24 11:35:58.910'
And your function like below
CREATE FUNCTION GETDATETIME(#columnName VARCHAR(100))
RETURNS DATETIME
AS
BEGIN
DECLARE #RESULT DATETIME;
SELECT TOP 1 #RESULT = CAST(PROP AS DATETIME)
FROM A_DUM
UNPIVOT
(
PROP FOR VAL IN (STARTDATE, ENDDATE,MIDDLEDATE)
)UP
WHERE VAL = #columnName
RETURN #RESULT
END
GO
There's a workaround to this, similar to #Shakeer's answer - if you are attempting to GROUP BY or perform a WHERE on a column name, then you can just use a CASE statement to create a clause to match on specific column names (if you know them).
Obviously this doesn't work very well if you have many columns to hard-code, but at least it's a way to achieve the general idea.
E.g. with WHERE clause:
WHERE
(CASE
WHEN #columnname = 'FirstColumn' THEN FirstColumnCondition
WHEN #columnname = 'SecondColumn' THEN SecondColumnCondition
ELSE SomeOtherColumnCondition
END)
Or with GROUP BY:
GROUP BY
(CASE
WHEN #columnname = 'FirstColumn' THEN FirstColumnGroup
WHEN #columnname = 'SecondColumn' THEN SecondColumnGroup
ELSE SomeOtherColumnGroup
END)
No you cannot use dynamic sql in functions in SQL. Please check this link for more info link.
So it is not possible to achieve this by any function, yes you may use stored procedures with output parameter for same.
You may find this link for reference link.

Why does T-SQL print statement cause ResourceClosedError: This result object does not return rows. It has been closed automatically

I'm calling a SQL Server SP from Jupyter, and the SP looks like this:
ALTER
procedure [dbo].[proc_Report_QuarterlyDistribution02] (#quarter int, #year int, #group int)
as
declare #total int,
#date date
set #date = cast(#year as varchar(4)) + '-01-01'
set #date = dateadd(quarter, #quarter - 1, #date)
print #date
select #total = count(1)
from DimMedical
where ServiceDate between
DATEADD(quarter, -9,#date) and #date
and carriercode = #group
and Category = 'Physicians'
The SP goes on - that's not the issue.
The problem is the line
print #date
Question Why would the print statement cause the error:
ResourceClosedError: This result object does not return rows. It has been closed automatically.
Why would the print statement cause the error: "This result object does not return rows"
This is probably a limitation in the client library you are using. Some client libraries stop looking for a result set when they see a message.
Either remove the print statement, upgrade your client library (not mentioned), or have the stored procedure insert into a table using INSERT … EXEC, and then select from that in a subsequent query.

SQL IF ELSE performance issue

I have a stored procedure that can get data from 2 different sources depending on if the user requests data from a single closed period (archived into a data warehouse table) or from an open period (data from transaction tables).
If I pass parameters that limit the select to the data warehouse table (providing a year and period for a closed period) the procedure takes a very long time to return results unless I comment out the ELSE BEGIN… code. No data is coming from the ELSE portion of code but it is still slowing down the procedure. If I comment out the ELSE portion of code, it is very fast.
I have tried OPTION (RECOMPILE) and I’m using local variables to avoid parameter sniffing but it’s not helping. Is there any way to get around this?
The following is an example of what I’m doing that runs slow:
IF #Year <> 0 AND #Period <> 0 AND (SELECT PerClosedTimestamp
FROM Period
WHERE
PerCompanyID = #CompanyID AND
PerYear = #Year AND
PerPeriod = #Period) IS NOT NULL
BEGIN
SELECT
datawhse.column1, datawhse.column2, etc …
FROM
datawhse
END
ELSE
BEGIN
SELECT
trantable.column1, trantable.column2, etc…
FROM
trantable
END
If I exclude the ELSE statement it runs very fast:
IF #Year <> 0
AND #Period <> 0
AND (SELECT PerClosedTimestamp
FROM Period
WHERE PerCompanyID = #CompanyID
AND PerYear = #Year
AND PerPeriod = #Period) IS NOT NULL
BEGIN
SELECT datawhse.column1
,datawhse.column2, etc …
FROM datawhse
END
Are #Year and #Period directly from the input of the stored procedure? like in your sproc definition, did you write in this following way?
create proc USP_name #Year int, #Period int as
begin
...
end
You can try using local variable, according to my experience in many cases like this, local variables help a lot.
create proc USP_name #Year int, #Period int as
begin
declare #Year_local int, #Period_local int
set #Year_local = #Year, #Period_local = #period
if #Year_local <> 0 AND #Period_local <> 0 AND ...
....
end
As mentioned in the comments, the definitive answer to why is it slow is always to be found in the query plan.
At a guess, the appearance of trantable in the procedure is biasing the query optimizer in a way that disfavors datawhse. I'd be tempted to at least try UNION ALL instead of IF/THEN, something along the lines of
SELECT
datawhse.column1, datawhse.column2, etc …
FROM
datawhse
WHERE #Year <> 0 AND #Period <> 0 AND (SELECT PerClosedTimestamp
FROM Period
WHERE
PerCompanyID = #CompanyID AND
PerYear = #Year AND
PerPeriod = #Period) IS NOT NULL
UNION ALL
SELECT
trantable.column1, trantable.column2, etc…
FROM
trantable
WHERE #Year = 0 OR #Period = 0 OR (SELECT PerClosedTimestamp
FROM Period
WHERE
PerCompanyID = #CompanyID AND
PerYear = #Year AND
PerPeriod = #Period) IS NULL
It would be interesting to see how the query plans compare.
Thanks everyone for your suggestions. I ended up creating 2 separate functions to return data from either the data warehouse table or the transaction tables. I select from the functions within the IF THEN ELSE statement and that seems to have solved my problem.

sql scalar functions

I have a scalar function in my code that calls another scalar function that calls 2 other tables as detailed below. I know this must be performing like a pig. It is used throughout the database... My problem is its a little outside developing skills to rewrite this as an table valued function.
I'm attempting to win some of the developers over to rewriting the function, but we only have JAVA guys and no dedicated SQL developer, so they dont see any problems. can anyone suggest how this should be rewritten? many thanks...
CREATE FUNCTION [dbo].[getInvertCurrencyExchangeRateByDate](#casino_id char(16),#currency_code char(3), #end_date datetime)
RETURNS float AS
BEGIN
declare #retval float;
set #retval =
dbo.getCurrencyExchangeRateByDate(#casino_id,#currency_code,#end_date);
if (#retval != 0) return 1/#retval;
return 0;
END
CREATE FUNCTION [dbo].[getCurrencyExchangeRateByDate](#casino_id char(16),#currency_code char(3), #end_date datetime)
RETURNS float AS
BEGIN
declare #retval float;
declare #casino_curr_code char(3);
set #casino_curr_code =
(SELECT TOP 1 currency_code
FROM Casino
WHERE
casino_id=#casino_id
);
if (#currency_code = #casino_curr_code) return 1;
set #retval =
COALESCE(
(
SELECT TOP 1 exchange_rate
FROM CurrencyExchangeRateHistory
WHERE
casino_id=#casino_id and
currency_code=#currency_code AND
transact_time <= #end_date
ORDER BY
transact_time DESC
),0.0);
return #retval
END
I'm sorry, but thats a heck lot of code for something rather simple
I think this satisfies the query needs.
CREATE FUNCTION dbo.TVF(#casino_id char(16),#currency_code char(3), #end_date datetime)
RETURNS TABLE
AS
RETURN --IF THE JOIN FAILS OR RETURNS 0, DIVISION WILL NEVER HAPPEN AND FALL IN THE ISNULL
SELECT TOP 1 CASE WHEN A.currency_code = #currency_code THEN 1 ELSE ISNULL(1/NULLIF(B.exchange_rate,0), 0) END AS RETVAL
FROM Casino A
LEFT JOIN CurrencyExchangeRateHistory B ON A.casino_id = B.casino_id AND B.transact_time <= #end_date AND B.currency_code = A.currency_code
WHERE A.casino_id = #casino_id
ORDER BY B.transact_time DESC

stored procedure returning value

using this code :
ALTER PROCEDURE [dbo].[get](#i int)
AS
BEGIN
declare #ADate datetime
select #ADate = ADate
from table
where i=#i
and DateDiff(day ,getDate(), aDate ) > 0
and aDate is not null
order by aDate asc
return select #ADAte
END
this returns 0 (or system 0 date time, which is not the desired result from the data base).
execute code
Declare #res datetime
exec #res = get 3
print #res
why?
Stored Procedures in SQL Server can only RETURN integers. If you need to return anything other than a single integer, then you should use one of these methods (some explained by the previous answers):
Use a SELECT in your procedure
Use an OUTPUT Parameter
Use a User Defined Function instead
There isn't any need to declare a variable and assign a value to it. Just return the select statement.
ALTER PROCEDURE [dbo].[get](#i int)
AS
BEGIN
select ADate
from table
where i=#i
and DateDiff(day ,getDate(), aDate ) > 0
and aDate is not null
order by aDate asc
END
Although you should be aware that depending on your data this may return more than one value.
EDIT
If you want to you could do it this way:
ALTER PROCEDURE [dbo].[get](#i int, #date datetime output)
AS
BEGIN
select #date = ADate
from table
where i=#i
and DateDiff(day ,getDate(), aDate ) > 0
and aDate is not null
order by aDate asc
END
And then you can use it like so:
Declare #res datetime
exec get 3, #res
print #res
You should select the value:
select #OADate
Your value will be the first value in the first row on the first resultset.
Have a look at CREATE PROCEDURE
you need to use the OUTPUT clause
**OUTPUT**
Indicates that the parameter is a return parameter. The value of this option can be returned to EXEC[UTE]. Use OUTPUT parameters to return information to the calling procedure.
Also, returning a single value seems like it is calling for a USER DEFINED SCALAR FUNCTION, rather than a STORED PROCEDURE

Resources