Create function return integer SQL Server 2008 - sql

I was trying to create a function which returns to an integer. However, I got the warning as
"Msg 2715, Level 16, State 3, Procedure median, Line 1
Column, parameter, or variable #0: Cannot find data type Median."
Here is the query. Thanks in advance.
CREATE FUNCTION dbo.median (#score int)
RETURNS Median
AS
BEGIN
DECLARE #MedianScore as Median;
SELECT #MedianScore=
(
(SELECT MAX(#score) FROM
(SELECT TOP 50 PERCENT Score FROM t ORDER BY Score) AS BottomHalf)
+
(SELECT MIN(#score) FROM
(SELECT TOP 50 PERCENT Score FROM t ORDER BY Score DESC) AS TopHalf)
) / 2 ;
RETURN #MedianScore;
END;
GO

Just change the return type to integer:
CREATE FUNCTION dbo.median (#score int)
RETURNS integer
AS
BEGIN
DECLARE #MedianScore as integer;
Unless you're intentionally using the Median type for something that you haven't stated.

Since you are calculating Median of some values I would suggest you return a Numeric value instead of Integer as MAX(#score) + MIN(#score)/ 2 can return a decimal number value. so trying to save that value in an INT variable will truncate the Decimal part. which can lead to wrong results.
In the following example I have used NUMERIC(20,2) return value.
CREATE FUNCTION dbo.median (#score int)
RETURNS NUMERIC(20,2)
AS
BEGIN
DECLARE #MedianScore as NUMERIC(20,2);
SELECT #MedianScore=
(
(SELECT MAX(#score) FROM
(SELECT TOP 50 PERCENT Score FROM t ORDER BY Score) AS BottomHalf)
+
(SELECT MIN(#score) FROM
(SELECT TOP 50 PERCENT Score FROM t ORDER BY Score DESC) AS TopHalf)
) / 2 ;
RETURN #MedianScore;
END;
GO
or if you do want to return an INTEGER use round function inside the function something like this..
CREATE FUNCTION dbo.median (#score int)
RETURNS INT
AS
BEGIN
DECLARE #MedianScore as INT;
SELECT #MedianScore=ROUND(
(
(SELECT MAX(#score) FROM
(SELECT TOP 50 PERCENT Score FROM t ORDER BY Score) AS BottomHalf)
+
(SELECT MIN(#score) FROM
(SELECT TOP 50 PERCENT Score FROM t ORDER BY Score DESC) AS TopHalf)
) / 2, 0) ;
RETURN #MedianScore;
END;
GO

You must declare a datatype on RETURNS. "Median" is not a type.
CREATE FUNCTION dbo.median (#score int)
RETURNS real -- you can use also float(24), numeric(8,3), decimal(8,3)...
AS
BEGIN
DECLARE #MedianScore as real;
SELECT #MedianScore=
(
(SELECT MAX(#score) FROM
(SELECT TOP 50 PERCENT Score FROM t ORDER BY Score) AS BottomHalf)
+
(SELECT MIN(#score) FROM
(SELECT TOP 50 PERCENT Score FROM t ORDER BY Score DESC) AS TopHalf)
) / 2 ;
RETURN #MedianScore;
END;
GO

create function [dbo].[Sum]
(
#x int,
#y int
)
RETURNS int
AS
BEGIN
return #x+#y
END

Related

sort given string alphabetically and return letter for letter

I basically want to alphabetically sort a given string and output letter for letter via inline-function...
create function dbo.SortStringLetter4Letter (
#key varchar(64)
) returns table
as
return select ...
And I want to call up the function like this:
select *
from dbo.SortStringLetter4Letter('TEST');
go
And get a result like this:
id Sorted
----------- --------
1 E
2 S
3 T
4 T
Anybody got an idea for this problem?
This should do;
create or alter function dbo.SortStringLetter4Letter (
#key varchar(64)
) returns table
as
return
WITH Numbers AS
(
SELECT 1 AS Number
UNION ALL
SELECT Number+1
FROM Numbers
WHERE Number < Len(#key)
)
Select
id = Number, Sorted = SUBSTRING(#Key, Number, 1)
from Numbers
GO
Select * from dbo.SortStringLetter4Letter('test') order by sorted
This is the solution I worked out:
drop function if exists dbo.LetterForLetter;
create function dbo.LetterForLetter (
#key varchar(128)
) returns #chars table (# int, character char)
as
begin
insert into #chars
select #,
substring(#key, #, 1) "character"
from Numbers
where # <= len(#key)
and # > 0
return
end;
go
select *
from dbo.LetterForLetter('TEST');
go
The table Numbers is just filled with numbers btw. that I used to give the keys an ID

SQL creating function got error on the return part

Can anyone please let me know where of the SQL query is wrong?
The error is
Incorrect syntax near "return"
Code:
CREATE FUNCTION getNthHighestSalary(#N INT)
RETURNS INT
AS
BEGIN
WITH ranksalary AS
(
SELECT
salary,
ROW_NUMBER() OVER (ORDER BY Salary DESC) AS Rank
FROM
Employee
)
RETURN (SELECT Salary AS getNthHighestSalary
FROM ranksalary
WHERE Rank = #N);
END
Looks like T-SQL to me. As #marc_s already pointed out you need to store the value in a variable first and then return that.
CREATE FUNCTION getNthHighestSalary(#N INT)
RETURNS INT
AS
BEGIN
DECLARE #result int
;WITH ranksalary AS
(
SELECT
salary,
ROW_NUMBER() OVER (ORDER BY Salary DESC) AS [Rank]
FROM
Employee
)
SELECT #result = Salary
FROM ranksalary
WHERE [Rank] = #N
RETURN #result
END
GO
Why not just use this logic?
declare #rank;
select #rank = count(*) + 1
from employee
where salary > #salary;
return #rank;

How to optimize SQL Server code?

I have a table with the columns: Id, time, value.
First step: Given input parameters as signal id, start time and end time, I want to first extract rows with the the signal id and time is between start time and end time.
Second: Assume I have selected 100 rows in the first step. Given another input parameter which is max_num, I want to further select max_num samples out of 100 rows but in a uniform manner. For example, if max_num is set to 10, then I will select 1, 11, 21, .. 91 rows out of 100 rows.
I am not sure if the stored procedure below is optimal, if you find any inefficiencies of the code, please point that out to me and give some suggestion.
create procedure data_selection
#sig_id bigint,
#start_time datetime2,
#end_time datetime2,
#max_num float
AS
BEGIN
declare #tot float
declare #step int
declare #selected table (id int primary key identity not null, Date datetime2, Value real)
// first step
insert into #selected (Date, Value) select Date, Value from Table
where Id = #sig_id
and Date > = #start_time and Date < = #end_time
order by Date
// second step
select #tot = count(1) from #selected
set #step = ceiling(#tot / #max_num)
select * from #selected
where id % #step = 1
END
EDITED to calculate step on the fly. I had first thought this was an argument.
;with data as (
select row_number() over (order by [Date]) as rn, *
from Table
where Id = #sig_id and Date between #start_time and #end_time
), calc as (
select cast(ceiling(max(rn) / #max_num) as int) as step from data
)
select * from data cross apply calc as c
where (rn - 1) % step = 0 --and rn <= (#max_num - 1) * step + 1
Or I guess you can just order/filter by your identity value as you already had it:
;with calc as (select cast(ceiling(max(rn) / #max_num) as int) as step from #selected)
select * from #selected cross apply calc as c
where (id - 1) % step = 0 --and id <= (#max_num - 1) * step + 1
I think that because you're rounding step up with ceiling you'll easily find scenarios where you get fewer rows than #max_num. You might want to round down instead: case when floor(max(rn) / #max_num) = 0 then 1 else floor(max(rn) / #max_num) end as step?

SQL Data Sampling

We have had a request to provide some data to an external company.
They require only a sample of data, simple right? wrong.
Here is their sampling criteria:
Total Number of records divided by 720 (required sample size) - this gives sampling interval (if result is a fraction, round down to next whole number).
Halve the sampling interval to get the starting point.
Return each record by adding on the sampling interval.
EXAMPLE:
10,000 Records - Sampling interval = 13 (10,000/720)
Starting Point = 6 (13/2 Rounded)
Return records 6, 19 (6+13), 32 (19+13), 45 (32+13) etc.....
Please can someone tell me how (if) something like this is possible in SQL.
If you have use of ROW_NUMBER(), then you can do this relatively easily.
SELECT
*
FROM
(
SELECT
ROW_NUMBER() OVER (ORDER BY a, b, c, d) AS record_id,
*
FROM
yourTable
)
AS data
WHERE
(record_id + 360) % 720 = 0
ROW_NUMBER() gives all your data a sequential identifier (this is important as the id field must both be unique and NOT have ANY gaps). It also defines the order you want the data in (ORDER BY a, b, c, d).
With that id, if you use Modulo (Often the % operator), you can test if the record is the 720th record, 1440th record, etc (because 720 % 720 = 0).
Then, if you offset your id value by 360, you can change the starting point of your result set.
EDIT
After re-reading the question, I see you don't want every 720th record, but uniformly selected 720 records.
As such, replace 720 with (SELECT COUNT(*) / 720 FROM yourTable)
And replace 360 with (SELECT (COUNT(*) / 720) / 2 FROM yourTable)
EDIT
Ignoring the rounding conditions will allow a result of exactly 720 records. This requires using non-integer values, and the result of the modulo being less than 1.
WHERE
(record_id + (SELECT COUNT(*) FROM yourTable) / 1440.0)
%
((SELECT COUNT(*) FROM yourTable) / 720.0)
<
1.0
declare #sample_size int, #starting_point int
select #sample_size = 200
select top (#sample_size) col1, col2, col3, col4
from (
select *, row_number() over (order by col1, col2) as row
from your_table
) t
where (row % ((select count(*) from your_table) / #sample_size)) - (select count(*) from your_table) / #sample_size / 2) = 0
It's going to work in SQL Server 2005+.
TOP (#variable) is used to limit rows (where condition because of integers rounding might not be enough, may return more rows then needed) and ROW_NUMBER() to number and order rows.
Working example: https://data.stackexchange.com/stackoverflow/query/62315/sql-data-sampling below code:
declare #tab table (id int identity(1,1), col1 varchar(3), col2 varchar(3))
declare #i int
set #i = 0
while #i <= 1000
begin
insert into #tab
select 'aaa', 'bbb'
set #i = #i+1
end
declare #sample_size int
select #sample_size = 123
select ((select count(*) from #tab) / #sample_size) as sample_interval
select top (#sample_size) *
from (
select *, row_number() over (order by col1, col2, id desc) as row
from #tab
) t
where (row % ((select count(*) from #tab) / #sample_size)) - ((select count(*) from #tab) / #sample_size / 2) = 0
SQL server has in-built function for it.
SELECT FirstName, LastName
FROM Person.Person
TABLESAMPLE (10 PERCENT) ;
You can use rank to get a row-number. The following code will create 10000 records in a table, then select the 6th, 19th, 32nd, etc, for a total of 769 rows.
CREATE TABLE Tbl (
Data varchar (255)
)
GO
DECLARE #i int
SET #i = 0
WHILE (#i < 10000)
BEGIN
INSERT INTO Tbl (Data) VALUES (CONVERT(varchar(255), NEWID()))
SET #i = #i + 1
END
GO
DECLARE #interval int
DECLARE #start int
DECLARE #total int
SELECT #total = COUNT(*),
#start = FLOOR(COUNT(*) / 720) / 2,
#interval = FLOOR(COUNT(*) / 720)
FROM Tbl
PRINT 'Start record: ' + CAST(#start as varchar(10))
PRINT 'Interval: ' + CAST(#interval as varchar(10))
SELECT rank, Data
FROM (
SELECT rank()
OVER (ORDER BY t.Data) as rank, t.Data AS Data
FROM Tbl t) q
WHERE ((rank + 1) + #start) % #interval = 0

how to return a cell into a variable in sql functions

I want to define a scaler function which in that I'm going to return the result into a variable but I do not know how to do this.
CREATE FUNCTION dbo.Funname ( #param int )
RETURNS INT
AS
declare #returnvar int
select #returnvar = select colname from tablename where someconditions = something
return(#returnvar)
I want to make a function something like the top. I mean the result of the select statement which is:
select colname from tablename where someconditions = something
Is only a single cell and we are sure about it. I want to store it into a variable and return it from the function. How can I implement this thing?
I should probably mention that scalar UDFs do come with a considerable health warning and can cause performance issues depending upon how you use them.
Here's an example though.
CREATE FUNCTION dbo.Funname ( #param INT )
RETURNS INT
WITH RETURNS NULL ON NULL INPUT
AS
BEGIN
RETURN (SELECT number FROM master.dbo.spt_values WHERE number < #param)
END
In the above example I didn't use a variable as it is redundant. The version with variable is
BEGIN
DECLARE #Result int
SET #Result = (SELECT number FROM master.dbo.spt_values WHERE number < #param)
RETURN #Result
END
For both of the above you would need to be sure the Query returned at most one row to avoid an error at runtime. For example
select dbo.Funname(-1) Returns -32768
select dbo.Funname(0) Returns error "Subquery returned more than 1 value."
An alternative syntax would be
BEGIN
DECLARE #Result int
SELECT #Result = number FROM master.dbo.spt_values WHERE number < #param
RETURN #Result
END
This would no longer raise the error if the subquery returned more than one value but you would just end up with an arbitrary result with no warning - which is worse.
Following Comments I think this is what you need
CREATE FUNCTION dbo.getcustgrade(#custid CHAR(200))
RETURNS INT
WITH RETURNS NULL ON NULL INPUT
AS
BEGIN
RETURN
( SELECT [cust grade]
FROM ( SELECT customerid,
DENSE_RANK() OVER (ORDER BY COUNT(*) DESC) AS [cust grade]
FROM Orders
GROUP BY CustomerID
)
d
WHERE customerid = #custid
)
END