Microsoft T-SQL - User Defined Functions
Microsoft T-SQL - User Defined Functions
http://www.learn-with-video-tutorials.com
DROP FUNCTION fn_get_car_name; GO CREATE FUNCTION dbo.fn_get_car_name (@p_model VARCHAR(40)) RETURNS VARCHAR(100) AS BEGIN DECLARE @p_result VARCHAR(100); SET @p_result = (SELECT car + ' ' + model FROM cars WHERE model = @p_model); RETURN @p_result; END;
http://www.learn-with-video-tutorials.com
CASE WHEN NOT EXISTS (SELECT * FROM T1 WHERE id = 1) THEN 1 ELSE (SELECT MIN(id) + 1 FROM T1 AS A WITH (NOLOCK) WHERE NOT EXISTS (SELECT * FROM T1 AS B WITH (NOLOCK) WHERE B.id = A.id + 1)) END; END;
This code adds a DEFAULT constraint to ID, which invokes the fn_T1_getid function.
ALTER TABLE T1 ADD DEFAULT(dbo.fn_T1_getid()) FOR id;
The following code inserts three rows, generating the keys 1, 2, and 3; deletes the row with the key 2; and inserts another row, generating the key 2.
INSERT INSERT INSERT DELETE INSERT INTO INTO INTO FROM INTO T1(col) VALUES('a'); T1(col) VALUES('b'); T1(col) VALUES('c'); T1 WHERE id = 2; T1(col) VALUES('d');
Notice that key 2 was assigned to the row that was inserted last, because the row with the key 2 was previously deleted.
http://www.learn-with-video-tutorials.com
The error occurs because the function doesnt meet one of the requirements for indexing, which says that the function must be schema bound. As you can see, the error message itself is not too helpful in indicating the cause of the error or in suggesting how to fix it. You need to realize that to fix the problem, you should alter the function by adding the SCHEMABINDING option. Try adding the computed column with the UNIQUE constraint again. This time your code runs successfully.
ALTER FUNCTION dbo.fn_add(@p_id INT) RETURNS INT WITH SCHEMABINDING AS BEGIN RETURN @p_id + 1; END; ALTER TABLE T1 ADD col2 AS dbo.fn_add(id) CONSTRAINT UK_T1_col2 UNIQUE;
Its a bit trickier when you try to create a PRIMARY KEY constraint on such a computed column. To see how this works, first drop the existing PRIMARY KEY from T1. Next, attempt to add another computed column called col3 with a PRIMARY KEY constraint.
ALTER TABLE T1 DROP CONSTRAINT PK_ID; ALTER TABLE T1 ADD col3 AS dbo.fn_add(id) CONSTRAINT PK_ID PRIMARY KEY;
The attempt fails, generating the error. You must explicitly guarantee that col2 never ends up with a NULL. You can achieve this by defining the column as PERSISTED and NOT NULL.
ALTER TABLE T1 ADD col3 AS dbo.fn_add(id) PERSISTED NOT NULL CONSTRAINT PK_ID PRIMARY KEY;
http://www.learn-with-video-tutorials.com
Unlike scalar and multistatement table-valued UDFs, you dont specify a BEGIN/END block in an inline UDFs body. All you specify is a RETURN clause and a query. In the functions header, you simply state that it returns a table.
IF OBJECT_ID('fn_get_cars') IS NOT NULL DROP FUNCTION fn_get_cars; GO CREATE FUNCTION dbo.fn_get_cars (@p_car_name VARCHAR(40)) RETURNS TABLE AS RETURN SELECT Car, Model FROM Cars WITH (NOLOCK) WHERE Car = @p_car_name;
http://www.learn-with-video-tutorials.com
SET @p_letter = UPPER(SUBSTRING(@p_car_name, 2, 1)); INSERT INTO @t_cars SELECT Car, Model FROM Cars WITH (NOLOCK) WHERE UPPER(SUBSTRING(Car, 1, 1)) = @p_letter; RETURN END;
This function will return all the cars, which name is equal to the p car name parameter, and all the cars, which name starts with the second letter of the p car name parameter. In this way, you can run the multistatement table-valued UDF.
SELECT * FROM dbo.fn_get_cars2('Ferrari'); SELECT * FROM dbo.fn_get_cars2('BMW');
Per-Row UDFs
Nondeterministic functions are functions that are not guaranteed to return the same output when invoked multiple times with the same input. When you invoke nondeterministic built-in functions in a query (such as RAND and GETDATE), those functions are invoked once for the whole query and not once per row. The only exception to this rule is the NEWID function, which generates a globally unique identifier (GUID). NEWID is the only nondeterministic built-in function that will be invoked once per row. To demonstrate this behavior of nondeterministic functions, run the following code. Suppose that you needed to invoke the RAND function for each row. You might have thought of invoking RAND from a UDF and then invoking the UDF in an outer query, knowing that a UDF is invoked once per row.
SELECT RAND(), GETDATE(), NEWID(), * FROM cars WITH (NOLOCK); IF OBJECT_ID('fn_rand') IS NOT NULL DROP FUNCTION fn_rand; GO CREATE FUNCTION fn_rand() RETURNS FLOAT AS BEGIN RETURN RAND(); END;
However, this attempt fails and produces the error. The error tells you that your function is not allowed to have side effects, and the RAND function does change an internal state (for use in a subsequent invocation of RAND). A back door allows you to implicitly invoke RAND from a UDF. Create a view that invokes RAND and query the view from the UDF.
http://www.learn-with-video-tutorials.com
CREATE VIEW v_rand AS SELECT RAND() AS R; CREATE FUNCTION fn_rand() RETURNS FLOAT AS BEGIN RETURN (SELECT R FROM v_rand); END; SELECT *, RAND() FROM cars WITH (NOLOCK); SELECT *, dbo.fn_rand() FROM cars WITH (NOLOCK);
We have achieved the desired effect, which shows the second query.
http://www.learn-with-video-tutorials.com