ALTER TABLE with programmatically determined constant DEFAULT value - sql-server

I am trying to add a column (MSSQL 2005) to a table (Employee) with a default constraint of a primary key of another table (Department). Then I am going to make this column a FK to that table. Essentially this will assign new employees to a base department based off the department name if no DepartmentID is provided.
This does not work:
DECLARE #ErrorVar INT
DECLARE #DepartmentID INT
SELECT #DepartmentID = DepartmentID
FROM Department
WHERE RealName = 'RocketScience'
ALTER TABLE [Employee]
ADD [DepartmentID] INT NULL
CONSTRAINT [DepartmentIDOfAssociate] DEFAULT (#DepartmentIDAssociate)
SELECT #ErrorVar = ##Error
IF (#ErrorVar <> 0)
BEGIN
GOTO FATAL_EXIT
END
The Production, Test, and Development databases have grown out of synch and the DepartmentID for the DepartmentName = ‘RocketScience’ may or may not be the same so I don’t want to just say DEFAULT (somenumber). I keep getting “Variables are not allowed in the ALTER TABLE statement” no matter which way I attack the problem.What is the correct way to do this? I have tried nesting the select statement as well which gets “Subqueries are not allowed in this context. Only scalar expressions are allowed.”
In Addition, what would be really great I could populate the column values in one statement instead of doing the {ALTER null} {Update values} {ALTER not null} steps. I read something about the WITH VALUES command but could not get it to work.
Thanks!!!

The accepted answer worked great (Thanks marc_s) but after I thought about it for a while I decided to go another route.Mainly because there has to be a function left on the server which I think ends up being called every time an employee is added.If someone messed with the function later then no one could enter an employee and the reason would not be obvious.
(Even if that is not true then there are still extra functions on the server that do not need to be there)
What I did was assemble the command dynamically in a variable and then call that using the EXECUTE command.
Not only that but since I used the DEFAULT keyword with NOT NULL the table was back populated and I didn't have to run multiple commands to get it done. I found that one out by luck...
DECLARE #ErrorVar INT
DECLARE #DepartmentIDRocketScience INT
DECLARE #ExecuteString NVARCHAR(MAX)
SELECT #DepartmentIDRocketScience = DepartmentID
FROM Department
WHERE RealName = 'RocketScience'
SET #ExecuteString = ''
SET #ExecuteString = #ExecuteString + 'ALTER TABLE [Employee] '
SET #ExecuteString = #ExecuteString + 'ADD [DepartmentID] INT NOT NULL '
SET #ExecuteString = #ExecuteString + 'CONSTRAINT [DF_DepartmentID_RocketScienceDepartmentID] DEFAULT ' +CAST(#DepartmentIDAssociate AS NVARCHAR(MAX))
EXECUTE (#ExecuteString)
SELECT #ErrorVar = ##Error
IF (#ErrorVar <> 0)
BEGIN
GOTO FATAL_EXIT
END

You could wrap the code to find your department ID into a stored function and use that in your DEFAULT constraint statement:
CREATE FUNCTION dbo.GetDepartment()
RETURNS INT
AS
BEGIN
DECLARE #DepartmentID INT
SELECT #DepartmentID = DepartmentID
FROM Department
WHERE RealName = 'RocketScience'
RETURN #DepartmentID
END
And then:
ALTER TABLE [Employee]
ADD [DepartmentID] INT NULL
CONSTRAINT [DepartmentIDOfAssociate] DEFAULT (dbo.GetDepartment())
Does that help?
Marc

If you apply your foreign key constraint after adding the column, you can use any value for the default value when altering the table. Then you can run an update statement with your variable value.
ALTER TABLE Employee
ADD DepartmentID INT NOT NULL
Default -1;
UPDATE Employee
SET Employee.DepartmentID = #DepartmentID
WHERE DepartmentID = -1;
ALTER TABLE Employee
ADD CONSTRAINT fk_employee_to_department FOREIGN KEY (DepartmentID)
REFERENCES Department;

Related

Compare two KeyValue types data in SQL Server 2012

I have the below data available in a table
DECLARE #AddressTbl As Table (ID int identity,Address varchar(100))
INSERT INTO #AddressTbl
VALUES ('State:AndhraPradesh,Dist:Prakasam')
Next time when I enter the same value I should be notified that this value exists in the table.
For this I will use an sp with warning message that the data is available. But I want how to implement the logic to compare the data.
Create Procedure usp_InsertAddress
(
#Address varchar(100)
)
AS
DECLARE #ID INT
SELECT #ID=(SELECT ID FROM #AddressTbl WHERE Address = #Address)
IF #ID IS NULL
BEGIN
INSERT INTO #AddressTbl
VALUES ('State:AndhraPradesh,Dist:Prakasam')
END;
I may enter the address like 'Dist:Prakasam,State:AndhraPradesh'
and there may be some blank spaces also. So need to parse the address and check the key and values.
I will use permanent table instead of table variable.
Appreciate your help.
You can use if exists for check. Or you can add unique CONSTRAINT in your table. The UNIQUE constraint ensures that all values in a column are different. This will return error if you are trying to insert duplicate value.
Create Procedure usp_InsertAddress
(
#Address varchar(100)
)
AS
if exists(SELECT ID FROM #AddressTbl WHERE Address = #Address)
begin
select 'Value is Already Exist in Table'---For Warning
end
else
BEGIN
INSERT INTO #AddressTbl
VALUES ('State:AndhraPradesh,Dist:Prakasam')
select 'Value Inserted Sucessful'---For Success
END;

How to increment primary key not set to identity in merge statement in SQL Server

I have a case that I want to do MERGE statement on a table with a primary key not set to identity and I want to increment the primary key column value within the INSERT part of the MERGE statement.
declare int variable to grab the max Id
declare SQL variable to create sequence dynamically to make start with the max Id determined before
now alter your table to make the primary key column has default constraint that get the next value of the created sequence
do you merge statement
clean up the sequence and the default constraint
Code sample:
DECLARE #id INT;
SELECT #id = MAX(Id) + 1
FROM dbo.Table;
DECLARE #sql NVARCHAR(MAX);
IF OBJECT_ID('tempSeqTable') IS NOT NULL
DROP SEQUENCE [dbo].[tempSeqTable];
SET #sql
= N'CREATE SEQUENCE [dbo].[tempSeqTable] AS INT START WITH ' + CAST(#id AS NVARCHAR)
+ N'INCREMENT BY 1';
EXEC sp_executesql #sql;
ALTER TABLE dbo.Table
ADD CONSTRAINT Id_Default DEFAULT NEXT VALUE FOR tempSeqTable FOR Id;
-- do the merge statement.
ALTER TABLE dbo.Tabel DROP CONSTRAINT Id_Default;
DROP SEQUENCE [dbo].[tempSeqTable];

How to set the default parameter value of a function for a user-defined table type?

I have a user-defined table type:
CREATE TYPE [dbo].[EntityKeysTable] AS TABLE
(
[EntityKey] [uniqueidentifier] NOT NULL,
PRIMARY KEY CLUSTERED( [EntityKey] ASC) WITH (IGNORE_DUP_KEY = OFF)
)
And a function that uses it... But that function is being used by different processes in our system and I don't want to go in and have to deal with the new parameter everywhere, so I rather just make it optional by having a default value in it...
ALTER FUNCTION [dbo].[fnMyFunctionNameHere]
(#siteId UNIQUEIDENTIFIER,
#entityKeys AS EntityKeysTable READONLY,
#partialDeploy AS BIT = false)
RETURNS TABLE
AS
RETURN (
WITH Overrides AS (
SELECT * ....
I did it for the #partialDeploy but unsure about how to do the same for the #entityKeys.
I am sorry for my prior misleading information.
Table valued functions cannot be null. But you can define them with no records in it. So, you would check for (select count(*) from #entityKeys) = 0 to check if there are entityKeys or not.
if (select count(*) from #entityKeys) = 0
--load #entityKeys as your wish
Some time ago I also searched for the answer to this question, but found nothing. So I did such:
create PROCEDURE dbo.Test
#table dbo.testType readonly
AS
BEGIN
declare #table2 dbo.testType
if(not exists(select * from #table))
.... do something like
insert into #table2(t1,t2) values ....
else
insert into #table2(t1,t2) select t1,t2 from #table
... do main code using #table2
END
User defined table types is optional. So you may not pass it to function.

Check Constraints using Functions

I was taking some time trying to write a check constraint that is pointed to a function and I found this article about it:
article about check constraints using functions
My question is: Check Constraints are definitively a better option than triggers?
here you have an example:
CREATE TABLE tmgr.MyTable
(
id INT IDENTITY PRIMARY KEY,
NAME NVARCHAR(50),
TypeId int
)
CREATE FUNCTION dbo.fn_CheckConstraintType
(
#Entity nvarchar(128),
#TypeId int
)
RETURNS int
AS
BEGIN
DECLARE #ReturnValue INT = 0
IF EXISTS (SELECT 1
FROM dbo.TypeEntity
WHERE Entityid = ( SELECT Entityid
FROM dbo.Entity
WHERE Name = #Entity
)
AND TypeId = #TypeId)
SET #ReturnValue = 1
RETURN #ReturnValue
END
Here you have the check constraint
ALTER TABLE dbo.MyTable WITH NOCHECK ADD CONSTRAINT CK_MyTable_CheckConstraintType CHECK (dbo.fn_CheckConstraintType('MyTableType', [TypeId])=1)
I want to be sure that this approach is the best option or there is any other option before using triggers. Check Constraints happen before the insert/update statement is this the main different of using triggers? too many question! sorry!
Thanks

SQL defaults - best practice?

What is the best thing to do in the CREATE and UPDATE stored procedures for a table with default constraints?
When I create a new column for a table, I try to set a propper default value (Default constraint).
Example:
CREATE TABLE Orders
(
O_ID INT NOT NULL
,State INT DEFAULT 0 -- 0 => Not Verified, 1 => Verified, 2 => Processing ....
,P_ID INT
,OrderDate DATE DEFAULT GETDATE()
)
What is the best thing to do in the CREATE and UPDATE stored procedures for this table?
Use the same defaults as in the constraint?
CREATE PROCEDURE UpdateOrder
(
#O_ID INT
,#State INT = 0
,#P_ID INT
,#OrderDate DATE
)
AS
UPDATE
Orders
SET
State = #State
,P_ID = #PID
,OrderDate = #OrderDate
WHERE
O_ID = #O_ID
That would be kind of repetitive, as it's already defaulted in the table.
On the other hand, it allows your parameters to be optional. I would say your choices are to default them to the same as the table (as you suggest), or to default them to null and the table will fill in the default values. The second way is less repetitive and error-prone.
If you want OrderDate to be updated during UPDATE, you need to include it in the UPDATE statement and use getdate() in place of #OrderDate
UPDATE
Orders
SET
State = #State ,
P_ID = #PID ,
OrderDate = getdate()
WHERE O_ID = #O_ID
To comply with DRY, one workaround would be to store your defaults in a table maybe:
CREATE TABLE dbo.MyDefaults
(
OrderState INT
);
INSERT dbo.MyDefaults(OrderState) SELECT 0;
You can't really do this with GETDATE(), so let's just leave that as is - not something you're likely to change, anyway. So now we can pull our default value from the table, instead of hard-coding it. Let's create a scalar function, because we can't use a subquery in a default constraint:
CREATE FUNCTION dbo.DefaultOrderState()
RETURNS INT
AS
BEGIN
RETURN (SELECT TOP (1) OrderState FROM dbo.MyDefaults);
END
GO
(If you have a lot of these, you might consider an EAV approach instead of dedicated columns.)
So now we can have our Orders table, and note that the constant "0" is never mentioned:
CREATE TABLE dbo.Orders
(
O_ID INT NOT NULL,
[State] INT NOT NULL DEFAULT (dbo.DefaultOrderState()),
P_ID INT,
OrderDate DATE NOT NULL DEFAULT (SYSDATETIME())
);
GO
And our update procedure can also grab the defaults (except you haven't defined whether you really want to reset the state to 0 if it is not currently 0 and no value is supplied to the procedure). Again the "0" constant is not mentioned.
CREATE PROCEDURE dbo.UpdateOrder
#O_ID INT,
#State INT = NULL,
#P_ID INT,
#OrderDate DATE = NULL
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.Orders
SET [State] = COALESCE(#State, dbo.DefaltOrderState()),
P_ID = #P_ID,
OrderDate = COALESCE(#OrderDate, OrderDate, SYSDATETIME())
WHERE
O_ID = #O_ID;
END
GO
For the update, you could send in null for "No Change" (or another sentinel if the column is nullable). A similar approach would work for inserts.
CREATE PROCEDURE UpdateOrder
(
#O_ID INT
,#State INT
,#P_ID INT = -1
,#OrderDate DATE
)
AS
UPDATE
Orders
SET
State = IsNull(#State,State)
,P_ID = case when #P_ID < 0 then P_ID else #P_ID end -- assuming this int is not nullable and something like -1 is the default value
,OrderDate = COALESCE(#OrderDate,OrderDate)
WHERE
O_ID = #O_ID

Resources