Selecting a user-defined scalar function that takes as a parameter another field - sql

I have a table a with a list of id's, and a user-defined function foo(id) that takes the id and returns a VARCHAR(20).
What I am trying to do is:
SELECT
id,
foo(id) AS 'text field'
FROM a
However, instead of calling the function for each ID number, like I desired, the text comes back the same for every row. I have tested the foo() function manually with the returned ID's and it does not have that problem, so I realize I must not understand something about the evaluation of the query.

This worked for me. I'm not sure what your saying you get.
CREATE TABLE [dbo].[a](
[id] [int] NULL
)
insert into a select 1
insert into a select 2
insert into a select 4
insert into a select 5
CREATE FUNCTION foo(#id int) RETURNS varchar(20)
AS
BEGIN
DECLARE #ResultVar varchar(20)
SELECT #ResultVar = '# - ' + CAST(#id as varchar(20))
RETURN #ResultVar
END
select id, dbo.foo(id) AS 'text field' from a
returns
id text field
----------- --------------------
1 # - 1
2 # - 2
4 # - 4
5 # - 5
6 # - 6

If the output of the function is functionally dependent on the input, then it will be called for each row, as you expect. If you see a different result, it means that you do not pass the correct input or your function output is not dependent on its input.

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

Can table parameters be null in a stored procedure and how can I test for inclusion of an item [duplicate]

This question already has an answer here:
Checking if UTD parameter has values in stored procedure
(1 answer)
Closed 3 years ago.
I have a stored proc in SQL Server that accepts a table (dataset) as a parameter like so:
CREATE PROCEDURE [dbo].[blah]
(
#someParm INT,
#someTableParm [dbo].[IntIdType] READONLY,
The type IntIdType is like this:
CREATE TYPE [dbo].[IntIdType] AS TABLE(
[ID] [int] NOT NULL
)
But I need to be able to test to see if the parameter passed is NULL or if it can even be passed as null, and if not, I need to make a special test if it contains zero items
WHERE [SomeParm] = 2 AND
(#someTableParm IS NULL OR EXISTS (SELECT something FROM #someTableParm)) AND ...
I get the error message: Msg 137, Level 16, State 1, Procedure xxxx, Line 58 [Batch Start Line 0]
Must declare the scalar variable "#someTableParm".
Any suggestions?
#someTableParm IS NULL would look for a scalar variable #someTableParm, but your variable a table, so that won't work. Therefore, stick to EXISTS:
WHERE [SomeParm] = 2
AND EXISTS (SELECT 1 FROM #someTableParm)
AND...
If there are no rows in #someTableParm, then the EXISTS will return false. As a quick sample:
CREATE TABLE test (ID int)
INSERT INTO test VALUES(1);
DECLARE #S table (I int);
SELECT *
FROM test
WHERE EXISTS (SELECT 1 FROM #S);
DROP TABLE test;
Notice that the SELECT statement returns no rows.

How does one automatically insert the results of several function calls into a table?

Wasn't sure how to title the question but hopefully this makes sense :)
I have a table (OldTable) with an index and a column of comma separated lists. I'm trying to split the strings in the list column and create a new table with the indexes coupled with each of the sub strings of the string it was connected to in the old table.
Example:
OldTable
index | list
1 | 'a,b,c'
2 | 'd,e,f'
NewTable
index | letter
1 | 'a'
1 | 'b'
1 | 'c'
2 | 'd'
2 | 'e'
2 | 'f'
I have created a function that will split the string and return each sub string as a record in a 1 column table as so:
SELECT * FROM Split('a,b,c', ',', 1)
Which will result in:
Result
index | string
1 | 'a'
1 | 'b'
1 | 'c'
I was hoping that I could use this function as so:
SELECT * FROM Split((SELECT * FROM OldTable), ',')
And then use the id and string columns from OldTable in my function (by re-writing it slightly) to create NewTable. But I as far as I understand sending tables into the function doesn't work as I get: "Subquery returned more than 1 value. ... not premitted ... when the subquery is used as an expression."
One solution I was thinking of would be to run the function, as is, on all the rows of OldTable and insert the result of each call into NewTable. But I'm not sure how to iterate each row without a function. And I can't send tables into the a function to iterate so I'm back at square one.
I could do it manually but OldTable contains a few records (1000 or so) so it seems like automation would be preferable.
Is there a way to either:
Iterate over OldTable row by row, run the row through Split(), add the result to NewTable for all rows in OldTable. Either by a function or through regular sql-transactions
Re-write Split() to take a table variable after all
Get rid of the function altogether and just do it in sql transactions?
I'd prefer to not use procedures (don't know if there is a solutions with them either) mostly because I don't want the functionality inside of the DB to be exposed to the outside. If, however that is the "best"/only way to go I'll have to consider it. I'm quite (read very) new to SQL so it might be a needless worry.
Here is my Split() function if it is needed:
CREATE FUNCTION Split (
#string nvarchar(4000),
#delimitor nvarchar(10),
#indexint = 0
)
RETURNS #splitTable TABLE (id int, string nvarchar(4000) NOT NULL) AS
BEGIN
DECLARE #startOfSubString smallint;
DECLARE #endOfSubString smallint;
SET #startOfSubString = 1;
SET #endOfSubString = CHARINDEX(#delimitor, #string, #startOfSubString);
IF (#endOfSubString <> 0)
WHILE #endOfSubString > 0
BEGIN
INSERT INTO #splitTable
SELECT #index, SUBSTRING(#string, #startOfSubString, #endOfSubString - #startOfSubString);
SET #startOfSubString = #endOfSubString+1;
SET #endOfSubString = CHARINDEX(#delimitor, #string, #startOfSubString);
END;
INSERT INTO #splitTable
SELECT #index, SUBSTRING(#string, #startOfSubString, LEN(#string)-#startOfSubString+1);
RETURN;
END
Hope my problem and attempt was explained and possible to understand.
You are looking for cross apply:
SELECT t.index, s.item
FROM OldTable t CROSS APPLY
(dbo.split(t.list, ',')) s(item);
Inserting in the new table just requires an insert or select into clause.

Select all records where function is false

I have a validation function that I'd like to run on each id in a table, and return only those ids which are invalid. Code that I've tried:
select myID
from myDB.dbo.myTable
where (myDB.dbo.validateID(myID) = 0)
Where myDB.dbo.validateID is a scalar-valued function.
This works but returns null for all the valid IDs - I want to only return the invalid ones. What is the most efficient way to return all the invalid rows using this function?
Update:
The validateID function returns 1 if the ID is valid, 0 if it isn't.
My code above returns null if the ID is valid, and the ID if it's not. I want it to instead only return the invalid IDs, without all the null results.
select myID
from myDB.dbo.myTable
where (myDB.dbo.validateID(myID) = 0)
This works but returns null for all the valid IDs
That is simply not possible. If you select myID, you get myID, so if you get null, then myID must be null, and your validateID function is detecting that as invalid.
If your function should treat null as a valid ID, you need to fix your function so that myDB.dbo.validateID(null) returns 1.
If your function should treat null as neither a valid nor an invalid ID, you need to fix your function so that myDB.dbo.validateID(null) returns null.
If your function should treat null as an invalid ID, but you still want to exclude null results, when just add a condition myID is not null to your selection.
I tried the same logic but am getting proper output as expected. Am getting rows returned only when function outputs 0 (ie) Invalid records. I don't see NULL values for valid records.
create table dbo.Contracts1(id int,name varchar(50),pric int)
INSERT INTO dbo.Contracts1
VALUES ( 1,'invalid',40000),
(2,'valid',50000),
(3,'valid',35000),
(4,'invalid',40000)
CREATE FUNCTION Testt(#id INT)
returns INT
AS
BEGIN
DECLARE #ret INT
IF EXISTS (SELECT 1
FROM dbo.Contracts1
WHERE name = 'Valid'
AND id = #id)
SELECT #ret = 1
ELSE
SELECT #ret = 0
RETURN #ret
END
SELECT *
FROM dbo.Contracts1
WHERE dbo.Testt(id) = 0
OUTPUT
id name pric
-- ------- -----
1 invalid 40000
4 invalid 40000

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.