Many to many with Multiple primary keys? - sql

Situation:
TableParent with 2 primaryKeys, ParentKey1 and ParentKey2
TableChild with 1 primaryKey, ChildKey
TableConnector with columns ParentKey1, ParentKey2 and ChildKey
This is where I think I should go with my Linq query. Notice I'm fetching all childs belonging to a parent so I have it's keys as parameters.
var query = from conn in db.TableConnector
join child in db.TableChild on conn.ChildKey equals child.childKey
join par in db.TableParent on conn.ParentKey1 equals par.parentkey1 into connGroup
from co in connGroup
where co.ParentKey1 == Parameter1
Select child;
Well I tthink this works up to a point, let's say if parent had only one key, am I right?
I guess I have to join some more into a second group but I'm currently lost.

Firstly, you don't have two primary keys on your table. You can only have one primary key (hence the name primary). It sounds like you have a composite primary key, which means a key that is composed of more than one column.
I'm not sure what problem you are trying to solve, but it seems to be retrieving all TableChild rows for a given TableParent key. It should be something like this:
db.TableParent
.Single(parent => parent.ParentKey1 == key1 && parent.ParentKey2 == key2)
.TableConnectors.Select(connector => connector.TableChild)
If you have your tables mapped correctly on your Linq-to-Sql designer then you don't have to manually join them - that's what the Linq-to-Sql code generation does for you.
For example, when you have a TableConnector you will be able to retrieve the TableChild rows for it using something like this
TableConnector t = db.TableConnectors.First();
List<TableChilds> tableChilds = tableConnector.TableChilds.ToList();

tableParent only needs one primary key (it's own Id) and tableChild needs one (it's Id)
the connectorTable only needs two columns to make the many-to-many-relation work:
ParentId and ChildId
For each relation between a parent and a child you simply add one row to the connectorTable, and in order to retrieve the results try this:
select * from tableParent
inner join connectorTable
on tableParent.Id = connectorTable.ParentId
inner join tableChild
on connectorTable.ChildId = tableChild.Id
If your reason for multiple keys in the parent table is that you want to create relations between parents as well this needs to be addressed either via a relation field in the parent table (one-to-many) or another relationTable (many-to-many)

Related

Join table with different primary key values

I want to join two tables with different primary key values as below
Table A
id name
1 John
2 Smith
and Table B
id name
1-x Foo
2-x Bar
I've tried using LIKE statements on my query but it won't stop executing.
SELECT * FROM A a JOIN B b ON a.id LIKE b.id +'%'
Is there any efficient way to JOIN these two tables?
If you use a query like this:
SELECT *
FROM A a JOIN
B b
ON a.id || '-x' = b.id;
Then SQLite should be able to use an index on B(id) for the query. It is worth a try.
No, there's no efficient way of joining them.
That doesn't mean it cannot be done (it can), but it probably doesn't make sense.
Primary keys are not supposed to be good looking or sexy. They are unique row identifiers. If you have a primary key with a value like that, it's because you probably want to expose it or display it somewhere; and that's not their goal.
If you need a nice looking row identifier I suggest you create a secondary unique column that could even be auto-generated.
Bottom line, keep the primary key simple.

Access: Updatable join query with 2 primary key fields that are both also foreign keys

In MS Access, I am trying to implement a many-to-many table that will store 2-way relationships, similar to Association between two entries in SQL table. This table stores info such as "Person A and Person B are coworkers" "C and D are friends", etc. The table is like this:
ConstitRelationships
LeftId (number, primary key, foreign key to Constituents.ConstitId)
RightId (number, primary key, foreign key to Constituents.ConstitId)
Description (text)
Note that the primary key is a composite of the two Id fields.
Also the table has constraints:
[LeftId]<>[RightId] AND [LeftId]<[RightId]
The table is working ok in my Access project, except that I cannot figure out how to make an updateable query that I want to use as a datasheet subform so users can easily add/delete records and change the descriptions. I currently have a non-updatable query:
SELECT Constituents.ConstituentId, Constituents.FirstName,
Constituents.MiddleName, Constituents.LastName,
ConstitRelationships.Description, ConstitRelationships.LeftId,
ConstitRelationships.RightId
FROM ConstitRelationships INNER JOIN Constituents ON
(Constituents.ConstituentId =
ConstitRelationships.RightId) OR (Constituents.ConstituentId =
ConstitRelationships.LeftId);
If I ignore the possibility that the constituentId I want is in the leftId column, I can do this, which is updatable. So the OR condition in the inner join above is what's messing it up.
SELECT Constituents.ConstituentId, Constituents.FirstName,
Constituents.MiddleName, Constituents.LastName,
ConstitRelationships.Description, ConstitRelationships.LeftId,
ConstitRelationships.RightId
FROM ConstitRelationships INNER JOIN Constituents ON
(Constituents.ConstituentId =
ConstitRelationships.RightId) ;
I also tried this wacky iif thing to collapse the two LeftId and RightId fields into FriendId, but it was not updateable either.
SELECT Constituents.ConstituentId, Constituents.FirstName,
Constituents.MiddleName,
Constituents.LastName, subQ.Description
FROM Constituents
INNER JOIN (
SELECT Description, Iif([Forms]![Constituents Form]![ConstituentId] <>
ConstitRelationships.LeftId, ConstitRelationships.LeftId,
ConstitRelationships.RightId) AS FriendId
FROM ConstitRelationships
WHERE ([Forms]![Constituents Form]![ConstituentId] =
ConstitRelationships.RightId)
OR ([Forms]![Constituents Form]![ConstituentId] =
ConstitRelationships.LeftId)
) subQ
ON (subQ.FriendId = Constituents.ConstituentId)
;
How can I make an updatable query on ConstitRelationships, including a JOIN with the Constituent.FirstName MiddleName LastName fields?
I am afraid that is not possible. Because you use joins in your query over three tables it is not updatable. There is no way around this.
Here some detailed information about the topic: http://www.fmsinc.com/Microsoftaccess/query/non-updateable/index.html
As mentioned in the linked article one possible solution and in my opinion best solution for you would be the temporary table. It is a load of work compared to the easy "bind-form-to-a-query"-approach but it works best.
The alternative would be to alter your datascheme in that way that you do not need joins. But then denormalized data and duplicates would go rampage which makes the temporary table a favorable choice.

How to include multiple where conditions using Icriteria in nhibernate

i am developing an application in MVC3 using nhibernate
I have a two tables and i want to perform a join between the two and also i want to include multiple where conditions..
I am posting all the tables that are related so that it will clear the requirements:
First table is HobbyMaster:
HobbyId
HobbyName
Second Table is HobbyHome:
HobbyHomeId
HobbyHomeName
Third Table is HobbyDetail:
HobbyDetailId
HobbyId(Foreign Key)
HobbyHomeId(Foreign key)
Fourth Table is HobbyHomeAddress:
Id(primary key)
HobbyHomeId(Foreign key)
StateId(Foreign Key)
DistrictId(Foreign key)
Fifth Table is State:
stateid
statename
last Table is District:
districtid
districtname
I want to write a query like this:
select * from HobbyHomeAddress hobadd,
HobbyDetail hobdet
where hobdet.HobbyId=hobbyid
and hobadd.HobbyId=hobdet.HobbyId
and hobadd.StateId=stateid;
I tried this but it is not working and also gives me NUll
hobbyhomeaddress = session.CreateCriteria(typeof(HobbyHomeAddress))
.CreateAlias("HobbyDetail", "HobbyDetail", NHibernate.SqlCommand.JoinType.InnerJoin)
.Add(Expression.Eq("HobbyID", hobbyid))
.Add(Restrictions.Eq("HobbyHomeAddress.HobbyHomeId", "HobbyDetail.HobbyHomeId"))
.Add(Expression.Eq("ProvincialState.ProvincialStateID", stateid))
.List<HobbyHomeAddress>();
i am able to obtain the id's through ajax but i am not understanding how to fire the following query
Please help me
One possible issue:
.Add(Restrictions.Eq("HobbyHomeAddress.HobbyHomeId", "HobbyDetail.HobbyHomeId"))
This part of the join (in SQL) should be in your nhibernate mappings, you don't need to explicitly specify it.

Unexpected results after joining another table

I use three tables to get to the final result. They are called project_board_members, users and project_team.
This is the query:
SELECT `project_board_members`.`member_id`,
`users`.`name`,
`users`.`surname`,
`users`.`country`,
`project_team`.`tasks_completed`
FROM `project_board_members`
JOIN `users`
ON (`users`.`id` = `project_board_members`.`member_id`)
JOIN `project_team`
ON (`project_team`.`user_id` = `project_board_members`.`member_id`)
WHERE `project_board_members`.`project_id` = '5'
You can ignore last line because it just points to the project I'm using.
Table project_board_members holds three entries and have structure like:
id,
member_id,
project_id,
created_at;
I need to get member_id from that table. Then I join to users table to get name, surname and country. No problems. All works! :)
After that, I needed to get tasks_completed for each user. That is stored in project_team table. The big unexpected thing is that I got four entries returned and the big what-the-f*ck is that in the project_board_members table are only three entries.
Why is that so? Thanks in advice!
A SQL join creates a result set that contains one row for each combination of the left and right tables that matches the join conditions. Without seeing the data or a little more information it's hard to say what exactly is wrong from what you expect, but I'm guessing it's one of the following:
1) You have two entries in project_team with the same user_id.
2) Your entries in project_team store both user_id and project_id and you need to be joining on both of them rather than just user_id.
The table project_board_members represent what is called in the Entity-Relationship modelling world an "associative entity". It exists to implement a many-to-many relationship (in this case, between the project and user entities. As such it is a dependent entity, which is to say that the existence of an instance of it is predicated on the existence of an instance of each of the entities to which it refers (a user and a project).
As a result, the columnns comprising the foreign keys relating to those entities (member_id and project_id) must be form part or all of the primary key.
Normally, instances of an associative entity are unique WRT the entities to which it relates. In your case the relationship definitions would be:
Each user is seated on the board of 0-to-many projects;
Each project's board is comprise of 0-to-many users
which is to say that a particular user may not be on the board of a particular project more than once. The only reason for adding other columns (such as your id column) to the primary key would be if the user:project relationship is non-unique.
To enforce this rule -- a user may sit on the board a particular project just once -- the table schema should look like this:
create table project_board_member
(
member_id int not null foreign key references user ( user_id ) ,
project_Id int not null foreign key references project ( project_id ) ,
created_at ...
...
primary key ( member_id , project_id ) ,
)
}
The id column is superfluous.
For debugging purposes do
SELECT GROUP_CONCAT(pbm.member_id) AS member_ids,
GROUP_CONCAT(u.name) as names,
GROUP_CONCAT(u.surname) as surnames,
GROUP_CONCAT(u.country) as countries,
GROUP_CONCAT(pt.tasks_completed) as tasks
FROM project_board_members pbm
JOIN users u
ON (u.id = pbm.member_id)
JOIN project_team pt
ON (pt.user_id = pbm.member_id)
WHERE pbm.project_id = '5'
GROUP BY pbm.member_id
All the fields that list multiple entries in the result are messing up the rowcount in your resultset.
To Fix that you can do:
SELECT pbm.member_id
u.name,
u.surname,
u.country,
pt.tasks_completed
FROM (SELECT
p.project_id, p.member_id
FROM project_board_members p
WHERE p.project_id = '5'
LIMIT 1
) AS pbm
JOIN users u
ON (u.id = pbm.member_id)
JOIN project_team pt
ON (pt.user_id = pbm.member_id)

How do I (quickly) collate IDs from various tables?

I have three denormalized tables that I have to take at face value (data comes from some external resource). The three tables have different definitions, but they each describe the same object from different perspectives.
object1 A B
object2 A
object3 B C
object4 C
The only commonality between these tables is their primary key. I can corral the IDs together using SELECT UNION SELECT, but the query seems relatively slow, even when each table has its PK field indexed. I could create a view to abstract this query, vw_object_ids, but it performs at the same speed. I thought I could add an index to materialize the view, but in SQL Server 2005, you can't index views with UNIONs.
What I want is to have a master index of IDs be in sync with with the underlying data, which may get updated or deleted whenever. I guess I could accomplish this indefinitely with a crazy set of triggers or just settle for the speed of the unindexed view. But I just wanted to make sure I'm not missing any options or if this scenario has a name or is indicative of a pattern.
Thoughts?
Create a master table that contains only the ID:
CREATE TABLE master (ID INT NOT NULL PRIMARY KEY)
and make all three tables to refer to that master table with ON DELETE CASCADE.
To populate the table for the first time, issue
INSERT
INTO master
SELECT id
FROM a
UNION
SELECT id
FROM b
UNION
SELECT id
FROM c
To populate the table on a regular basis, create a trigger on each of three tables.
This trigger should try to insert the new ID to master and silently fail on PRIMARY KEY violation.
To query, use:
SELECT *
FROM master m
LEFT OUTER JOIN
a
ON a.id = m.id
LEFT OUTER JOIN
b
ON b.id = m.id
LEFT OUTER JOIN
c
ON c.id = m.id
This will use indexes efficienty.
To delete, use:
DELETE
FROM master
WHERE id = #id
This will fire ON DELETE CASCADE and delete records from all three tables if any.
Why not just do an outer join and then coalesce the columns from the component tables?