How to handle mututally/recursively related tables? - c#

I'm pretty new to databases and sql. I have a problem where I have two tables which both contain a foreign key to the primary key of the other. My problem is I have a large number of elements which can have multiple names in different languages, but MUST have a single primary name/language.
alt text http://img688.imageshack.us/img688/1121/11768540.png
Firstly, I want to know if this is possible, or should I just give up already? The obvious solution is to have an extra boolean field in the ElementName table to say IsDefaultName, but it adds some extra complexity for querying and updating. If this is the best solution, how do I constrain the ElementName table to not accept any submission if IsDefaultName is set and the table already has an entry with the same ElementId and IsDefaultName set (or would I need to query this manually)?
I'm attempting to use LINQ to SQL here. The code I'm using to attempt to insert new items throws an exception at SubmitChanges with The INSERT statement conflicted with the FOREIGN KEY constraint "FK_ElementName_Element". I can understand why this is, but wondering if there's a fix/better solution.
var db = new MyDBDataContext();
var element = new Element();
var elementName = new ElementName() {
ElementName1 = "MyElement",
Language = "English",
};
element.ElementName = elementName;
db.Elements.InsertOnSubmit(element);
db.ElementNames.InsertOnSubmit(elementName);
db.SubmitChanges();

Solution 1
element
------------------
element_id
~....
element_name
------------------
element_name_id
fk_element_id
name
language_id
is_default_name Default ( 0 )
Trigger:
if ( ( select count ( 1 ) from element_name where is_default_name = 1 ) > 1 )
BEGIN
raisError ( 'only 1 element_name may be marked is_default_name = true.', 16, 1 );
END
Solution 2
element
------------------
element_id ( pk )
~....
element_name
------------------
element_name_id ( pk )
fk_element_id
name
language_id
element_name_default
------------------
fk_element_id
fk_element_name_id
( pk - fk_element_id, fk_element_name_id )
Solution 3
element
------------------
element_id
fk_element_name_id_default NULL
~....
element_name
------------------
element_name_id
fk_element_id
name
language_id
order of code:
* Insert to element_name
* update of element
I would stick with what you had, cause it is just fine, just:
db.Elements.InsertOnSubmit(element);
db.ElementNames.InsertOnSubmit(elementName);
//I don't know this syntax to say
// set the property of element.fk_element_name_id_default
// to the newly inserted elementName from above
db.Elements.?.?

Why not simply use a self-join like so:
Create Table Elements(
ElementId... Not Null Primary Key
, DefaultElementId ... Not Null
References Elements( ElementId )
, Name ...
, Language ...
)
The default name is the one where ElementId = DefaultElementId.
Btw, this is a place where a guid PK is nicer than an identity column. With a guid PK, you could generate both the ElementId and DefaultElementId from the client. If you are using an identity column with this schema, you'll probably have to create a "Unknown" elementId with a known value like zero so that you can do the insert and then turn around and do an update all in a single transaction.
** ADDITION **
Given what you said in comments, it sounds like you are trying to localize the elements data. My inclination would be to recommend adding a non-nullable "Name" column to the Elements table which represents the language neutral or default language name. Your ElementNames table would have a foreign key to the Elements table and would only populate that table when you localized an element name. Your queries would then need to coalesce on the requested language name and the name in the elements table if the requested language did not have a localized name.

Related

Entity Framework Core does not recognize an existent column in a PostgreSQL table

In a DataBase first .net 6 app I get the following error
"Npgsql.PostgresException (0x80004005): 42703: no existe la columna b.CampusId"
Hint: Probablemente quiera hacer referencia a la columna «b.campusid».
Which means that the column b.CampusId does not exist as a column, and as a hit it tells me you are probably looking for b.campusid.
But I have the following schema:
CREATE TABLE "Campus" (
"CampusId" SERIAL PRIMARY KEY,
"Name" varchar(250) NOT NULL,
"UniversityId" int NOT NULL,
"StateId" int NOT NULL,
"CreatedAt" TIMESTAMP NOT NULL DEFAULT NOW(),
"ModifiedAt" TIMESTAMP,
UNIQUE("Name", "UniversityId", "StateId"),
FOREIGN KEY ("UniversityId")
REFERENCES "University" ("UniversityId"),
FOREIGN KEY ("StateId")
REFERENCES "State" ("StateId")
);
And the part where the code breaks is in this one:
var campus_ids = await _context.Set<Campus>().FromSqlRaw<Campus>(
$"SELECT * FROM \"Campus\" c WHERE LOWER(UNACCENT(c.\"Name\")) LIKE '%{search}%' LIMIT {MAX_RESULTS} ")
.AsNoTracking()
.Select(x => x.CampusId) // This part is the one it complains about
.ToListAsync();
I do not get why the error ocurs in the Select part, since the Campus Model has the exact property name as the column in the Campus TABLE, both are named exactly CampusId, and I created them with quotes so it could be case sensitive, but still npgsql hints me about the correct name being in lowercase, notwithstanding the data base shows the column exactly like CampusId.
The generated code by EF is:
SELECT b.campusid
FROM (
SELECT * FROM "Campus" c WHERE LOWER(UNACCENT(c."Name"))
LIKE '%universidad%' LIMIT 20
) AS b
So, my questions are:
Why it yielded that bad translation when the entity name is CampusId?
How can I make EF to translate its queries to the appropiate property model and column names?
It should have been 'SELECT b.CampusId' instead of what it actually yielded.

C# MySQL transaction duplicate entry exception [duplicate]

I have a table with 3 columns - id (pk), pageId (fk), name. I have a php script which dumps about 5000 records into the table, with about half being duplicates, with same pageId and name. Combination of pageId and name should be unique. What is the best way to prevent duplicates being saved to the table as I loop through the script in php?
First step would be to set a unique key on the table:
ALTER TABLE thetable ADD UNIQUE INDEX(pageid, name);
Then you have to decide what you want to do when there's a duplicate. Should you:
ignore it?
INSERT IGNORE INTO thetable (pageid, name) VALUES (1, "foo"), (1, "foo");
Overwrite the previously entered record?
INSERT INTO thetable (pageid, name, somefield)
VALUES (1, "foo", "first")
ON DUPLICATE KEY UPDATE (somefield = 'first')
INSERT INTO thetable (pageid, name, somefield)
VALUES (1, "foo", "second")
ON DUPLICATE KEY UPDATE (somefield = 'second')
Update some counter?
INSERT INTO thetable (pageid, name)
VALUES (1, "foo"), (1, "foo")
ON DUPLICATE KEY UPDATE (pagecount = pagecount + 1)
You can also ignore the error with mysql: INSERT IGNORE INTO TABLE ... it will ignore the key error, skip over that insert and move on to the next.
From a mysql point you can do
alter table YOURTABLE add unique index(pageId, name);
If your wording is correct and you want to do it from php you can do
$already_done = array();
foreach ($records as $record)
{
$unique_hash = md5($record['name'].$record['pageId']);
if (!in_array($unique_hash, $already_done))
{
$already_done[] = $unique_hash;
// sql insert here
}
}
either way those should do you just fine.
You can set the PageID and Name to a Unique index in the MySQL database. This way when you insert the rows, it will cause an error, which can be ignored by PHP, and you can just go to the next row.
This assumes you are inserting rows individually. AKA:
foreach($large_data as $fields)
{
mysql_query("INSERT INTO TABLE (`Something`) VALUES('".$fields['something']."');
}

Syntax error while creating primary key

I'm creating VF tables using C#. The Create Table statement looks like
{CREATE TABLE NMMAIN (
NAMETYPE Character(4),
NAME_ID Numeric(11) AUTOINCREMENT,
RACE Character(2),
RELIGION Character(10),
REPORTAREA Character(8),
RESTRICTNM Numeric(1),
RES_STATUS Character(1),
SEX Character(1),
SKINTONE Character(6),
CONSTRAINT primKey PRIMARY KEY(NAME_ID)
)}
This throws a "Syntax Error" on executing the query. It does work fine if I don't add the Constraint. any help is appreciated.
You are using the wrong syntax. Looking at the CREATE TABLE syntax for Visual Foxpro the correct syntax would be either:
CREATE TABLE NMMAIN (
...,
NAME_ID Numeric(11) AUTOINC PRIMARY KEY,
...
)
Or:
CREATE TABLE NMMAIN (
...,
NAME_ID Numeric(11) AUTOINC,
...,
PRIMARY KEY NAME_ID TAG primKey
)
Note I have used AUTOINC instead of AUTOINCREMENT, as the reference I link to doesn't show AUTOINCREMENT.
Your final problem, I think, is that only the Integer type can use AUTOINC, not Numeric.
Here is the CREATE TABLE clause definition:
CREATE TABLE | DBF Table_Name
[CODEPAGE = nCodePage]
(FieldName1 FieldType [( nFieldWidth [, nPrecision] )] [NULL | NOT NULL]
[AUTOINC [NEXTVALUE NextValue [STEP StepValue]]]
[, FieldName2 ... ] [ ... ] )
And an example:
CREATE TABLE "D:\Data\customer.dbf" (id I AUTOINC NEXTVALUE 1 STEP 1, name C(40), payment F(5,2))
Taken from here.

How to check if a column is already a foreign key?

I have table named Person and column named ID
how to check if ID is already FOREIGN KEY cause I want to make it with this code:
ALTER TABLE Person ADD FOREIGN KEY(ID) REFERENCES Job(ID)
ON DELETE CASCADE ON UPDATE CASCADE
but if ID is already a FOREIGN KEY it gives me the following error "may cause cycles or multiple cascade paths" because of the condition with two cascades... How to check if this field is FOREIGN KEY to avoid this error?
You'd want to look in the INFORMATION SCHEMA views
Though it's not as complete as it should be. This is the final query you'd want:
SELECT
KCU1.CONSTRAINT_NAME AS 'FK_CONSTRAINT_NAME'
, KCU1.TABLE_NAME AS 'FK_TABLE_NAME'
, KCU1.COLUMN_NAME AS 'FK_COLUMN_NAME'
, KCU1.ORDINAL_POSITION AS 'FK_ORDINAL_POSITION'
, KCU2.CONSTRAINT_NAME AS 'UQ_CONSTRAINT_NAME'
, KCU2.TABLE_NAME AS 'UQ_TABLE_NAME'
, KCU2.COLUMN_NAME AS 'UQ_COLUMN_NAME'
, KCU2.ORDINAL_POSITION AS 'UQ_ORDINAL_POSITION'
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC
JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1
ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG
AND KCU1.CONSTRAINT_SCHEMA = RC.CONSTRAINT_SCHEMA
AND KCU1.CONSTRAINT_NAME = RC.CONSTRAINT_NAME
JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU2
ON KCU2.CONSTRAINT_CATALOG =
RC.UNIQUE_CONSTRAINT_CATALOG
AND KCU2.CONSTRAINT_SCHEMA =
RC.UNIQUE_CONSTRAINT_SCHEMA
AND KCU2.CONSTRAINT_NAME =
RC.UNIQUE_CONSTRAINT_NAME
AND KCU2.ORDINAL_POSITION = KCU1.ORDINAL_POSITION
See here for more information
http://msdn.microsoft.com/en-us/library/aa175805(v=sql.80).aspx
Here is a simple little version
SELECT TOP(1) a.COLUMN_NAME FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS b JOIN
INFORMATION_SCHEMA.KEY_COLUMN_USAGE a ON a.CONSTRAINT_CATALOG = b.CONSTRAINT_CATALOG AND a.CONSTRAINT_NAME = b.CONSTRAINT_NAME WHERE a.COLUMN_NAME = *your column*)
You can easily add table name and DB name in the where clause as well

SubSonic 2.1: Need explanation of SqlQuery.ExecuteJoinedDataSet()

I have a SqlQuery that looks like this:
SqlQuery query =
DB.Select(
Order.Schema.TableName + ".*",
OrderDetail.Schema.TableName + ".*")
.From<Order>()
.InnerJoin<OrderDetail>()
.Where(Order.IdColumn).IsEqualTo(1);
Now I would expect the Method SqlQuery.ExecuteJoindDataSet() to generate a DataSet for me, that contains 2 DataTables (one for Orders, one for OrderDetails) and put a DataRelation into the DataSet, so I don't have to do this all by hand.
But ExecuteJoinedDataSet() only generates one Table containing all the data from Order but not from OrderDetail:
// Order = 104 Columns
// OrderDetail = 74 Columns
query.ExecuteJoinedDataSet().Tables.Count => 1
query.ExecuteJoinedDataSet().Tables[0].Columns.Count => 104
query.ExecuteDataSet().Tables[0].Columns.Count => 177
I think I am on the right way, but can someone please tell me, what I am doing wrong?
The purpose of this is that the Printing Component I use in my project does not accept generic objects, but DataSet's as a DataSource.
ExecuteJoinedDataSet actually uses all the table columns from the first table and replaces the value in any column that has a foreign key with the first non-forgeign-key value from the corresponding row in the foreign table. It does inner joins for non-null foreign-key columns, and left joins for nullable ones.
So for this schema
create table tblBaseType
(
id int not null primary key identity(1,1),
name not null varchar(100) unique
)
create table tblBaseLocation
(
id int not null primary key identity(1,1),
name not null varchar(100) unique
)
create table tblBase
(
id int not null primary key identity(1,1),
name varchar(100) not null unique,
baseTypeID int not null references tblBaseType(id),
baseLocationID int null references tblBaseLocation(id)
)
and a SqlQuery like
SqlQuery q = new Select().From(TblBase.Schema).Where(TblBase.IdColumn).IsEqualTo(1);
DataSet ds = q.ExecuteJoinedDataSet();
this approximate sql would be generated:
select tblBase.Id,
tblBase.Name,
tblBaseType.Name as baseTypeId,
tblBaseLocation.name as baseLocationId
from tblBase
inner join tblBaseType on tblBase.baseTypeID = tblBaseType.id
left join tblBaseLocation on tblBase.baseLocationID = tblBaseLocation.id
The actual sql is fully qualified, this is just a rough from-scratch approximation.

Categories

Resources