SQL Server / T-SQL: Return a single value from a constructed table - sql

I am writing an inline function, which accepts a fixed number of parameters (9 in the code example), arranges those parameters into a constructed table with 2 columns and 4 rows, sorts the table, and then returns a single value from said table (the value in column 1, and in whichever row 1-4 the calling statement requested).
I've got most of it done.
But I need to know how to select a specific column and row index at the end.
You may be asking why I need an inline function for this, and the answer is because a) the sorting is crucial and b) this is being used in a computed column in a memory-optimized table, where sub-queries are not allowed, etc.
Here's the code I have so far:
CREATE FUNCTION [dbo].[SelectOrderedInputValue]
(
-- 1-based row index of the desired return value
#RequestedRowIndex INT,
#InputValue1 INT,
#InputValue2 INT,
#InputValue3 INT,
#InputValue4 INT,
#InputRank1 INT,
#InputRank2 INT,
#InputRank3 INT,
#InputRank4 INT
)
RETURNS TABLE AS RETURN
(
/* Places the parameters into a table, and sorts by rank.
I need to figure out how to specify a row and column,
so I can return the single requested value from this table.
In this case, I need to return the value in column #1 (alias InputValues)
and row # #RequestedRowIndex*/
SELECT TOP 4 InputValues FROM
(VALUES
([InputValue1], [InputRank1]),
([InputValue2], [InputRank2]),
([InputValue3], [InputRank3]),
([InputValue4], [InputRank4])
) AS Inputs(InputValues, InputRanks) ORDER BY InputRanks ASC
)

There is no need to return a table for a single value. Just use a scalar function:
CREATE FUNCTION [dbo].[SelectOrderedInputValue] (
-- 1-based row index of the desired return value
#RequestedRowIndex INT,
#InputValue1 INT,
#InputValue2 INT,
#InputValue3 INT,
#InputValue4 INT,
#InputRank1 INT,
#InputRank2 INT,
#InputRank3 INT,
#InputRank4 INT
) RETURNS INT AS
BEGIN
/* Places the parameters into a table, and sorts by rank.
I need to figure out how to specify a row and column,
so I can return the single requested value from this table.
In this case, I need to return the value in column #1 (alias InputValues)
and row # #RequestedRowIndex*/
DECLARE #retval INT;
SET #retval = (SELECT TOP 4 *
FROM (VALUES ([InputValue1], [InputRank1]),
([InputValue2], [InputRank2]),
([InputValue3], [InputRank3]),
([InputValue4], [InputRank4])
) Inputs(InputValues, InputRanks)
ORDER BY InputRanks ASC
OFFSET #RequestedRowIndex-1 FETCH FIRST 1 ROW ONLY
);
RETURN #retval;
END;

Try with OFFSET FETCH
DECLARE #RequestedRowIndex INT=4
SELECT * FROM (
SELECT TOP 4 * FROM
(VALUES
(10, 11),
(20, 21),
(30, 31),
(40, 41)
) AS Inputs(InputValues, InputRanks)
ORDER BY InputRanks ASC )A
ORDER BY InputRanks ASC
OFFSET #RequestedRowIndex-1 ROWS FETCH NEXT 1 ROWS ONLY

Related

SQL matching with rest of the columns if any of the where condition parameter is missing

Here is my table structure:
ID cid Name Course Interval
1 1 KB Y 2
2 1 TB Y 3
3 2 BK N 1
I need to write a query which returns all rows with matching condition.
if at all any of the parameter is null or not provided then i need to return all of the matching rows.
In my select query if my parameters are (cid ==1 and Name== null and course ==Y or (cid ==1 and course ==Y ) then I need to return rows with id
1 and 2.
What exactly I need is this:
If I get all the matching record then i can take interval corresponding to the record. Else I need to take average interval of the matching record
Is this what you are after? Its a variable number or arguments - in my case the parameters are explicitly defined, yours may be passed in from a stored proc etc.
-- Create the table
create table #t(ID int, cid int, Name char(2), Course char(1), Interval int)
insert #t values (1,1,'KB','Y',2)
,(2,1,'TB','Y',3)
,(3,2,'BK','N',1)
-- Declare the arguments
declare #cid int
declare #name char(2)
declare #course char(1)
-- Set one or more arguments
set #cid=1
set #name=null
set #course='Y'
select AVG(convert(decimal(5,2),interval)) from #t
where isnull(#cid,cid)=cid
and isnull(#name,name)=name
and isnull(#course,course)=course

insert text into table conditional on values returned from stored procedures (and a select statement)

My goal is to update a table with a string "finished" and some static values (in the other columns in the table), based on a select statement and conditional that the output from the select statements fulfil certain conditions. These conditions are that the output from around 18 stored procedures are returned with a value=0.
Each stored procedure returns some value ("Number_of") for each unit ("EnhetsId"), ranging from 0 to infinity. The stored procedures first calls a database ('GR_PS09_1') and then a unique unit based on its unit ID ('EnhetsId').
So first I have a select statement, that given a certain condition gives me an output of a number of units. Then, if these units fulfil the condition that the SP returns the value 0 for each of the units, I want to insert into a temp-table the "qualified" unit ID (EnhetsId), some static values and the string "finished".
My code so far, with help from #MrReband:
declare #temp2 table (
EnhetsId varchar(50),
TjanstId Int,
Tabell varchar(50),
Kommentar ntext,
Uppdaterad datetime
);
WITH ENHET_CTE AS
(
SELECT A.[EnhetsId]
FROM [StatistikinlamningDataSKL].[dbo].[StatusHistorik] A
inner join (
select [EnhetsId], max(SenastUppdaterad) as SenastDatum
from [StatistikinlamningDataSKL].[dbo].[StatusHistorik]
group by [EnhetsId]
) B
on A.[EnhetsId] = B.[EnhetsId] and A.[SenastUppdaterad] = B.SenastDatum
WHERE [NyStatus] = 4
),
/* this code below is the one I am trying to solve */
SP_01 AS
(
set #enhet = (select exec StatistikinlamningDataSKL.dbo.SKL_admin_KN_aform 'GR_PS09_1', '''EnhetsId'''
where 'Number_of' = 0)
),
/*add more stored procedures*/
insert into #temp2
(EnhetsId, TjanstId, Tabell, Kommentar, Uppdaterad)
SELECT
EnhetsId, 1, 'GR_PS09_1', 'finished', getdate()
from ENHET_CTE;
Does anyone have some input on how to proceed with this code?

Assigning Value along with Data Retrieval

Is there a way to combine assigning a value to a variable and Selecting a column in sql. I need to compute and select a column in a table based on the variable. The variable's value changes based on another column in the table.
var #BeginValue
Columns in table : ReducedBy
My initial begin value is stored in #BeginValue. The table has reducedBy which is a factor by which my begin value should be reduced. So when i select, beginvalue for the first recored would be #BeginValue and the #EndValue should be #BeginValue = #BeginValue - reducedBy. It continues like this, as many times as the number of records in my table.
Result set must be like this:
#Begin = 10
Begin End ReducedBy
10 8 2
8 6 2
6 5 1
Is there a way with which i can achieve this without using a cursor or with multiple update statements.
You can't assign in a query that returns a result set. The closest you can get is to store the result in a table variable. Then you can both do computations against that table, and return it as a result set:
-- Store results in table variable
declare #tbl table (id int, col1 int, ...)
insert #tbl
(id, col1, ...)
select id
, col1
, ...
from ... your query here ...
-- Assign variable
select #YourVariable = ... your computation here ...
from #tbl
-- Return result set
select *
from #tbl
If your question is
Can I do..
SELECT #a = field, field2 from table
and get a resultset and set the value of #a?
Then the answer is no, not in a single statement.

SQL How to find if all values from one field exist in another field in any order

I am trying to match data from an external source to an in house source. For example one table would have a field with a value of "black blue" and another table would have a field with a value of "blue black". I am trying to figure out how to check if all individual words in the first table are contained in a record the 2nd table in any order. It's not always two words that need to be compared it could be 3 or 4 as well. I know I could use a cursor and build dynamic sql substituting the space with the AND keywod and using the contains function but I'm hoping not to have to do that.
Any help would be much appreciated.
Try doing something like this: Split the data from the first table on the space into a temporary table variable. Then use CHARINDEX to determine if each word is contained in the second table's record. Then just do this for each word in the first record and if the count is the same as the successful checks then you know every word from the first record is used in the second.
Edit: Use a Split function such as:
CREATE FUNCTION dbo.Split (#sep char(1), #s varchar(512))
RETURNS table
AS
RETURN (
WITH Pieces(pn, start, stop) AS (
SELECT 1, 1, CHARINDEX(#sep, #s)
UNION ALL
SELECT pn + 1, stop + 1, CHARINDEX(#sep, #s, stop + 1)
FROM Pieces
WHERE stop > 0
)
SELECT pn,
SUBSTRING(#s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS s
FROM Pieces
)
Here's another method you could try, you could sample some simple attributes of your strings such as, length, number of spaces, etc.; then you could use a cross-join to create all of the possible string match combinations.
Then within your where-clause you can sort by matches, the final piece of which in this example is a check using the patindex() function to see if the sampled piece of the first string is in the second string.
-- begin sample table variable set up
declare #s table(
id int identity(1,1)
,string varchar(255)
,numSpace int
,numWord int
,lenString int
,firstPatt varchar(255)
);
declare #t table(
id int identity(1,1)
,string varchar(255)
,numSpace int
,numWord int
,lenString int
);
insert into #t(string)
values ('my name');
insert into #t(string)
values ('your name');
insert into #t(string)
values ('run and jump');
insert into #t(string)
values ('hello my name is');
insert into #s(string)
values ('name my');
insert into #s(string)
values ('name your');
insert into #s(string)
values ('jump and run');
insert into #s(string)
values ('my name is hello');
update #s
set numSpace = len(string)-len(replace(string,' ',''));
update #s
set numWord = len(string)-len(replace(string,' ',''))+1;
update #s
set lenString = len(string);
update #s
set firstPatt = rtrim(substring(string,1,charindex(' ',string,0)));
update #t
set numSpace = len(string)-len(replace(string,' ',''));
update #t
set numWord = len(string)-len(replace(string,' ',''))+1;
update #t
set lenString = len(string);
-- end sample table variable set up
-- select all combinations of strings using a cross join
-- and sort the entries in your where clause
-- the pattern index checks to see if the sampled string
-- from the first table variable is in the second table variable
select *
from
#s s cross join #t t
where
s.numSpace = t.numspace
and s.numWord = t.numWord
and s.lenString = t.lenString
and patindex('%'+s.firstPatt+'%',t.string)>0;

TSQL - Count specific values within Multistatement table UDF and pass them to additional column

during coding my project I have encountered an obstacle and cannot go through with this issue...
here is what I would like to achieve, I have a simple table which stores data connected with football players like: Number (ID), Name, Goals (are additional ones, but right now are irrelevant) and I have created a UDF Multistatement table LotOfGoals which looks as follows:
CREATE FUNCTION LotOfGoals()
RETURNS #Players TABLE
(
Number INT,
Name VARCHAR(20),
Goals INT
FuzzyLevel FLOAT(3) --extra column which I would like to add to result
)
AS
BEGIN
INSERT #Players
SELECT Number, Name, Goals
FROM FuzzyFootballTeam
WHERE Goals > 2
ORDER BY Number
-- here FuzzyLevel column should include data counted by MembershipLevel
-- scalar UDF.
-- I want to pass each number of goals into MembershipLevel function and
-- insert return value into a new column FuzzyLevel.
RETURN
END
GO
now MembershipLevel function:
CREATE FUNCTION MembershipLevel(#Goals INT)
RETURNS float(3)
AS
BEGIN
DECLARE #Level float(3)
SET #Level = 0.25*#Goals - 0.5;
RETURN #Level
END
As I have written, after WHERE clause I would like to pass each number of goals to a MembershipLevel and then its return value insert into new column FuzzyLevel.
I would be really really grateful for any hint, idea etc.
Thanks in advance !
True, I will change to in-line one. One more question is there any way to use FuzzyLevel column in where clause ? (I receive Invalid column name 'FuzzinessLevel') what I want is to limit allowed fuzzylevel. I have expanded both functions with one more additional argument #AcceptedFuzzyLevel float and the scalar function looks like this:
DECLARE #Level float(3)
DECLARE #TempLevel float(3)
IF (#Goals <= 2)
SET #TempLevel = 0;
IF (#TempLevel >= #FuzzyLevelAccepted)
SET #Level = #TempLevel;
ELSE IF (#Goals > 2 AND #Goals < 6)
SET #TempLevel = 0.25*#Goals - 0.5;
IF (#TempLevel >= #FuzzyLevelAccepted)
SET #Level = #TempLevel;
ELSE IF (#Goals >= 6)
SET #TempLevel = 1;
IF (#TempLevel >= #FuzzyLevelAccepted)
SET #Level = #TempLevel;
RETURN #Level
But after execution I also receive records with NULL values.
OK, I have fixed it. Just resolved following inequality: x > 4y + 2. Works but I`m curious why it is not possible to use new column in Where clause.
Thanks a million !
Just add it as a column because MembershipLevel is a scalar udf. It doesn't matter about the outer code (stored proc or tablek valued udf or SELECT)
INSERT #Players (Number, Name, Goals, FuzzyLevel)
SELECT Number, Name, Goals,
dbo.MembershipLevel(Goals)
FROM FuzzyFootballTeam ft
WHERE Goals > 2
ORDER BY Number
Observations: I'd explicitly specify the column list for #Players. I'd also ask myself why this isn't an in-line table valued function: a multi-statement udf is often a performance killer...