So I can't have NOT NULL in column definition of temporal tables? - sql

I am using SQL Server 2016 and trying to create Temporal table. Here is the definition:
USE zbachoreTest
GO`enter code here`
IF EXISTS (SELECT 1 FROM sys.tables WHERE name = 'People' AND temporal_type_desc ='SYSTEM_VERSIONED_TEMPORAL_TABLE' )
ALTER TABLE dbo.People SET( SYSTEM_VERSIONING = OFF)
DROP TABLE IF EXISTS dbo.People
CREATE TABLE dbo.People(
PersonID INT IDENTITY(1,1) NOT NULL CONSTRAINT PK_People PRIMARY KEY(PersonID),
FirstName VARCHAR(50) NOT NULL,
LastName VARCHAR(50) NULL,
StartTime DATETIME2 GENERATED ALWAYS AS ROW START NOT NULL
CONSTRAINT DF_People_StartTime DEFAULT SYSDATETIME(),
EndTime DATETIME2 GENERATED ALWAYS AS ROW END NOT NULL
CONSTRAINT DF_People_EndTime DEFAULT CONVERT(DATETIME2,'9999-12-31 23:59:59.9999999'),
PERIOD FOR SYSTEM_TIME(StartTime,EndTime)
) WITH (SYSTEM_VERSIONING = ON (HISTORY_TABLE = dbo.PeopleHistory))
when I run the above code, I am getting the following error message:
Msg 13531, Level 16, State 1, Line 7
Setting SYSTEM_VERSIONING to ON failed because column 'FirstName' does not have the same nullability attribute in tables 'zbachoreTest.dbo.People' and 'zbachoreTest.dbo.PeopleHistory'.
Any smart folks out there please help. Thanks!

Yes, you can.
Do drop table dbo.PeopleHistory first. If it already exists, it will be only validated, not recreated.
From: https://learn.microsoft.com/en-us/sql/relational-databases/tables/creating-a-system-versioned-temporal-table
The history table must always be schema-aligned with the current or
temporal table, in terms of number of columns, column names, ordering
and data types.
and
If the table specified by the HISTORY_TABLE parameter already exists,
it will be validated against the newly created temporal table in terms
of schema consistency and temporal data consistency. If you specify an
invalid history table, the CREATE TABLE statement will fail.

Related

How do I select insert into select a table which already has values in the primary key column without adding new rows?

I'm working on a database for my school project in which I have to produce a functional database by normalizing sample tables given to us.
One table I'm having trouble with is itineraries. I produce 3 tables from the normalization which are "Destinations", "Itineraries" and "Itinerary_Destinations".
The code for Destinations is:
create table Destinations
(
DestinationID varchar(5) primary key,
Name varchar(45)
);
The code for Itineraries is:
create table Itineraries
(
ItineraryID varchar(5),
Name varchar(45)
);
The code for the last table is:
create table Itinerary_Destinations
(
DI varchar(5) primary key,
ItineraryID varchar(5) foreign key references Itineraries(ItineraryID),
Itinerary_Name varchar(45),
DestinationID varchar(5) foreign key references Destinations(DestinationID),
Destination_Name varchar(45)
);
Data has already been inserted into all 3 tables with the exception of 'Destination_Name' and 'Itinerary_Name' columns. The code I'm attempting to use is returning as error. The code is shown below.
insert into Itinerary_Destinations (Itinerary_name)
select Name from Itineraries where
Itineraries.ItineraryID = ItineraryID;
The error it returns is
Msg 515, Level 16, State 2, Line 1 Cannot insert the value NULL into
column 'DI', table 'DDDAssignment.dbo.Itinerary_Destinations'; column
does not allow nulls. INSERT fails. The statement has been terminated.
Is there a method to accomplish the task of inserting the Destination_Name and Itinerary_Name without creating new records that require primary keys?
Or should I do it manually?
If you want to modify records which already exist, then you should be using an UPDATE rather than an INSERT:
UPDATE a
SET Itinerary_name = b.Name
FROM Itinerary_Destinations a
INNER JOIN Itinerary_name b
ON a.ItineraryID = b.ItineraryID;
But, if you do have some data which is not already logically associated with the Itinerary_Destinations table, then using an insert is appropriate.
use coalesce funtion in case null it will insert blank string, as your column does not allow null value thats why you got that error in your query
insert into Itinerary_Destinations (Itinerary_name)
select coalesce(Name,' ') from Itineraries where
Itineraries.ItineraryID = ItineraryID;

Can not add a column to existing table

I have a table viz. expenses with three columns as under
ExpenseId int NOT NULL,
ExpenseName varchar(50) NOT NULL,
Invalid bit NOT NULL
To add a new column (OldCode char(4) not null), I used design feature for tables in Microsoft SQL Server Management Studio. But I get following error
'Expenses' table
- Unable to modify table. Cannot insert the value NULL into column 'OldCode', table 'TransportSystemMaster.dbo.Tmp_Expenses'; column does not allow nulls. INSERT fails. The statement has been terminated.
Incidentally I have been able to add same column with same specifications to other tables of the same database.
Any help?
Your Table Consist of Existing Records
and you are pushing a new column of type NOT NULL.
so for older records the data have to be something.
try something like this
ALTER TABLE MY_TABLE ADD Column_name INT NULL
GO
UPDATE MY_TABLE <set valid not null values for your column>
GO
ALTER TABLE MY_TABLE ALTER COLUMN Column_name INT NOT NULL
GO
Since OldCode is NOT NULL, you should specify a default value for it.
when you have some rows on your table you can't add a column that is not nullable you should provide a default value for it
Alter Table table_name add OldCode int not null DEFAULT(0);
You have to specify values for all the 4 fields of the table, its purely because, while designing the table you set the definition of the columns to be not null. Again you are adding a new column called OldCode and setting to be not null, all ready existing records hasn't got a value. So that is the reason its complains

SQL Server Create Table With Column Unique Not Null and Not Empty(Check)

How to create a table with a column which is unique, not null and not empty(Check)?
I tried below Query
CREATE TABLE Persons
(
P_Id int NOT NULL UNIQUE,
LastName nvarchar(255) NOT NULL,
FirstName nvarchar(255),
Address nvarchar(255),
City nvarchar(255),
CHECK (P_Id>0)
)
When i try to create a table with both UNIQUE and CHECK constraint its throwing following error. Is it possible to use two constraint in a single query?
Major Error 0x80040E14, Minor Error 25501
> CREATE TABLE Persons
(
P_Id int NOT NULL UNIQUE,
LastName nvarchar(255) NOT NULL,
FirstName nvarchar(255),
Address nvarchar(255),
City nvarchar(255),
CHECK (P_Id>0)
)
There was an error parsing the query. [ Token line number = 8,Token line offset = 1,Token in error = CHECK ]. I am using SQL Server 2008.
CREATE TABLE tab
(
id INT,
notnullandnotemptystr VARCHAR(10) NOT NULL UNIQUE CHECK (DATALENGTH(notnullandnotemptystr) > 0)
)
It should be some thing like this.
CREATE TABLE [dbo].[TABLE1](
[COL1] [nvarchar](50) NOT NULL UNIQUE
)
ALTER TABLE [dbo].[TABLE1] WITH CHECK
ADD CONSTRAINT [CK_TABLE1] CHECK (([COL1]<>N''))
for this problem you can use Constraint in sql server
ALTER TABLE TBL WITH CHECK ADD CONSTRAINT [CK_TBL] CHECK
(([dbo].[TBLCheckCustomeUnique](ID)=(1)))
TBLCheckCustomeUnique is a user define function that check this conditions
You can controll the uniqueness of a column or column set by the UNIQUE constraint.
The data stored in the column or column set could be checked/controlled (and forced with various rules) by the CHECK constraint.
The CHECK constraint to achieve your goal is the following:
ALTER TABLE [YourTable]
ADD CONSTRAINT CK_CheckConstraintName
CHECK (LEN([YourColumn]) >= {MinimumColumnWidth})
You can add the constraints in the CREATE TABLE statement or if the table already exists you can add it with the ALTER TABLE .. ADD CONSTRAINT statement.

Altering SQL table to add column

I currently have a table with four columns - i wanted to add a fifth column but having some trouble.
I open the table in sql server studio management 2008 and i added the column info like so:
CREATE TABLE [dbo].[Case]
(
CaseId UNIQUEIDENTIFIER DEFAULT (newid()) NOT NULL,
CaseNumber NVARCHAR(50) NOT NULL,
CourtId INT NOT NULL,
DateOpened DATETIME NOT NULL,
)
my addition:
CREATE TABLE [dbo].[Case]
(
CaseId UNIQUEIDENTIFIER DEFAULT (newid()) NOT NULL,
CaseNumber NVARCHAR(50) NOT NULL,
CaseName NVARCHAR(50),
CourtId INT NOT NULL,
DateOpened DATETIME NOT NULL,
)
After adding CaseName column, i tried executing the table in Management Studio but i got the error message "There is already an object named 'Case' in the database."
I tried saving and then building my database hoping that the column will be added but that wasn't successful. I tried a New Query and writing the 'Alter table "case" add CaseName nvarchar(50) but again without luck. It shows that the file is changed with the new column because i saved it but after building my overall database it isn't making any changes. Any helpful tips will be great.
You want to ALTER, as follows:
ALTER TABLE [dbo].[Case] ADD CaseName NVARCHAR(50)
Better yet, you can check for the existance of the column first:
if not exists (SELECT 1 FROM sysobjects INNER JOIN syscolumns ON
sysobjects.id = syscolumns.id
WHERE sysobjects.name = N'Case' AND syscolumns.name = N'CaseName')
ALTER TABLE [dbo].[Case] ADD CaseName NVARCHAR(50)
you should try this
ALTER TABLE [dbo].[Case]
ADD CaseName NVARCHAR(50)
You are trying to create another table Case but one already exists that's why you have an error. When you want to edit a table, you have to use Alter table
Use an Alter table statement instead of Create
If you can't get the Alter statement to work for some reason, you could also drop the existing table and create a new one with the new field, but all your existing rows will be lost.
If you're using SSMS, you can Design the table instead of Edit to add the column.
ALTER is what you need to investigate (F1)
An alternative is.
Create a new table
CREATE TABLE [dbo].[Case2]
(
CaseId UNIQUEIDENTIFIER DEFAULT (newid()) NOT NULL,
CaseNumber NVARCHAR(50) NOT NULL,
CourtId INT NOT NULL,
DateOpened DATETIME NOT NULL,
newcolumn INT NULL
)
Move data from existing table into the new one
INSERT INTO [dbo].[Case2]
SELECT * FROM [dbo].[Case]
Then
DROP TABLE [dbo].[Case]
Then in management studio right-click 'Case2' and re-name it 'Case'
I recommend checking for the existence of the column prior to adding it, especially important when you work with migration scripts.
Here is how I usually do it:
IF NOT EXISTS(SELECT * FROM sys.columns WHERE Name = N'ColumnName' AND Object_ID = Object_ID(N'TableName'))
BEGIN
ALTER TABLE [dbo].TableName ADD ColumnName NVARCHAR(512) null
END

Constraint for only one record marked as default

How could I set a constraint on a table so that only one of the records has its isDefault bit field set to 1?
The constraint is not table scope, but one default per set of rows, specified by a FormID.
Use a unique filtered index
On SQL Server 2008 or higher you can simply use a unique filtered index
CREATE UNIQUE INDEX IX_TableName_FormID_isDefault
ON TableName(FormID)
WHERE isDefault = 1
Where the table is
CREATE TABLE TableName(
FormID INT NOT NULL,
isDefault BIT NOT NULL
)
For example if you try to insert many rows with the same FormID and isDefault set to 1 you will have this error:
Cannot insert duplicate key row in object 'dbo.TableName' with unique
index 'IX_TableName_FormID_isDefault'. The duplicate key value is (1).
Source: http://technet.microsoft.com/en-us/library/cc280372.aspx
Here's a modification of Damien_The_Unbeliever's solution that allows one default per FormID.
CREATE VIEW form_defaults
AS
SELECT FormID
FROM whatever
WHERE isDefault = 1
GO
CREATE UNIQUE CLUSTERED INDEX ix_form_defaults on form_defaults (FormID)
GO
But the serious relational folks will tell you this information should just be in another table.
CREATE TABLE form
FormID int NOT NULL PRIMARY KEY
DefaultWhateverID int FOREIGN KEY REFERENCES Whatever(ID)
From a normalization perspective, this would be an inefficient way of storing a single fact.
I would opt to hold this information at a higher level, by storing (in a different table) a foreign key to the identifier of the row which is considered to be the default.
CREATE TABLE [dbo].[Foo](
[Id] [int] NOT NULL,
CONSTRAINT [PK_Foo] PRIMARY KEY CLUSTERED
(
[Id] ASC
) ON [PRIMARY]
) ON [PRIMARY]
GO
CREATE TABLE [dbo].[DefaultSettings](
[DefaultFoo] [int] NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[DefaultSettings] WITH CHECK ADD CONSTRAINT [FK_DefaultSettings_Foo] FOREIGN KEY([DefaultFoo])
REFERENCES [dbo].[Foo] ([Id])
GO
ALTER TABLE [dbo].[DefaultSettings] CHECK CONSTRAINT [FK_DefaultSettings_Foo]
GO
You could use an insert/update trigger.
Within the trigger after an insert or update, if the count of rows with isDefault = 1 is more than 1, then rollback the transaction.
CREATE VIEW vOnlyOneDefault
AS
SELECT 1 as Lock
FROM <underlying table>
WHERE Default = 1
GO
CREATE UNIQUE CLUSTERED INDEX IX_vOnlyOneDefault on vOnlyOneDefault (Lock)
GO
You'll need to have the right ANSI settings turned on for this.
I don't know about SQLServer.But if it supports Function-Based Indexes like in Oracle, I hope this can be translated, if not, sorry.
You can do an index like this on suposed that default value is 1234, the column is DEFAULT_COLUMN and ID_COLUMN is the primary key:
CREATE
UNIQUE
INDEX only_one_default
ON my_table
( DECODE(DEFAULT_COLUMN, 1234, -1, ID_COLUMN) )
This DDL creates an unique index indexing -1 if the value of DEFAULT_COLUMN is 1234 and ID_COLUMN in any other case. Then, if two columns have DEFAULT_COLUMN value, it raises an exception.
The question implies to me that you have a primary table that has some child records and one of those child records will be the default record. Using address and a separate default table here is an example of how to make that happen using third normal form. Of course I don't know if it's valuable to answer something that is so old but it struck my fancy.
--drop table dev.defaultAddress;
--drop table dev.addresses;
--drop table dev.people;
CREATE TABLE [dev].[people](
[Id] [int] identity primary key,
name char(20)
)
GO
CREATE TABLE [dev].[Addresses](
id int identity primary key,
peopleId int foreign key references dev.people(id),
address varchar(100)
) ON [PRIMARY]
GO
CREATE TABLE [dev].[defaultAddress](
id int identity primary key,
peopleId int foreign key references dev.people(id),
addressesId int foreign key references dev.addresses(id))
go
create unique index defaultAddress on dev.defaultAddress (peopleId)
go
create unique index idx_addr_id_person on dev.addresses(peopleid,id);
go
ALTER TABLE dev.defaultAddress
ADD CONSTRAINT FK_Def_People_Address
FOREIGN KEY(peopleID, addressesID)
REFERENCES dev.Addresses(peopleId, id)
go
insert into dev.people (name)
select 'Bill' union
select 'John' union
select 'Harry'
insert into dev.Addresses (peopleid, address)
select 1, '123 someplace' union
select 1,'work place' union
select 2,'home address' union
select 3,'some address'
insert into dev.defaultaddress (peopleId, addressesid)
select 1,1 union
select 2,3
-- so two home addresses are default now
-- try adding another default address to Bill and you get an error
select * from dev.people
join dev.addresses on people.id = addresses.peopleid
left join dev.defaultAddress on defaultAddress.peopleid = people.id and defaultaddress.addressesid = addresses.id
insert into dev.defaultaddress (peopleId, addressesId)
select 1,2
GO
You could do it through an instead of trigger, or if you want it as a constraint create a constraint that references a function that checks for a row that has the default set to 1
EDIT oops, needs to be <=
Create table mytable(id1 int, defaultX bit not null default(0))
go
create Function dbo.fx_DefaultExists()
returns int as
Begin
Declare #Ret int
Set #ret = 0
Select #ret = count(1) from mytable
Where defaultX = 1
Return #ret
End
GO
Alter table mytable add
CONSTRAINT [CHK_DEFAULT_SET] CHECK
(([dbo].fx_DefaultExists()<=(1)))
GO
Insert into mytable (id1, defaultX) values (1,1)
Insert into mytable (id1, defaultX) values (2,1)
This is a fairly complex process that cannot be handled through a simple constraint.
We do this through a trigger. However before you write the trigger you need to be able to answer several things:
do we want to fail the insert if a default exists, change it to 0 instead of 1 or change the existing default to 0 and leave this one as 1?
what do we want to do if the default record is deleted and other non default records are still there? Do we make one the default, if so how do we determine which one?
You will also need to be very, very careful to make the trigger handle multiple row processing. For instance a client might decide that all of the records of a particular type should be the default. You wouldn't change a million records one at a time, so this trigger needs to be able to handle that. It also needs to handle that without looping or the use of a cursor (you really don't want the type of transaction discussed above to take hours locking up the table the whole time).
You also need a very extensive tesing scenario for this trigger before it goes live. You need to test:
adding a record with no default and it is the first record for that customer
adding a record with a default and it is the first record for that customer
adding a record with no default and it is the not the first record for that customer
adding a record with a default and it is the not the first record for that customer
Updating a record to have the default when no other record has it (assuming you don't require one record to always be set as the deafault)
Updating a record to remove the default
Deleting the record with the deafult
Deleting a record without the default
Performing a mass insert with multiple situations in the data including two records which both have isdefault set to 1 and all of the situations tested when running individual record inserts
Performing a mass update with multiple situations in the data including two records which both have isdefault set to 1 and all of the situations tested when running individual record updates
Performing a mass delete with multiple situations in the data including two records which both have isdefault set to 1 and all of the situations tested when running individual record deletes
#Andy Jones gave an answer above closest to mine, but bearing in mind the Rule of Three, I placed the logic directly in the stored proc that updates this table. This was my simple solution. If I need to update the table from elsewhere, I will move the logic to a trigger. The one default rule applies to each set of records specified by a FormID and a ConfigID:
ALTER proc [dbo].[cpForm_UpdateLinkedReport]
#reportLinkId int,
#defaultYN bit,
#linkName nvarchar(150)
as
if #defaultYN = 1
begin
declare #formId int, #configId int
select #formId = FormID, #configId = ConfigID from csReportLink where ReportLinkID = #reportLinkId
update csReportLink set DefaultYN = 0 where isnull(ConfigID, #configId) = #configId and FormID = #formId
end
update
csReportLink
set
DefaultYN = #defaultYN,
LinkName = #linkName
where
ReportLinkID = #reportLinkId