Table-valued function refresh - sql-server

I have a table-valued function (TVF) in SQL Server that looks like this:
CREATE FUNCTION TVF_xyz(#AuditKey INT)
RETURNS TABLE
AS
RETURN
SELECT *
FROM xyz
WHERE AUDIT_KEY = #AuditKey
GO
Now, I added a new column to the xyz table.
When I query using TVF_xyz, it doesn't show me the new column (shows all other columns except newly added).
Query:
SELECT TOP 10 *
FROM TVF_xyz (1543)
I would like to know, how to refresh TVF to show new column.
PS: Select * used in TVF to fetch all columns.

After bit of searching, I found sp_refreshsqlmodule (Transact-SQL), its common behavior of TVF.
In order to refresh TVF, following SP needs to be executed:
EXEC sys.sp_refreshsqlmodule 'TVF_xyz'

https://msdn.microsoft.com/en-us/library/bb386954(v=vs.110).aspx
The following SQL function explicitly states that it returns a TABLE. Therefore, the returned rowset structure is implicitly defined.
sample
CREATE FUNCTION ProductsCostingMoreThan(#cost money)
RETURNS TABLE
AS
RETURN
SELECT ProductID, UnitPrice
FROM Products
WHERE UnitPrice > #cost

Related

How to update a table column based on output from a table-based function

I have 2 tables:
Course Table
CourseId
CatalogNumber
MajorVersion
CreatedDate
1
ABC
1.2
1-2-21
2
ABC
1.3
2-2-21
3
123
.4
5-2-21
State Table
Course
MajorVersion
ABC
For every record in the State table (tens of thousands), I want to populate the latest version from the Course table.
I created a table-based function to get the latest version:
function fn_get_latest_major_course
(
#course nvarchar(50)
)
returns table
as
return
(
select top(1) [c].[CourseID], c.[MajorVersion] from [dbo].[Course] c where
([CatalogNumber] = #course)
order by [c].[CreatedDate] desc
);
I'm trying to move the data with this query, passing in the Course column for each State record to the table-based function to pull-back the MajorVersion
update State set MajorVersion = (select MajorVersion from fn_get_latest_major_course(Course))
But when I run this query I get this error:
Column name or number of supplied values does not match table definition.
This works just fine.
declare #catalogNumber nvarchar(50) = 'ABC'
select * from fn_get_latest_major_course(#catalogNumber)
So how do I, for every row in a table, call a table-based function and use the output to update one or more columns?
You would use CROSS APPLY to run your function for each row in State. It's like doing a JOIN on the results of the function.
UDPATE s
SET s.MajorVersion = f.MajorVersion
FROM State s
CROSS APPLY fn_get_latest_major_course(course) f;
Note that this works in your case because your function is returning only a single row. If your function returned more than one row this would potentially blow up quickly.

Function to check value and returns result

I need to create function in SQL to check data in variables or parameters as below
#Category as varchar(50)='ABC,DEF'
#Value as varchar(50)='1,2'
And compare #Category value with Category in table then return value matching from parameter
JOB TABLE ---
JOB CATEGORY
123 ABC
234 DEF
234 SSS
Select JobNo,FUNCTION(#Category,#Value,CATEGORY) from JOB
FINAL RESULTS
JOB VALUE
123 1
234 2
234 0
If category match then return value from parameter else return 0.
If you can't use a static lookup table as mentioned in the comments (for example, perhaps the mapping needs to be dynamic based on data supplied by the application), then this looks like a job for a table valued parameter.
Right now you have two parameters, but it seems to me that the values in the parameters are related. That is to say, right now you have #category = 'ABC,DEF' and #value = '1,2', but I think you intend each "element" in the comma delimited set of "categories" to associate with the "element" in the comma delimiited set of "values" that is in the same position.
Right now the design is brittle, because what would happen if I use parameters like this: #category = 'ABC,DEF,GHI,JKL', #value = '1'?
So, you can make your code more durable, and use the sort of "join-based" lookup table solution being recommended to you in the comments, by using a function that takes a table valued parameter argument. To do this, you first have to create the table valued parameter as a type in your database. We then accept a parameter of that type, and join onto it. In the solution below I have "guessed" at datatypes for category and value that seem reasonable based on the sample data in your question.
Also, I've kept the "structure" of your solution - ie, the function is written in such a way that it can be "applied" against every row in jobs, individually. We don't have to do it this way. We could instead just do the whole query inside the function (ie, including the join to the job table), but perhaps you want to use the function against other tables that also have a cateogry column? I won't try to second guess the overall design here. But I will switch the function to an inline table valued function (which returns one row with one column) rather than a scalar function, for performance reasons.
-- schema and data to match your question
create table dbo.job (job int, category char(3));
insert dbo.job(job, category) values (123, 'ABC'), (234, 'DEF'), (234, 'SSS');
go
-- solution
create type dbo.CategoryValues as table
(
category char(3) unique,
[value] int
);
go
create or alter function dbo.MapCategory(#category char(3), #map dbo.CategoryValues readonly)
returns table as return
(
select [value] from #map where category = #category
);
go
-- to call the function we need to pass a parameter of type
-- dbo.CategoryValues with the mappings we desire
declare #map dbo.CategoryValues;
insert #map values ('ABC', 1), ('DEF', 2)
-- outer apply the function to each row in the job table.
select j.job, [value] = isnull(v.[value], 0)
from dbo.job j
outer apply dbo.MapCategory(j.category, #map) v

SQL Server does not refresh functions

I am using SQL Server 2008 R2. I have two functions under Programmatibility > Functions > Table-valued Functions.
The first one retrieves a collection of all available columns and records.
The second one filters out the first one with an Id check.
Let's say I have a Customers table.
The first function looks like this:
ALTER FUNCTION [dbo].[fnGetCustomers]()
RETURNS TABLE
AS
RETURN
SELECT
C.CustomerId AS Id,
C.CustomerName AS Name
FROM
Customers AS C
and I call it like so SELECT * FROM fnGetCustomers().
The second one looks like this:
ALTER FUNCTION [dbo].[fnGetCustomerById]
(#CustomerId AS INT)
RETURNS TABLE
AS
RETURN
SELECT *
FROM fnGetCustomers()
WHERE (Id = #CustomerId)
and I call it like so SELECT * FROM fnGetCustomerById(1).
My problem is when I add a new column to my Customers table scheme.
First I edit fnGetCustomers to include the new column like so.
ALTER FUNCTION [dbo].[fnGetCustomers]()
RETURNS TABLE
AS
RETURN
SELECT
C.CustomerId AS Id,
C.CustomerName AS Name,
C.CustomerPhone AS Phone
FROM
Customers AS C
The fnGetCustomers works fine after the modification. Though all of the other functions, like fnGetCustomerById, that depend on this specific one miss the new column. Despite using * in the selection query.
I can solve the problem by Right-Click on function > Modify > Execute! on each one of the functions.
I guess something runs internally and refreshes them.
My database uses a great number of functions that depend on this specific basic one, so it's almost impossible to locate and "modify > execute" all of them.
What can be done so the database scheme updates/refresh itself automatically?

How to access the a table ABC_XXX constantly in Teradata where XXX changes periodically?

I have a table in Teradata ABC_XXX where XXX will change monthly basis.
For Ex: ABC_1902, ABC_1812, ABC_1904 etc...
I need to access this table in my application without modifying the code every month.
Is that any way to do in Teradata or any alternate solution.??
Please help
Can you try using DBC.TABLES in subquery like below:
with tbl as (select 'select * from ' || databasename||'.'||tablename as tb from
dbc.tables where tablename like 'ABC_%')
select * from tbl;
If you can get the final query executed in your application, you will be able to query the required table without editing the query.
The above solution is with expectation that the previous month's table gets dropped whenever a new month's table is created.
However, if previous table is not being dropped, then you can try the below approach:
select 'select * from db.ABC_' ||to_char(current_date,'YYMM')
Output will be
select * from db.ABC_1902
execute the output in your application, you will be able to query dynamic table.

How can I get the result from SQL generated Identity? [duplicate]

I'm trying to get a the key-value back after an INSERT-statement.
Example:
I've got a table with the attributes name and id. id is a generated value.
INSERT INTO table (name) VALUES('bob');
Now I want to get the id back in the same step. How is this done?
We're using Microsoft SQL Server 2008.
No need for a separate SELECT...
INSERT INTO table (name)
OUTPUT Inserted.ID
VALUES('bob');
This works for non-IDENTITY columns (such as GUIDs) too
Use SCOPE_IDENTITY() to get the new ID value
INSERT INTO table (name) VALUES('bob');
SELECT SCOPE_IDENTITY()
http://msdn.microsoft.com/en-us/library/ms190315.aspx
INSERT INTO files (title) VALUES ('whatever');
SELECT * FROM files WHERE id = SCOPE_IDENTITY();
Is the safest bet since there is a known issue with OUTPUT Clause conflict on tables with triggers. Makes this quite unreliable as even if your table doesn't currently have any triggers - someone adding one down the line will break your application. Time Bomb sort of behaviour.
See msdn article for deeper explanation:
http://blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/update-with-output-clause-triggers-and-sqlmoreresults.aspx
Entity Framework performs something similar to gbn's answer:
DECLARE #generated_keys table([Id] uniqueidentifier)
INSERT INTO Customers(FirstName)
OUTPUT inserted.CustomerID INTO #generated_keys
VALUES('bob');
SELECT t.[CustomerID]
FROM #generated_keys AS g
JOIN dbo.Customers AS t
ON g.Id = t.CustomerID
WHERE ##ROWCOUNT > 0
The output results are stored in a temporary table variable, and then selected back to the client. Have to be aware of the gotcha:
inserts can generate more than one row, so the variable can hold more than one row, so you can be returned more than one ID
I have no idea why EF would inner join the ephemeral table back to the real table (under what circumstances would the two not match).
But that's what EF does.
SQL Server 2008 or newer only. If it's 2005 then you're out of luck.
There are many ways to exit after insert
When you insert data into a table, you can use the OUTPUT clause to
return a copy of the data that’s been inserted into the table. The
OUTPUT clause takes two basic forms: OUTPUT and OUTPUT INTO. Use the
OUTPUT form if you want to return the data to the calling application.
Use the OUTPUT INTO form if you want to return the data to a table or
a table variable.
DECLARE #MyTableVar TABLE (id INT,NAME NVARCHAR(50));
INSERT INTO tableName
(
NAME,....
)OUTPUT INSERTED.id,INSERTED.Name INTO #MyTableVar
VALUES
(
'test',...
)
IDENT_CURRENT: It returns the last identity created for a particular table or view in any session.
SELECT IDENT_CURRENT('tableName') AS [IDENT_CURRENT]
SCOPE_IDENTITY: It returns the last identity from a same session and the same scope. A scope is a stored procedure/trigger etc.
SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY];
##IDENTITY: It returns the last identity from the same session.
SELECT ##IDENTITY AS [##IDENTITY];
##IDENTITY Is a system function that returns the last-inserted identity value.
There are multiple ways to get the last inserted ID after insert command.
##IDENTITY : It returns the last Identity value generated on a Connection in current session, regardless of Table and the scope of statement that produced the value
SCOPE_IDENTITY(): It returns the last identity value generated by the insert statement in the current scope in the current connection regardless of the table.
IDENT_CURRENT(‘TABLENAME’) : It returns the last identity value generated on the specified table regardless of Any connection, session or scope. IDENT_CURRENT is not limited by scope and session; it is limited to a specified table.
Now it seems more difficult to decide which one will be exact match for my requirement.
I mostly prefer SCOPE_IDENTITY().
If you use select SCOPE_IDENTITY() along with TableName in insert statement, you will get the exact result as per your expectation.
Source : CodoBee
The best and most sure solution is using SCOPE_IDENTITY().
Just you have to get the scope identity after every insert and save it in a variable because you can call two insert in the same scope.
ident_current and ##identity may be they work but they are not safe scope. You can have issues in a big application
declare #duplicataId int
select #duplicataId = (SELECT SCOPE_IDENTITY())
More detail is here Microsoft docs
You can use scope_identity() to select the ID of the row you just inserted into a variable then just select whatever columns you want from that table where the id = the identity you got from scope_identity()
See here for the MSDN info http://msdn.microsoft.com/en-us/library/ms190315.aspx
Recommend to use SCOPE_IDENTITY() to get the new ID value, But NOT use "OUTPUT Inserted.ID"
If the insert statement throw exception, I except it throw it directly. But "OUTPUT Inserted.ID" will return 0, which maybe not as expected.
This is how I use OUTPUT INSERTED, when inserting to a table that uses ID as identity column in SQL Server:
'myConn is the ADO connection, RS a recordset and ID an integer
Set RS=myConn.Execute("INSERT INTO M2_VOTELIST(PRODUCER_ID,TITLE,TIMEU) OUTPUT INSERTED.ID VALUES ('Gator','Test',GETDATE())")
ID=RS(0)
You can append a select statement to your insert statement.
Integer myInt =
Insert into table1 (FName) values('Fred'); Select Scope_Identity();
This will return a value of the identity when executed scaler.
* Parameter order in the connection string is sometimes important. * The Provider parameter's location can break the recordset cursor after adding a row. We saw this behavior with the SQLOLEDB provider.
After a row is added, the row fields are not available, UNLESS the Provider is specified as the first parameter in the connection string. When the provider is anywhere in the connection string except as the first parameter, the newly inserted row fields are not available. When we moved the the Provider to the first parameter, the row fields magically appeared.
After doing an insert into a table with an identity column, you can reference ##IDENTITY to get the value:
http://msdn.microsoft.com/en-us/library/aa933167%28v=sql.80%29.aspx

Resources