This is in SQL Server 2005.
I have an address table:
dbo.Address
(
AddressID INT IDENTITY(1, 1) PRIMARY KEY
LastUpdateBy VARCHAR(30)
<bunch of address columns>
)
I also have a history table:
dbo.AddressHistory
(
AddressID INT,
AsOf DATETIME,
UpdateBy VARCHAR(30)
<all the address columns>
CONSTRAINT PK_dbo_AddressHistory PRIMARY KEY CLUSTERED (AddressID, AsOf)
)
I have a trigger on dbo.Address to create history entries on both INSERT and UPDATE which will basically do this:
INSERT INTO dbo.AddressHistory(AddressID, AsOf, UpdateBy, <address columns>)
SELECT AddressID, CURRENT_TIMESTAMP, #UpdateBy, <address columns>
FROM INSERTED
But, every once in while, I'll get a PK violation on dbo.AddressHistory complaining about a duplicate PK being inserted. How is this possible if part of the PK for AddressHistory is the current timestamp of the insertion?
Even executing this will insert two rows into the history table successfully:
INSERT INTO dbo.Address
(LastUpdateBy, <address columns>)
SELECT 'test', <address columns>
FROM dbo.Address
WHERE AddressID < 3
And the only update sproc I have for the dbo.Address table will update a row for a given AddressID. So it should only be updating one row at a time. My insert sproc only inserts one row at a time as well.
Any idea what conditions cause this to occur?
Based on your description two concurrent executions of the stored procedure with the same parameter would seem likely.
datetime only has a precision of 1/300 second so conflicts can occur if these executions happen very close together.
Related
There are two tables with the dependence one to many on field occupation_id (project about booking hotel rooms). Each occupation entity can have several booked rooms. Is there way to do in a single sql request insertion to the first table (occupation) and several batch insertions to the second table(booked_rooms).
Tables are:
Table: Occupations
occupation_id bigint (autoincrement)
user_id bigint
check_in_date date
check_out_date date
status text
Table: Booked_rooms
booked_room_id bigint (autoincrement)
occupation_id bigint
beds int
class_rate int
Use a transaction:
BEGIN;
INSERT INTO ...;
SELECT LAST_INSERT_ID();
INSERT INTO ...;
COMMIT;
I beginning to learn how to write trigger with this basic database.
I'm also making my very 1st database.
Schema
Team:
TeamID int PK (TeamID int IDENTITY(0,1) CONSTRAINT TeamID_PK PRIMARY KEY)
TeamName nvarchar(100)
History:
HistoryID int PK (HistoryID int IDENTITY(0,1) CONSTRAINT HistoryID_PK PRIMARY KEY)
TeamID int FK REF Team(TeamID)
WinCount int
LoseCount int
My trigger: when a new team is inserted, it should insert a new history row with that team id
CREATE TRIGGER after_insert_Player
ON Team
FOR INSERT
AS
BEGIN
INSERT INTO History (TeamID, WinCount, LoseCount)
SELECT DISTINCT i.TeamID
FROM Inserted i
LEFT JOIN History h ON h.TeamID = i.TeamID
AND h.WinCount = 0 AND h.LoseCount = 0
END
Executed it returns
The select list for the INSERT statement contains fewer items than the insert list. The number of SELECT values must match the number of INSERT columns.
Please help thank. I'm using SQL Server
The error text is the best guide, it is so clear ..
You try inserting one value from i.TeamID into three columns (TeamID,WinCount,LoseCount)
consider these WinCount and LoseCount while inserting.
Note: I Think the structure of History table need to revisit, you should select WinCount and LoseCount as Expressions not as actual columns.
When you specify insert columns, you say which columns you will be filling. But in your case, right after insert you select only one column (team id).
You either have to modify the insert to contain only one column, or select, to retrieve 3 fields as in insert.
If you mention the columns where values have to be inserted(Using INSERT-SELECT).
The SELECT Statement has to contain the same number of columns that have been specified to be inserted. Also, ensure they are of the same data type.(You might face some issues otherwise)
In SQL Server I have a table as RawTable (temp) which gets fed by a CVS, let's say it has 22 columns in it. Then, I need to copy existing records (ONLY FEW COLUMNs NOT ALL) into another table as Visitors which is not temporary table.
Visitor table has an ID column as INT and that is primary key and incremental.
RawData table
id PK, int not null
VisitorDate Varchar(10)
VisitorTime Varchar(11)
Visitors table
VisitorID, PK, big int, not null
VisitorDate, Varchar(10), null
VisitorTime Varchar(11), null
So I did:
insert into [dbo].[Visitors] ( [VisitorDate], [VisitorTime])
select [VisitorDate], [VisitorTime]
from RawTable /*this is temp table */
Seems SQL Server doesn't like this method so it throws
Msg 515, Level 16, State 2, Line 1
Cannot insert the value NULL into column 'VisitorID', table 'TS.dbo.Visitors'; column does not allow nulls. INSERT fails. The statement has been terminated.
How can I keep Sql Server not to complain about the primary key? this column as you know better will be fed by sql server itself.
Any idea?
Just because your visitors table has an ID column that is the primary key doesn't mean that the server will supply your ID values for you. if you want SQL to provide the ID's then you need to alter the table definition and make the visitorsId column an IDENTITY column.
Otherwise, you can psuedo-create these id's during the insert with the ROW_NUMBER function -
DECLARE #maxId INT;
SELECT #maxId = (SELECT MAX(visitorsId) FROM dbo.visitors);
INSERT INTO [dbo].[Visitors] ( [visitorsId],[VisitorDate], [VisitorTime])
SELECT #maxId + ROW_NUMBER() OVER (ORDER BY visitorDate), [VisitorDate], [VisitorTime]
from RawTable /*this is temp table */
I have 3 data tables to update database
Invoice table, primary key is InvoiceNo
InvoiceProduct table, primary key is InvoiceProductNo and foreign key InvoiceNo
InvoiceProductExp table, primary key is InvoiceProductExpNo and foreign keys are InvoiceNo and InvoiceProductNo
Facts:
One InvoiceNo has many InvoiceProductNo
one InvoiceProductNo has many InvoiceProductExpNo
3 Data Tables data entry would be for example is
Invoice (InvoiceNo,...)
(0001,...)
InvoiceProduct (InvoiceProductNo, InvoiceNo)
(1,0001,...)
(2,0001,...)
InvoiceProductExp (InvoiceProductExpNo,InvoiceProductNo,InvoiceNo)
(1,1,0001,...)
(2,1,0001,...)
(3,2,0001,...)
(4,2,0001,...)
The problem is I liked to use SQL Server generated Identity column for all primary keys of 3 tables
How can I prepare for insert statements?
Insert Into InvoiceProductExp values (auto_number, ?, ?)
How can I get InvoiceProductNo to insert into InvoiceProductExp table since InvoiceProductNo is auto number?
You're looking for SCOPE_IDENTITY()
DECLARE #InvoiceNo INT
DECLARE #InvoiceProductNo INT
INSERT INTO Invoice ([Date])
VALUES (GETDATE())
SELECT #InvoiceNo = SCOPE_IDENTITY()
INSERT INTO InvoiceProduct([InvoiceNo])
VALUES (#InvoiceNo)
SELECT #InvoiceProductNo = SCOPE_IDENTITY()
INSERT INTO InvoiceProductExp ([InvoiceProductNo], [InvoiceNo])
VALUES (#InvoiceProductNo, #InvoiceNo)
Here is SQLFiddle demo
If your primary keys are of identity column type then you don't have to insert a value into the primary key column. The identity column will auto populate when the row is committed to the database.
Does this resolve your question?
Why must manually? You can set your ID as automatically auto increment.
When you create table:
UserID INT IDENTITY(1,1) NOT NULL
sample (create the table)
CREATE TABLE dbo.Tool(
UserID INT IDENTITY NOT NULL PRIMARY KEY,
Name VARCHAR(40) NOT NULL
)
Inserting values
INSERT INTO dbo.Tool(Name) VALUES ('Person 1')
INSERT INTO dbo.Tool(Name) VALUES ('Person 2')
I'm using SQL Server 2005 and wish to create a number address records, updating the contact records with the new Id's:
Take the following tables
create table contact(id int primary key identity, home_address_id int, work_address_id int)
create table address(id int primary key identity, street varchar(25), number int)
And foreign keys:
ALTER TABLE dbo.contact ADD CONSTRAINT FK_contact_address1 FOREIGN KEY (home_address_id) REFERENCES dbo.address(id)
ALTER TABLE dbo.contact ADD CONSTRAINT FK_contact_address2 FOREIGN KEY (work_address_id) REFERENCES dbo.address(id)
some dummy data
insert into contact default values
insert into contact default values
insert into contact default values
How can I insert a default empty address record for all contacts who have no home address, and update the home_address_id in one go?
The first part is simple:
insert into address(street) select null from contact where home_address_id is null
I can even get the newly create address id's:
declare #addressTable table(id int)
insert into address(street)
OUTPUT INSERTED.Id INTO #addressTable
select null from contact where home_address_id is null
Here's the new id's
select * from #addressTable
But how to update the contact table with these new Id's?
If possible, I would suggest normalizing your database by adding a Contact_Addresses table:
CREATE TABLE Contact_Addresses
(
contact_id INT NOT NULL,
address_id INT NOT NULL,
address_type VARCHAR(10) NOT NULL,
CONSTRAINT PK_Contact_Addresses PRIMARY KEY CLUSTERED (contact_id, address_id, address_type),
CONSTRAINT FK_ContactAddresses_Contacts (contact_id) REFERENCES Contacts (id),
CONSTRAINT FK_ContactAddresses_Addresses (address_id) REFERENCES Addresses (id),
CONSTRAINT CK_ContactAddresses_address_type CHECK address_type IN ('HOME', 'WORK')
)
Next, I would suggest not putting "dummy" records in your database. It's going to end up causing headaches down the road. The database should contain an accurate record of the data in your system. If you want to display some value by default when no address exists in the system for a contact then handle that in your UI.
If you really must though, then the following code should do the trick:
;WITH C_CTE AS
(
SELECT
id,
home_address_id,
ROW_NUMBER() OVER(ORDER BY id) AS seq
FROM
Contacts
),
(
SELECT
id,
ROW_NUMBER() OVER(ORDER BY id) AS seq
FROM
Addresses
)
UPDATE
C_CTE
SET
home_address_id = A.id
FROM
C_CTE C
INNER JOIN A_CTE A ON A.seq = C.seq
I would do it from the moment you get a new contact, thusly:
[receive contact information]
//prior to inserting contact
declare #homeAddress int, #workAddress int
[insert home address here (real or default based on input)]
set #homeAddress = ##Identity
[insert work address here (real or default)]
set #workAddress = ##Identity
[insert contact here referencing #homeAddress & #workAddress]
For the stuff already in your table, you're going to have to associate all of your null value ids to a contact id. Or, you could clear out your null value addresses, and modify the above statement to an update somehow (brain's not working at the moment, so all I'm coming up with is a cursor, and cursors are evil).