Project summary
We're creating a 'digital' kitchen where it's possible to add which items are in your fridge, freezer etc. Then it's possible to view these items on a webapplication, so you know groceries you have in your kitchen on the go.
The database
In total there are three tables.
Lists
This holds ListIDs and names of these Lists, like 'Fridge' or 'Freezer'. So, this is basically the 'container'.
Items
Then there's Items which holds itemtypes, i.e. Milk, 1 gallon.
ListItems
Then there's ListItems (sorry for a slightly confusing name) which holds specific items. While Items is just a table of items which can be added, ListItems are the added items. So the rows added to this table naturally have a foreign key to both a List on the Lists table and a foreign key to an Item on the Items table. This table's primary key is a superkey, made from nearly all its attributes.
Two ListItems can refer to the same Item and same List, as long as they have different attributes. They could expire on different dates or be different unit sizes. The only thing that makes an Item unique is the name, like if you added a 'Ham', it would never overwrite a 'Milk'. Adding another 'Milk' would then be seen as the same Item
The Problem
Here's an example. You want to add two seperate items. The first is 3 hams, each 200 g which expire on the 28th of May.
The next is another ham, this one single, but 500g and expires on the 31st of May:
ListID 1 refers to the List which is called Fridge.
ItemID 1 refers to the item which is called Ham.
See the problem? The foreign keys are the same.
It stores just fine on our local database but when syncing with an Azure database, we get the following error:
{"Violation of PRIMARY KEY constraint 'PK__#A4D1762__44A4C03D49E5E4B8'.
Cannot insert duplicate key in object 'dbo.#changeTable'.
The duplicate key value is (1, 1).\r\n
The data for table-valued parameter \"#changeTable\" doesn't conform to the table type of the parameter.
SQL Server error is: 3602, state: 30\r\nThe statement has been terminated."}
Is this poor database design or an Azure problem?
Update 1
Here's how the DDL looks on the local database:
CREATE TABLE [dbo].[ListItems] (
[ListId] INT NOT NULL,
[ItemId] INT NOT NULL,
[Amount] INT NOT NULL,
[Volume] INT NOT NULL,
[Unit] NVARCHAR(MAX) NULL,
[ShelfLife] DATETIME NOT NULL,
CONSTRAINT [pk_ListItems] PRIMARY KEY CLUSTERED ([ShelfLife], [Volume], [Amount], [ItemId], [ListId]),
CONSTRAINT [fk_ListItems] FOREIGN KEY ([ListId]) REFERENCES [dbo].[Lists] ([ListId]) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT [fk_ListItems2] FOREIGN KEY ([ItemId]) REFERENCES [dbo].[Items] ([ItemId]) ON DELETE CASCADE ON UPDATE CASCADE
);
And the Azure database DDL:
CREATE TABLE [dbo].[ListItems] (
[ListId] INT NOT NULL,
[ItemId] INT NOT NULL,
[Amount] INT NOT NULL,
[Volume] INT NOT NULL,
[Unit] NVARCHAR (MAX) NULL,
[ShelfLife] DATETIME NOT NULL,
CONSTRAINT [PK_dbo.ListItems] PRIMARY KEY CLUSTERED ([ListId] ASC, [ItemId] ASC, [Amount] ASC, [Volume] ASC, [ShelfLife] ASC),
CONSTRAINT [FK_dbo.ListItems_dbo.Items_ItemId] FOREIGN KEY ([ItemId]) REFERENCES [dbo].[Items] ([ItemId]) ON DELETE CASCADE,
CONSTRAINT [FK_dbo.ListItems_dbo.Lists_ListId] FOREIGN KEY ([ListId]) REFERENCES [dbo].[Lists] ([ListId]) ON DELETE CASCADE
);
The PK CONSTRAINT line is slightly different, but the attributes are the same. Could this be the problem?
I don't think the error is where you think it is. If the PK on the local and Azure ListItems both name the same columns (irrespective of order), they are the same. The error message, however, mentions a different PK definition (and a different tablename):
Violation of PRIMARY KEY constraint 'PK__#A4D1762__44A4C03D49E5E4B8'.
Cannot insert duplicate key in object 'dbo.#changeTable'.
The duplicate key value is (1, 1).
There appears to be a table whose name is "#changetable" -- which looks like a store procedure parameter to me -- that is defined with a two-column primary key. Track it down and you'll solve the problem.
Related
I'm building an application using Entity Framework and C#, and in one of my tables, I noticed I named the column with the primary key the same name as the foreign key column i.e. dbo.MembershipTypes table's primary key is on "MembershipTypeId" property, and the dbo.Customers table it has a relationship with has a foreign key on "MembershipTypeId" property i.e. public MembershipType MembershipTypeId { get; set; }).
This caused problems, so I renamed MembershipTypeId back to Id (it was initially Id, but the column wasn't an identity column, and EF refused to make it one even after using [DatabaseGenereated(DatabaseGeneratedOption.Identity)] and [Key] attributes).
I'm using code-first migrations, so when I try reverting the changes using
update-database -TargetMigration:(migration before mistake goes here)
in the package manager console, I get this error:
The CREATE UNIQUE INDEX statement terminated because a duplicate key was found for the object name 'dbo.MembershipTypes' and the index name 'PK_dbo.MembershipTypes'. The duplicate key value is (0).
Could not create constraint or index. See previous errors.
Here's the migration it gives me the error when I try to revert back to an earlier version of the database:
public partial class RenameIdColumnAndMakeItIdentityInMembershipTypeTable : DbMigration
{
public override void Up()
{
DropForeignKey("dbo.Customers", "MembershipTypeId", "dbo.MembershipTypes");
DropPrimaryKey("dbo.MembershipTypes");
AddColumn("dbo.MembershipTypes", "MembershipTypeId", c => c.Byte(nullable: false, identity: true));
AddPrimaryKey("dbo.MembershipTypes", "MembershipTypeId");
AddForeignKey("dbo.Customers", "MembershipTypeId", "dbo.MembershipTypes", "MembershipTypeId", cascadeDelete: true);
DropColumn("dbo.MembershipTypes", "Id");
}
public override void Down()
{
AddColumn("dbo.MembershipTypes", "Id", c => c.Byte(nullable: false));
DropForeignKey("dbo.Customers", "MembershipTypeId", "dbo.MembershipTypes");
DropPrimaryKey("dbo.MembershipTypes");
DropColumn("dbo.MembershipTypes", "MembershipTypeId");
AddPrimaryKey("dbo.MembershipTypes", "Id");
AddForeignKey("dbo.Customers", "MembershipTypeId", "dbo.MembershipTypes", "Id", cascadeDelete: true);
}
}
Here's the T-SQL code EF used to create the table. I made the table with its properties first then populated it with the reference data in a separate migration.
CREATE TABLE [dbo].[MembershipTypes]
(
[SignUpFee] SMALLINT NOT NULL,
[DurationInMonths] TINYINT NOT NULL,
[DiscountRate] TINYINT NOT NULL,
[Name] NVARCHAR(255) DEFAULT ('') NOT NULL,
[MembershipTypeId] TINYINT IDENTITY (1, 1) NOT NULL,
CONSTRAINT [PK_dbo.MembershipTypes]
PRIMARY KEY CLUSTERED ([MembershipTypeId] ASC)
);
Here's the code used to generate the Customers table:
CREATE TABLE [dbo].[Customers]
(
[Id] INT IDENTITY (1, 1) NOT NULL,
[Name] NVARCHAR (255) NOT NULL,
[IsSubscribedToNewsletter] BIT DEFAULT ((0)) NOT NULL,
[MembershipTypeId] TINYINT DEFAULT ((0)) NOT NULL,
[Birthdate] DATETIME NULL,
CONSTRAINT [PK_dbo.Customers]
PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_dbo.Customers_dbo.MembershipTypes_MembershipTypeId]
FOREIGN KEY ([MembershipTypeId]) REFERENCES [dbo].[MembershipTypes] ([MembershipTypeId])
ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX [IX_MembershipTypeId]
ON [dbo].[Customers]([MembershipTypeId] ASC);
This all came about as I was trying to fix the API to create a new customer using [HttpPost].
Please help me understand why I'm getting this error and how to fix it. Also, kindly help me understand why the duplicate key starts at 0 and not 1.
Thanks in advance for the assistance!
Thanks to Gert Arnold for answering this one in the comments.
The reason I was getting this error is that the Id column in the Down method of my migration was not an identity column.
My understanding is that since the Id column, which often has the primary key in a relational database, cannot be null, 0 is inserted there because this is the default value for a byte or integer types.
In my Up method, I made Id an identity column, but since the default value of 0 was already present, this created a conflict because the database is now generating a value for Id starting with 0.
This can't work because an Id column is unique and can't have duplicate values. Even if, for example, you had 5 customers in your database and deleted customer 4, no other customer will have an Id of 4.
Hope this helps someone in the future!
I use the MS Sync Framework to sync my SQL Server instance with a local SQL CE file to make it possible working offline with my Windows app.
I use GUIDs as keys. On my table I have a unique index on 2 columns: user_id and setting_id:
usersettings table
------------------
id PK -> I also tried it without this column. Same result
user_id FK
setting_id FK
value
Now I do the following:
I create a new record in this table in both databases - SQL Server and SQL CE with the same user_id and setting_id.
This should work and merge the data together since this can happen in real life. But I get an error when syncing saying the unique key constraint led to an error. The key pair already exists in the table.
A duplicate value cannot be inserted into a unique index. [ Table name = user_settings,Constraint name = unique_userid_settingid ]
Why can't MS sync handle that? It should not try to insert the key pair again. It should update the value if needed.
The issue is if you add the same key pair to different copies of the table, they get different IDs (GUIDs) as primary keys in this usersettings table.
As this is simply a many-to-many table between Users and Settings, there is no need to have that ID as a PK (or even a column at all).
Instead, just use a concatenated key of the two FKs e.g.,
CREATE TABLE [dbo].[usersettings](
[user_id] [UNIQUEIDENTIFIER] NOT NULL,
[setting_id] [UNIQUEIDENTIFIER] NOT NULL,
[value] [varchar](50) NOT NULL,
CONSTRAINT [PK_usersettings] PRIMARY KEY CLUSTERED ([user_id] ASC, [setting_id] ASC) );
Of course, include appropriate field settings (e.g., if you use VARCHARs to store the IDs) and relevant FKs.
As the rows inserted should now be identical on the two copies, it should merge fine.
If you must have a single column as a unique identifier for the table, you could make it meaningful e.g.,
the PK (ID) becomes a varchar (72)
it gets filled with CONCAT(user_ID, setting_id)
As the User_ID and Setting_ID are FKs, you should already have them generated so concatenating them should be easy enough.
Do you get the error during sync, then it should appear as a conflict, that you must solve in code.
https://learn.microsoft.com/en-us/previous-versions/sql/synchronization/sync-framework-2.0/bb734542(v=sql.105)
I also see this in the manual: By default, the following objects are not copied to the client database: FOREIGN KEY constraints, UNIQUE constraints, DEFAULT constraints, and the SQL Server ROWGUIDCOL property. This indicates poor support for your scenario
I suggest you remove the unique constraint from the device table.
I know it's first std question but someone asked me, but i was unable give him apropriate answer.
There are two tables Employee,Technology having many to many relation saved in employee_technology_rel.Employee table having fields- emp_id (auto_increment),emp_name(varchar),DOB (date) where Technology having fields tech_id(auto_increment),tech_name(varchar)
these two tables allows duplication of names.which unique constraint should i define for allowing unique entry?
You can define the unique entry on table employee_technology_rel,
ALTER TABLE employee_technology_rel
ADD CONSTRAINT emptech_pk PRIMARY KEY (emp_id, tech_id)
// or if you have set a primary key already, you can still define via UNIQUE
ALTER TABLE employee_technology_rel
ADD CONSTRAINT emptech_uq UNIQUE (emp_id, tech_id)
what it does is it only allows unique technology for every employee.
in order for you to have unique emp_name on table Employee as well as unique tech_name on table Technology, you can also alter the table by adding unique constraint
ALTER TABLE Employee ADD CONSTRAINT emp_uq UNIQUE (emp_name)
ALTER TABLE Technology ADD CONSTRAINT tech_uq UNIQUE (tech_name)
You want a composite primary key defined on the two columns in employee_technology_rel: emp_id and tech_id.
Unique Index and Unique Constraint are the same. They achieve same
goal. SQL Performance is same for both.
Add Unique Constraint
ALTER TABLE dbo. ADD CONSTRAINT
UNIQUE NONCLUSTERED ( ) ON
[PRIMARY]
Add Unique Index
CREATE UNIQUE NONCLUSTERED INDEX
ON dbo. ( ) ON
[PRIMARY]
Source sqlauthority.com and msdn from Google search: "SQL server unique index".
I have two tables:
CREATE TABLE Order (
orderId INTEGER IDENTITY NOT NULL,
PRIMARY KEY (orderId)
)
CREATE TABLE OrderAdditionalDetails (
additionalDetailsId INTEGER IDENTITY NOT NULL,
orderId INTEGER NOT NULL,
PRIMARY KEY (additionalDetailsId),
FOREIGN KEY (orderId) REFERENCES Order(orderId)
)
I have a Foreign key (FK_OrderAdditionalDetails_Order) declared on the OrderAdditionalDetails table, on the orderId field. I also have a 'unique' constraint on the orderId field in the OrderAdditionalDetails table. The idea is that each 'order' will have zero or one entries in the 'OrderAdditionalDetails' table.
This all picked up by the entity framework model file, however when I try to create the Navigation property, it only lets me declare a 1 to many relationship. The error I get is as follows:
Running transformation: Multiplicity is not valid in Role 'OrderAdditionalDetails' in relationship 'FK_OrderAdditionalDetails_Order'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be *.
I'm really not sure what this means - googling the error did not prove helpful. Can anybody shed some light on what I am doing wrong?
In your OrderAdditionalDetails table, remove the additionalDetailsID column and make the orderID the CLUSTERED PRIMARY KEY. Keep the FOREIGN KEY you already have. That is the right way to implement this.
There is not only no value added by the additionalDetailsId column, it makes things worse by taking more space in the table. The orderID is already a sufficient key; you need no secondary artificial key that is nothing but a surrogate for orderID.
Your Foreign Key must be defined as UNIQUE in order to enforce a One-To-Zero-Or-One relationship.
Maybe try something like this:
CREATE TABLE OrderAdditionalDetails (
additionalDetailsId INTEGER IDENTITY NOT NULL,
orderId INTEGER NOT NULL UNIQUE,
PRIMARY KEY (additionalDetailsId),
FOREIGN KEY (orderId) REFERENCES Order(orderId)
)
See Also: Implementing one-to-zero-or-one relation in SQL Server
I was trying to associate a table with a view of itself plus some other fields. (There is a very good reason for this that has nothing to do with the answer)
What cause the same error was there was more than one key field on the view. Even though I had specified the fields involved in the association it wanted both to be the only key fields for a 1 to 1 to work.
I also set the key field to be Distinct in the view, but I did that before I removed the key attribute of other fields, so it may ,or may not, be necessary.
I'm using SubSonic 3.0.0.3 and I can't seem to get the ActiveRecord .tt files to recognize and generate code for the foreign keys and relationships in my SQLite database.
I think it generates everything else just fine, but after looking at other snippets online it looks like there should be more generated code than just single classes in ActiveRecord.cs and Structs.cs for each of my tables. Looking inside Structs.cs, IsForeignKey is always false for every column, even the ones I have a foreign key defined for. Additionally, each Foreign Keys region is empty within each generated ActiveRecord class.
I'm using VS2008 with references to SubSonic 3.0.0.3, System.Data.SQLite 1.0.66.0, and System.Data.SQLite.Linq 2.0.38.0 in my project. I created the database using SQLite Expert Personal 3.1.0.2076. I made some dummy tables to try to test out how SubSonic handles one:many and many:many relationships. Here's the DDL SQLite Expert spits out for my small database:
CREATE TABLE [Person] (
[PersonID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
[PersonName] TEXT NOT NULL,
[PersonAge] INT NOT NULL
);
CREATE TABLE [Group] (
[GroupID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
[GroupName] TEXT NOT NULL,
[GroupDescription] TEXT NOT NULL
);
CREATE TABLE [Dog] (
[DogID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
[PersonID] INT NOT NULL CONSTRAINT [DogPersonFK] REFERENCES [Person]([PersonID]) ON DELETE CASCADE ON UPDATE CASCADE,
[DogName] TEXT NOT NULL);
CREATE TABLE [GroupPersons] (
[GroupID] INTEGER NOT NULL CONSTRAINT [GroupPersonToGroupFK] REFERENCES [Group]([GroupID]) ON DELETE CASCADE ON UPDATE CASCADE,
[PersonID] INTEGER NOT NULL CONSTRAINT [GroupPersonToPersonFK] REFERENCES [Person]([PersonID]) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT [sqlite_autoindex_GroupPersons_1] PRIMARY KEY ([GroupID], [PersonID]));
I know foreign keys are enabled and work in the database - SQLite Expert says they're on and when I change data in one place, like Person's PersonID, it does indeed change that PersonID in the Dog and GroupPersons tables. I've tried re-adding the database to the project, 'running custom tool' to get the .tt files to execute again, and even deleting them and adding them back. I can get a simple project to build that perform simple querying and insertions, however I tried just now to change the primary key of a single Person, Dog, or Group and x.Save() but System.Data.SQLite threw an exception for all three, saying SQLite error near "WHERE":syntax error. at the Save().
Any suggestions for what I should try to do next?
It seems that the FKTables attribute for each table is not assigned in the file "SQLite.ttinclude". So i add some lines of code and managed to generate foreign key code :
After line 16 (var schema = conn.GetSchema("COLUMNS");), insert :
var schemaForeignKeys = conn.GetSchema("FOREIGNKEYS");
After line 29 (tbl.Name = row["TABLE_NAME"].ToString();), insert :
tbl.FKTables = new List<FKTable>();
var foreignKeyTables = schemaForeignKeys.Select("TABLE_NAME='" + tbl.Name + "'");
foreach (var foreignKeyTable in foreignKeyTables) {
FKTable foreignKey = new FKTable();
foreignKey.ThisTable = foreignKeyTable["TABLE_NAME"].ToString();
foreignKey.ThisColumn = foreignKeyTable["FKEY_FROM_COLUMN"].ToString();
foreignKey.OtherTable = foreignKeyTable["FKEY_TO_TABLE"].ToString();
foreignKey.OtherColumn = foreignKeyTable["FKEY_TO_COLUMN"].ToString();
foreignKey.OtherClass = CleanUp(foreignKey.OtherTable);
foreignKey.OtherQueryable = foreignKey.OtherClass;
tbl.FKTables.Add(foreignKey);
}
And after line 53 (col.IsNullable=row["IS_NULLABLE"].ToString()=="True";), insert :
col.IsForeignKey = tbl.FKTables.Any(x => x.ThisColumn == col.Name);
This is for generate the foreign key code.
Moreover, you have maybe encounter a problem when you have to delete a record which has a column to be a foreign key in an other table ? For exemple :
Person(Id, Name)
Dog(Id, #PersonId)
If you have set the #PersonId foreign key on-delete action to "SET TO NULL", this won't work because foreign key support is disabled by default in SQLite 3.6.23.1 (version used by Data.SQLite 1.0.66.0).
To enable foreign key support, you have to execute this command with each connection :
PRAGMA foreign_keys = ON;
Now, this is not supported by Data.SQLite, but it will (in version 1.0.67.0, http://sqlite-dotnet2.cvs.sourceforge.net/viewvc/sqlite-dotnet2/SQLite.NET/System.Data.SQLite/SQLiteConnection.cs?r1=1.80&r2=1.81).
So you have to wait for the release or you can (like me) download the Data.SQLite source and compile the last version. It work great for me.
Good luck.
And sorry for my english :)
I'm trying to reason this. There seems to be two issues at hand:
Subsonic not recognising your foreign keys
The x.Save() function sending that error message.
SQLite will be enforcing referential integrity on its own, so while Subsonic does not see the foreign references, SQLite does, and that's why your updates go through. Subsonic does not drive SQLite, it is driving itself, which is fine.
I'm attempting to learn what SubSonic is and does. In the mean time, I have this hypothesis: the table definitions are not parsed correctly. If x.Save() is uses automatically generated SQL, it could be that the two issues are really just one.
To validate (or invalidate) this hypothesis, could you try defining the tables thus, giving the foreign keys as table attributes, not attributes of specific fields:
CREATE TABLE [Dog] (
[DogID] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
[PersonID] INTEGER NOT NULL,
[DogName] TEXT NOT NULL,
FOREIGN KEY ([PersonID]) REFERENCES [Person]([PersonID]) ON DELETE CASCADE ON UPDATE CASCADE);
CREATE TABLE [GroupPersons] (
[GroupID] INTEGER NOT NULL,
[PersonID] INTEGER NOT NULL,
FOREIGN KEY ([GroupID]) REFERENCES [Group]([GroupID]) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY ([PersonID]) REFERENCES [Person]([PersonID]) ON DELETE CASCADE ON UPDATE CASCADE,
PRIMARY KEY ([GroupID], [PersonID]));