I don't know if this is possible, but i would like to know if when we create a table on which a field has a default value, we can make this Default value get the value of another column upon row insertion.
Create Table Countries (
ID Int Not Null Identity(1,1),
CountryCode Char (2) Not Null,
Country Varchar (50) Not Null,
CountryRegion Varchar (50) Null Default ('Country'),
Nationality Varchar (75) Not Null Default ('Not Known'),
InsertDate Datetime2 Not Null Default Getdate(),
Constraint PK_CountryCode Primary Key (CountryCode));
On CountryRegion field, I could place an ('Unknown') default value, but like I said, is it possible this field gets by default the value inserted on Country field if nothing is placed on it?
Using a trigger would do - UPDATE a column right after insertion
CREATE TRIGGER CountriesTrigger ON Countries AFTER INSERT
AS
BEGIN
SET NOCOUNT ON
UPDATE Countries
SET
Countries.CountryRegion = inserted.Country
FROM Countries, inserted
WHERE Countries.ID = inserted.ID
AND inserted.CountryRegion is null
END
I think there is no easy of doing this at TABLE level. There are some workarounds to do this :
1. If you are using stored procs then you can write your logic over there.
2. Trigger is also an option but overhead in terms of execution.
Thanks.
Related
I'm new to SQL Server and I am getting this error "Cannot insert the value NULL into column 'Occupied', table 'DBProjectHamlet.dbo.tblGrave'; column does not allow nulls. INSERT fails. The statement has been terminated."
This is my code for the insert followed by the code to create the table
INSERT INTO tblGrave (GraveName)
SELECT Grave
FROM tblPlotsandOccupants
IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'tblGrave' AND TABLE_SCHEMA = 'dbo')
DROP TABLE dbo.tblGrave;
GO
CREATE TABLE tblGrave
(
GraveID INT IDENTITY (1,1),
GraveName VARCHAR(MAX) NULL,
GraveTypeID INT NOT NULL,
PlotID INT NOT NULL,
Occupied BIT NOT NULL
)
I'm not trying to insert anything into column Occupied, I don't know why this is happening or how to fix it. I just want to insert values into tblGrave (GraveName). Any help would be great.
Exactly! You aren't doing anything with Occupied and that is the problem. The column is specified to be NOT NULL but has no default value. You are not inserting a value, so it gets the default. The default default is NULL, and that is not allowed.
One simple solution is:
INSERT INTO tblGrave (GraveName, Occupied)
SELECT Grave, 0
FROM tblPlotsandOccupants;
This fixes your immediate problem, but will then you will get an error on PlotId.
A more robust solution would add a default value for the NOT NULL columns and declare the rest to be nullable (the default). Something like this:
CREATE TABLE tblGrave (
GraveID INT IDENTITY (1,1) PRIMARY KEY,
GraveName VARCHAR(MAX),
GraveTypeID,
PlotID INT,
Occupied BIT NOT NULL DEFAULT 0
);
When you created your table, you defined that column as "NOT NULL" rather than allowing it to be null.
You need to either allow "Occupied" to be null, set a default value, or define the value that you want upon inserting.
You can even set the value to be ' ' which is blank but isn't null.
EDIT
Note #Gordon's answer for sql examples.
I stumbled upon this definition:
CREATE TABLE dbo.whatever (
[flBlahBlah] BIT DEFAULT ((0)) NOT NULL,
[txCity] NVARCHAR (50) DEFAULT ('') NOT NULL,
[cdFrom] VARCHAR (10) DEFAULT ('') NOT NULL
);
I can't think of a reason to add those default values. Not null string is defaulted to '' and bit is defaulted to 0. Is there a reason for defining these default values? Am I missing something? Is this in some best practice handbook I'm not aware of?
I'd just use:
CREATE TABLE dbo.whatever (
[flBlahBlah] BIT NOT NULL,
[txCity] NVARCHAR (50) NOT NULL,
[cdFrom] VARCHAR (10) NOT NULL
);
The database is in MS SQL Server 2012, now migrating to Azure Database.
For example you create table from a first batch of your question. Then insert value like this
INSERT INTO dbo.whatever (flBlahBlah) VALUES (1)
You will get 1 row dbo.whatever
flBlahBlah txCity cdFrom
1
So if you "forget" to insert in one of the column with default values determined - SQL Server will take care of them.
It is very useful when you got table, in which you need to insert new field. With default value determined you don't need to change SP/query's/other stuff that works with this table.
The column name on table [dbo].[payment_info] must be changed from NULL to NOT NULL. If the table contains data, the ALTER script may not work.
To avoid this issue, you must add values to this column for all rows or mark it as allowing NULL values, or enable the generation of smart-defaults as a deployment option.
CREATE TABLE [dbo].[payment_info]
(
[name] VARCHAR (50) NOT NULL,
[card_no] VARCHAR (50) NULL,
[card_type] VARCHAR (50) NOT NULL,
[tel_no] VARCHAR (50) NULL,
[mob_no] VARCHAR (50) NULL,
[address] VARCHAR (MAX) NULL
);
I cannot change NULLto NOT NULL; when I update it's showing the above warning.
I am using visual studio 2013 asp.net and c#.
If table already exists and is fulfilled with data, you have to update all NULLs in column you want to change on some value which is not NULL. Then ALTER command should work wthout warnings and/or errors.
I am not really sure if I understood your problem correctly, but the warning says it all - you can't switch column to not nullable, if there are nulls already in the column.
You have to update and set some values to empty entries or set DEFAULT value
EDIT:
You should try first:
select *
from [dbo].[payment_info]
where name is null
and check if there are any problems
Right-click on your table in server explorer and click "new query". Type:
ALTER TABLE
table
ALTER COLUMN
column
int NOT NULL;
This error is produced by SSDT. This will happen if you have an existing table and you would like to add a new non-nullable column to it. In order to do so, you must have a default for this new column (the default can be some temporary value).
CREATE TABLE [dbo].[payment_info]
(
[WhateverColumn] VARCHAR (50) NOT NULL DEFAULT 'Foo',
-- and so on
);
If you want to change the default to a more meaningful value, you can write the script to update the table and set the column's value to a more meaningful value in a post deployment. In post deployment you can also now drop the default since it was temporary:
ALTER TABLE WhateverTable
ALTER COLUMN WhateverColumn DROP DEFAULT;
Now your deployment will succeed.
Note: If your column is a foreign-key column, the default has to exist in the parent table even if the value is temporary.
How can I enforce a table to have only one row? Below is what I tried. The UPDATE trigger might work, however, the CREATE trigger definitely will not. For the CREATE, I would like to use SET, however, SET is not supported by SQLite.
CREATE TABLE IF NOT EXISTS `config` (
`id` TINYINT NOT NULL DEFAULT 0,
`subdomain` VARCHAR(45) NOT NULL,
`timezone` CHAR(3) NOT NULL,
`timeout` TINYINT NOT NULL,
`offline` TINYINT NOT NULL,
`hash_config` CHAR(32) NOT NULL,
`hash_points` CHAR(32) NOT NULL,
PRIMARY KEY (`id`));
INSERT INTO config(id,subdomain,timezone,timeout,offline,hash_config,hash_points) VALUES(0,'subdomain','UTC',5,0,'hash_config','hash_points');
CREATE TRIGGER `config_insert_zero`
BEFORE INSERT ON `config`
FOR EACH ROW
BEGIN
-- SET NEW.id=0;
NEW.id=OLD.id;
END;
CREATE TRIGGER `config_update_zero`
BEFORE UPDATE ON `config`
FOR EACH ROW
BEGIN
-- SET NEW.id=0;
NEW.id=OLD.id;
END;
In the general case, to limit the number of rows in a table, you have to prevent any further insert.
In SQLite, this is done with RAISE():
CREATE TRIGGER config_no_insert
BEFORE INSERT ON config
WHEN (SELECT COUNT(*) FROM config) >= 1 -- limit here
BEGIN
SELECT RAISE(FAIL, 'only one row!');
END;
However, if the limit is one, you could instead simply constrain the primary key to a fixed value:
CREATE TABLE config (
id INTEGER PRIMARY KEY CHECK (id = 0),
[...]
);
One idea you may want to consider is to make it appear like the table has only one row. In reality, you keep all previous rows because it's quite possible you will one day want to maintain a history of all past values.
Since there is only one row, there really is no need for an ID column, the purpose of which is to uniquely differentiate each row from all the others. You do need, however, a timestamp which will be used to identify the "one row" which will be the latest row written to the table.
CREATE TABLE `config_history` (
`created` timestamp default current_timestamp,
`subdomain` VARCHAR(45) NOT NULL,
`timezone` CHAR(3) NOT NULL,
`timeout` TINYINT NOT NULL,
`offline` TINYINT NOT NULL,
`hash_config` CHAR(32) NOT NULL,
`hash_points` CHAR(32) NOT NULL,
PRIMARY KEY (`created`)
);
Since you are normally interested in only the last row written (the latest version), the query selects the row with the latest creation date:
select ch.created effective_date, ch.subdomain, ch.timezone, ch.timeout,
ch.offline, ch.hash_config, ch.hash_points
from config_history ch
where ch.created =(
select max( created )
from config_history );
Put a create view config as in front of this query and you have a view that selects only one row, the latest, from the table. Any query against the view returns the one row:
select *
from config;
An instead of trigger on the view can convert Updates to Inserts -- you don't actually want to change a value, just write a new row with the new values. This then becomes the new "current" row.
Now you have what appears to be a table with only one row but you also maintain a complete history of all the past changes ever made to that row.
I have a Stored Procedure that populates a table: This table as indicated in the code below has an identity column which is also the primary key column.
I would like to append the primary key to contain leading letters: Example: ABC123.
Obviously this is not possible because the Primary key column is INT datatype.
So I created an additional column so that I can insert the appended primary key. This works except I have to make the new column Null and I am using an UPDATE statement.
Something tells me there is a better way.
Is there a way I can do this without using UPDATE after the initial Insert and have the new column CategoryID as Not Null?
Table Code:
CREATE TABLE [dbo].[Registration] (
[SystemID] INT IDENTITY (100035891, 1) NOT NULL,
[CategoryID] CHAR (13) NULL,
[FName] VARCHAR (30) NOT NULL,
[LName] VARCHAR (30) NOT NULL,
[MInit] CHAR (1) NULL,
PRIMARY KEY CLUSTERED ([SystemID] ASC)
);
Stored Procedure:
CREATE PROCEDURE [dbo].[uspInsertRegistration]
#FName VARCHAR(30),
#LName VARCHAR(30),
#MInit CHAR(1),
#CategoryID CHAR(13),
#SystemID int OUTPUT
AS
BEGIN
SET NOCOUNT ON
DECLARE #ErrCode int
INSERT INTO [dbo].[Registration] ([FName],[LName],[MInit])
VALUES (#FName, #LName, #MInit)
SELECT #ErrCode = ##ERROR, #SystemID = SCOPE_IDENTITY()
UPDATE [dbo].[Registration]
SET CategoryID = 'ABC'+ CAST(SystemID AS CHAR)
SET NOCOUNT OFF
RETURN #ErrCode
END
Finally this is what the table looks like with the data:
Thanks for being contagious with your knowledge. :)
Guy
My suggestion is to use a computed column, as what you're trying to do introduces redundancy. See below:
http://msdn.microsoft.com/en-us/library/ms191250%28v=sql.105%29.aspx
Alternately, make it big enough to contain a GUID, put a GUID into the column on the insert, then update it afterwards.