I'm trying to seed a table, and for the initial data load I would like the CreateBy column to have the value of the default GUID 00000000-0000-0000-0000-000000000000. Is there a simple way to do this? Is there a way to get NEWID() to generate the default value?
ALTER TABLE dbo.YOUR_TABLE ADD CONSTRAINT
DF_UID DEFAULT CAST ('00000000-0000-0000-0000-000000000000' AS UNIQUEIDENTIFIER) FOR COLUMN_NAME
Just add a default to the column
You could just explicitly cast this string:
INSERT INTO mytable
(createdBy)
VALUES (CAST ('00000000-0000-0000-0000-000000000000' AS UNIQUEIDENTIFIER));
I think the simplest approach is to cast a binary zero to a guid:
SELECT CAST(0x0 AS UNIQUEIDENTIFIER);
This will be casted implicitly actually. Try this:
Setting a typed GUID-variable to 0x0
DECLARE #id1 UNIQUEIDENTIFIER;
SET #id1=0x0;
SELECT #id1;
Assign 0x0 directly
DECLARE #id2 UNIQUEIDENTIFIER=0x0;
SELECT #id2;
Or use this against a table's column:
DECLARE #tbl TABLE(Id UNIQUEIDENTIFIER)
INSERT INTO #tbl(id) VALUES(0x0);
SELECT * FROM #tbl;
And it will work as default constraint too:
DECLARE #tbl TABLE(Id UNIQUEIDENTIFIER DEFAULT (0x0), SomeOtherValue VARCHAR(100));
INSERT INTO #tbl(SomeOtherValue) VALUES('GUID by default');
SELECT * FROM #tbl;
Hint: This looks as if you have a user's table with GUIDs as IDs and this is the FK-id pointing to this table. In this case you will have to maintain a user with a zero-id as dummy. To be honest: I would rather recommend to have a nullable column and mark the missing value with a NULL instead with a magic value...
Related
I have a table with ID as identity column, and Code as a varchar column. Code's default value is set to a scalar function X.
Inside that scalar function X, getting the scope_identity() or ##identity returns null. Is there a way to get the inserted identity without using insert trigger?
Any help is greatly appreciated
You could just skip the ID and IDENTITY altogether and generate the code as the primary key. EG
--drop table if exists t
--drop sequence t_seq
--drop function t_key_gen
go
create sequence t_seq start with 1000 increment by 1
go
create or alter function t_key_gen(#i int)
returns char(10)
as
begin
return concat('AB',right(concat('0000000000',#i),8));
end
go
create table t(code char(10) primary key default dbo.t_key_gen(next value for t_seq), a int)
go
insert into t(a) values (1),(2),(3)
select * from t
This is the sample procedure I am using.
create procedure PRO_ProcName
#description varchar(max),
#txn_no varchar
as
begin
declare #txn table (
id bigint,
description varchar(max),
txn_no varchar
);
declare #txn_id bigint;
insert into transactions
(
description,
txn_no
)
output
inserted.description,
inserted.txn_no
into #txn
values
(
#description,
#txn_no
)
select #txn_id = id from #txn;
end
I am getting an error like:
Column name or number of supplied values does not match table definition.
I know that it is because I have id field in my temporary table and it is not getting inserted in the insert into statement. I cannot give value for id because it is the auto increment primary key.
How can I tackle this situation and get id of inserted record into a variable?
The inserted table represents the data that exists in the target table after the insert - so it also includes the auto-generated values, whether they where generated by a default value definition or by an identity definition on the columns - so you need to add inserted.id to the output clause.
However, there are two more things wrong in your procedure.
The first and most important is the fact that you didn't specify a length to the #txn_no varchar parameter. SQL Server will implicitly specify the length of 1 char in this case.
The second is the fact that you are not specifying the columns list of #txn in the output clause.
Here is a improved version of your code with all these issues fixed:
create procedure PRO_ProcName
#description varchar(max),
#txn_no varchar(255) -- Note: I don't know the actual length you need
as
begin
declare #txn table (
id bigint,
description varchar(max),
txn_no varchar
);
declare #txn_id bigint;
insert into transactions
(
description,
txn_no
)
output
inserted.id,
inserted.description,
inserted.txn_no
into #txn(id, description, txn_no)
values
(
#description,
#txn_no
)
select #txn_id = id from #txn;
end
I cannot give value for id because it is the auto increment primary key.
No it isn't. You haven't declared it to be anything of the sort. So we need to fix that first:
declare #txn table (
id bigint IDENTITY PRIMARY KEY,
description varchar(max),
txn_no varchar
);
And then we fix it by specifying a column list in your INTO clause:
output
inserted.description,
inserted.txn_no
into #txn (description, txn_no)
It's always a good habit to specify column lists anyway.
Or if I've misinterpreted your question, and the id should be coming from transactions, then you just add inserted.id as another column in your OUTPUT clause. inserted represents that state of the table after the insert. So you can include columns from it in your OUTPUT clause even if you didn't specify them in the INSERT.
I have an error when loading a procedure telling me
Cannot insert the value NULL into column 'requestID', table 'MCAST.a01.tbl_enrollmentRequests'; column does not allow nulls. INSERT fails.
Now requestID is a UNIQUEIDENTIFIER type of variable. Is UNIQUEIDENTIFIER an auto generated number or not? Below is a sample of my code where you can see requestID.
CREATE PROCEDURE [a01].[usp_auditAcceptRequest]
(#AccountID UNIQUEIDENTIFIER,
#GroupID UNIQUEIDENTIFIER,
#Reason NVARCHAR(45)
)
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO [a01].[tbl_enrollmentRequests] (requestDate, groupID, accountID)
VALUES (SYSDATETIMEOFFSET(), #GroupID, #AccountID)
DECLARE #RequestID UNIQUEIDENTIFIER
SET #RequestID = (SELECT requestID
FROM [a01].tbl_enrollmentRequests
WHERE groupID = #GroupID AND accountID = #AccountID)
INSERT INTO [a01].[tbl_enrollmentAudits] (entryDate, requestID, groupID, accountID, accepted, reason)
VALUES (SYSDATETIMEOFFSET(), #RequestID, #GroupID, #AccountID, 1, #Reason)
DELETE FROM [a01].[tbl_enrollmentRequests]
WHERE requestID = #RequestID
END;
GO
Here is where I am implementing the above procedure
BEGIN
DECLARE #AccountID UNIQUEIDENTIFIER;
DECLARE #GroupID UNIQUEIDENTIFIER;
(SELECT #AccountID = accountID
FROM [a01].[tbl_userAccounts] WHERE accountUsername='saraht');
(SELECT #GroupID = groupID FROM [a01].[tbl_groups] WHERE groupName LIKE '%Foo%');
EXECUTE [a01].[usp_addRequest] #AccountID, #GroupID;
END;
GO
Thanks for your help !!
A uniqueidentifier is a normal column, and if you want to have a automatically assigned value you need to add a default to the column. Typically the functions used for the default are newid() or newsequentialid().
Edit based on the posted table definition; you could use this:
CREATE TABLE [a01].[tbl_enrollmentRequests](
requestID UNIQUEIDENTIFIER PRIMARY KEY DEFAULT (NEWID()),
requestDate DATETIMEOFFSET NOT NULL,
groupID UNIQUEIDENTIFIER REFERENCES [a01].[tbl_groups] (groupID) NOT NULL,
accountID UNIQUEIDENTIFIER REFERENCES [a01].[tbl_userAccounts] (accountID) NOT NULL
);
That being said, you can also pre-generate a uniqueidentifier and assign that to a variable in the stored procedure prior to insertion, since the generated GUID can be assumed not to collide with any existing GUID. The benefit of this is that you know the id of the inserted row even without retrieving it from an OUTPUT clause.
A notice on performance: a significant number of rows with a clustered primary key of random GUIDs (as generated bynewid()) are a performance issue, since the inserts will cause many page splits to occur due to the randomness. The newsequentialid() function pretty much completely resolves the performance problem, but it makes the generated GUIDs guessable, so that this can only be used when "random" IDs are not required.
Is UNIQUEIDENTIFIER an auto generated number or not?
What do you ask us? You have a look at the table definition and see whether a default that sets a new uniqueidentifier is defined or not.
If it is not - then no.
If you try to insert null, then also not (as your insert overrides the default value).
---Edit:
As per the table definition you posted:
requestID UNIQUEIDENTIFIER PRIMARY KEY
no default value defined that sets it. So no.
Is there a way to prevent specifying explicit values in a column? The identity attribute prevents specifying explicit values (as long as IDENTITY_INSERT is off), which is the type of behavior I want.
create table testguid (
ID uniqueidentifier default newsequentialid() not null primary key
,somedate datetime
)
[constraint or trigger here?]
insert into testguid (somedate) values (getdate()) -- this is ok
insert into testguid (ID, somedate) values (newid(), getdate()) -- this is not ok
I want the database to insert values, but I want to prevent any way of specifying them.
You could use an INSTEAD OF TRIGGER to basically re-write the INSERT statement, leaving out the ID column.
CREATE TRIGGER dbo.testguid_beforeinsert
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT dbo.testguid(somedate --, other columns
) SELECT GETDATE() --, other columns
FROM inserted;
END
GO
Probably want something similar for UPDATE, too.
I have a Code (int) in my table, the ID is set to identity. How can I set a default value for my code to be filled by the same value az ID? I mean Identity.
You could use an after insert trigger:
create table TestTable (id int identity, col1 int)
go
create trigger TestTrigger on TestTable after insert
as begin
update TestTable
set col1 = id
where col1 is null
and id in (select id from inserted)
end
go
Test code:
insert TestTable default values
insert TestTable (col1) values (666)
insert TestTable default values
select * from TestTable
In general, I try to stay clear of triggers. In the long run using a stored procedure for insert is much more maintainable:
create procedure dbo.InsertTestRow(
#col1 int)
as
insert TestTable (col1) values (#col1)
if #col1 is null
begin
update TestTable
set col1 = id
where id = SCOPE_IDENTITY()
end
If it always has the same value - why don't you just drop that field. Otherwise it can be maintained with triggers (BEFORE INSERT one).
I'm looking for something in the
default value! If it is null it should
be filled with the same value as id
but if it is provided with some value,
it should keep that value
You could solve the issue by using coalesce in your queries instead.
create table T (ID int identity, ID2 int)
insert into T values (default)
insert into T values (null)
insert into T values (78)
select
ID,
coalesce(ID2, ID) as ID2
from T
Result
ID ID2
-- ---
1 1
2 2
3 78
Assuming your table's ID is an Identity column, you could consider using a constraint:
ALTER TABLE MyTable
ADD CONSTRAINT MyTableCodeDefault
DEFAULT IDENT_CURRENT('MyTable') FOR Code
This works for these use cases:
INSERT INTO MyTable DEFAULT VALUES
INSERT INTO MyTable ({columns NOT including 'Code'})
VALUES ({value list matching insert columns})
INSERT INTO MyTable (Code) VALUES (666)
INSERT INTO MyTable (Code) SELECT 8 UNION SELECT 13 UNION SELECT 21
But it does not work for bulk inserts:
INSERT INTO MyTable ({columns NOT including 'Code'})
SELECT {value list matching insert columns}
UNION
SELECT {value list matching insert columns}
UNION
SELECT {value list matching insert columns}
This restriction may seem onerous, but in my practical experience, it's rarely a problem. Most of the use cases I've encountered that need a default value involve user/UI 'convenience': don't force the user to pick a value if they don't want to.
OTOH, rarely do I encounter bulk insert situations where it's impractical to specify the value for the columns you're targeting.
You could use computed column, like this:
if object_id('TempTable') is not null drop table TempTable
create table TempTable (Id int identity(1,1), Code as Id)
insert into TempTable
default values
insert into TempTable
default values
insert into TempTable
default values
select * from TempTable
Of course if you have other columns, then you dont need default values:
if object_id('TempTable') is not null drop table TempTable
create table TempTable (Id int identity(1,1), Code as Id, SomethingElse int)
insert into TempTable (SomethingElse)
select 10 union all
select 11 union all
select 12
select * from TempTable
But, like zerkms said - why do you need two columns that are same?
If the field is an Identity field in SQL Server, the database engine will take care of its value. What we normally do is to read the record back (after inserting) to get to the generated Id.
EDIT: It sounds like you are trying to "override" the identity? If so, before you insert, run:
SET IDENTITY_INSERT [tableName] ON
You'll have to be careful not to insert a value that already exists. This can get tricky, though. So maybe consider removing the identity property altogether, and managing the default values yourself?