First of all, thank you guys. You always know how to direct me when I can't even find the words to explain what the heck I'm trying to do.
The default values of the columns on a couple of my tables need to be equal the result of some complicated calculations on other columns in other tables. My first thought is to simply have the column default value equal the result of a stored procedure. I would also have one or more of the parameters pulled from the columns in the calling table.
I don't know the syntax of how to do it though, and any time the word "stored" and "procedure" land next to each other in google I'm flooded with info on Parameter default values and nothing relating to what I actually want.
Half of that was more of a vent than a question...any ideas though? And plz plz don't say "Well, you could use an On-Insert Trigger to..."
You can't have the default be the result of a stored procedure, it has to be a function. If you can convert the procedure into a function, then you can use that function. If you cannot, then you must use a trigger.
You would have to convert that stored procedure to a user-defined function. There are different types of UDF's - the one you're looking at would be the scalar UDF - returning a single value.
So for instance you could create a function like this:
CREATE FUNCTION dbo.YourDefaultFunction(#Input1 INT, #Input2 VARCHAR(10))
RETURNS VARCHAR(100)
AS BEGIN
DECLARE #Result VARCHAR(100)
SET #Result = #Input2 + ' - giving: ' + CAST(#Input1 AS VARCHAR(5))
RETURN #Result
END
Of course, yours would be a lot more complicated :-)
Once you've done that, you can define this to be the default for a column of type VARCHAR(100) - either directly when declaring the table, or later on via an ALTER TABLE statement:
CREATE TABLE dbo.YourTable(.......
SomeColumn VARCHAR(100)
CONSTRAINT DF_YourTable_SomeColumn
DEFAULT (dbo.YourDefaultFunction(417, 'Test')),
.....)
or :
ALTER TABLE dbo.YourTable
ADD CONSTRAINT DF_YourTable_SomeColumn
DEFAULT (dbo.YourDefaultFunction(4711, 'Test2'))
FOR SomeColumn
Unfortunately, you cannot pass other columns as parameters to your function when defining it as a default value for a column.
Does that help??
Related
I am trying to add a check constraint to my table in SQL database so that user can only enter the work_group in the column that is in the 'approved list' - dbo.work_groups.
I've read a few forums and created the following table, user defined function and constraint.
dbo.work_groups is populated with the list of work groups, i.e. 'Admin','Accountant', 'Engineer'.
When I enter the above work groups into dbo.test it accepts them. But it also accepts any other values.
How can I limit the constraint only to those work groups that are listed in the dbo.work_groups.
What have I done wrong?
Thanks.
-----test table
CREATE TABLE [dbo].[test]
([testname] nvarchar NOT NULL)
-----user defined function
CREATE FUNCTION [dbo].[check_work_group](#testname NVARCHAR(50))
RETURNS NVARCHAR(50)
AS
BEGIN
RETURN (SELECT work_group FROM [dbo].[work_groups] WHERE work_group=#testname)
END;
-----constraint
ALTER TABLE test ADD constraint CK_testname
CHECK (testname=dbo.check_work_group(testname));
I think the issue with the function. When there is no record in the work_groups table for a given parameter value, it is returning null, which is causing the issue. One way to solve it is by forcing the function to return a value that does not exist in work_groups table ('-1' in the example below) when no record is found for the given parameter value.
CREATE FUNCTION [dbo].[check_work_group](#testname NVARCHAR(50))
RETURNS NVARCHAR(50) AS
BEGIN
RETURN COALESCE((SELECT work_group FROM [dbo].[work_groups] WHERE work_group=#testname), '-1');
END;
I am sure the answer is NO, but I'll ask the expert anyway ;)
I've declared a table variable in my stored procedure.
DECLARE #OrderMapIds TABLE
(
OrderId INT NOT NULL,
NewOrderId INT NOT NULL
);
INSERT INTO #OrderMapIds (OrderId, NewOrderId)
SELECT [OrderId], [OrderId] FROM [tblOrder]
...
...
...
EXEC [AS.uspOrder_MoveOrder] #OrderMapIds = #OrderMapIds; --I need to move order ids based on the mapped id
I need to pass #OrderMapIds to [AS.uspOrder_MoveOrder]. The question is how?
CREATE PROCEDURE [AS.uspOrderItem_CopyRecord]
(
#OrderMapIds AS TABLE -- This thrown error
)
AS
BEGIN
...
...
...
END;
Now, I can accomplish this problem using Table-Valued Parameter (TVP). But if I could pass it without TVP, then it will be better (so I don't have to create TVP for small stuff).
Now, after looking at Google, I am sure the answer is NO (ie. I need to create TVP to accomplish task above). But I thought to ask the question in hope I might have missed something.
Any help is greatly appreciated.
Thanks
You are correct. The answer is indeed No.
In SQL Server, the only way to pass a table to a stored procedure is using a user defined table type.
You do have some confusion in the terms, though.
TVP is the parameter itself - so even if you could just pass any table variable a stored procedure - it would still be a Table Valued Parameter.
What you want to avoid (but can't) is a User Defined Table Type.
If this was allowed, you would end up with a stored procedure that takes in a table valued parameter with an unknown structure - And this could lead to errors, extremely cumbersome code, and worst of all - silently using the wrong data.
An interesting alternative, especially when the table structure is variable/dependent will be to convert data as JSON/XML ( data type for parameter will be NVARCHAR).
Way to go in your example will be
DECLARE #OrderMapIds NVARCHAR(MAX)=
(
SELECT [OrderId], [OrderId] FROM [tblOrder] FOR JSON PATH
);
...
...
...
EXEC [AS.uspOrder_MoveOrder] #OrderMapIds = #OrderMapIds;
I need to get a userstamp into a table and have not managed to figure out how the GENERATED FOR EACH ROW ON UPDATE AS statement works with the SESSION_USER variable in DB2 10.5 (LUW).
Managed to get an implementation working using a function which has a fake variable for forcing the evaluation in update statements:
CREATE OR REPLACE FUNCTION XXX.CURRENT_USER( tmp varchar(128))
SPECIFIC xxx.XXX_CURRENT_USER
RETURNS VARCHAR(128)
CONTAINS SQL DETERMINISTIC NO EXTERNAL ACTION
BEGIN
RETURN session_user ;
END
GO
CREATE TABLE xxx (
i INTEGER,
t VARCHAR(128) GENERATED ALWAYS AS (XXX.CURRENT_USER(i))
)
However, would be nice have less "hacky" implementation for a basic thing like this.
For the time stamps there is that "FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP" statement but no equivalent for other register variables it seems.
Help is very much appreciated
Does this work?
CREATE TABLE xxx (
i INTEGER,
t VARCHAR(128) WITH DEFAULT session_user
);
I don't have DB2 on hand to check, but this is very similar to the syntax used in other databases (although the more typical syntax does not use WITH).
I have 2 tables
Account(AccountId, Encoding)
DeviceAccountMap(AccountId, DeviceId)
Now I need to fetch the devices from the DeviceAccountMap. I pass a list of AccountId to a stored procedure and while fetching the DeviceId from the DeviceAccountMap table I need to compare the Encoding value for each account with a particular value.
Which is the easy way to do this? I am totally lost.
The select clause in the stored procedure will look something like this:
DECLARE #Accounts [usp].[Array]
and [usp].[Array] is defined as below
CREATE TYPE [usp].[Array] AS TABLE
(
Value VARCHAR(36) NULL
)
SELECT
DeviceId,
AccountEncoding = A.Encoding
FROM
usp.DeviceControllerAccountMap DCAM
INNER JOIN
usp.Account A ON (DCAM.AccountId = A.AccountId)
WHERE
DCAM.AccountId IN (SELECT Value From #AccountIds)
AND DCAM.IsShared = 1
AND AccountEncoding LIKE A.Encoding + '.%'
In other words I need to fetch the encoding value for each account and use that in this where clause.
So you can look up information on Table-Valued Parameters (TVPs) in T-SQL.
Here is an article by Erland Sommarskog.
You can refer to this StackOverflow answer to see an example of C# code calling a stored procedure that uses a TVP. I believe TVPs require SQL Server 2008 or higher.
TVPs, as far as I understand, provide a way to make your own data type in sql server that gets treated as if it was a table. You're doing this when you declare your Array type and then when you use the #AccountIds in your stored procedure's select statement.
CREATE TYPE [usp].[Array] AS TABLE -- maybe choose a more descriptive name than 'Array'
(
Value VARCHAR(36) NULL -- choose a more descriptive name than 'Value'
)
CREATE PROCEDURE [usp].[your_procedure_name]
#AccountIds [usp].[Array] READONLY -- use TVP as a parameter
AS
SELECT …
It is not clear form your question details whether you also mean to have a parameter in the stored procedure for the Encoding. It seems like you're looking for accounts whose Encodings start with a period '.'.
So first, create your type, like you're doing.
Then create your stored procedure.
Then test your stored procedure, something like this:
DECLARE #mylist Array -- make TVP sample data
INSERT #mylist(Value) VALUES(1),(11),(27),(123) -- insert some values
exec your_procedure_name #mylist -- run stored procedure
The following line is completely unnecessary. The JOIN to Account does this filter for you.
DCAM.AccountId IN (SELECT Value From #AccountIds)
Or am I missing something?
I am using a stored procedure to insert records into a table. And do this at least 12 times in a loop to insert multiple records which is very inefficient.
here is the procedure as CREATED
Create PROC [dbo].[SP_INSERT_G_SAMPLING]
#GameID INT,
#ScoreID INT
as
begin
INSERT INTO GAMESCORE (GAMEID, SCOREID) VALUES
(#GameID, #ScoreID)
end
I pass on the values ex(1,3) and loop with more values from the website.
I want to however pass on all the values at one time like (1,3),(4,5),(8,9)
and then alter the above procedure to receive and insert multiple rows.
ALTER PROC [dbo].[SP_INSERT_G_SAMPLING]
#totalinsert nvarchar(Max)
INSERT INTO GAMESCORE (GAMEID, SCOREID) VALUES
(#totalinsert)
with #totalinsert being like (1,3),(4,5),(8,9) pushed from the webpage.
any help is greatly appreciated
What you're going to have to do is write a table valued function which accepts the multi-value string and breaks it out into a table object. If you can change your source to use a record delimiter instead of having comma sets it would be slightly easier to process. An example of that would look like this.
The below is pure psuedo and has not been validated in any way, just meant to give you a rough idea of where to go.
ex: #TotalInsert = 1,2|4,5|8,9
DECLARE #Results TABLE
(
value1 INT,
value2 INT
)
DECLARE #setlist VARCHAR(max);
WHILE Len(#TotalInsert) > 0
BEGIN
SET #setlist = LEFT(#totalinsert, Charindex('|', #totalinsert))
INSERT INTO #results
SELECT LEFT(#setlist, Charindex(',', #setlist) - 1),
RIGHT(#setlist, Charindex(',', Reverse(#setlist)) + 1)
SET #totalinsert = RIGHT(#totalinsert, Len(#totalinsert) - Len(#setlist))
END
I'm assuming you're using .NET for your website since you're also using SQL Server.
Have a look at table valued parameters, this page also includes a nice example of how to use the table valued parameters in .NET.
Check here for a better example of making a stored procedure with a table valued parameter in T-SQL.
Here is the full discussion:
http://www.sommarskog.se/arrays-in-sql-2005.html#XMLlist%20of%20values
Personally, I sent xml to the stored procedure, I "shred it" into #variable or #temp tables, then I do my INSERT/UPDATE/MERGE/DELETE from there.
Here is a fuller discussion on xml-shredding.
http://pratchev.blogspot.com/2007/06/shredding-xml-in-sql-server-2005.html
My personal trick is to create a strong dataset, populate the strong dataset with rows, and use the ds.GetXml() to send the xml down to the TSQL. With a strong dataset, I get strong-typing when populating the values. But at the end of the day, dataset is just some super fancy xml.