Select columns from join table only without requiring a join - sql

Given these tables:
create table Orders (
Id INT IDENTITY NOT NULL,
primary key (Id)
)
create table Items (
Id INT IDENTITY NOT NULL,
primary key (Id)
)
create table OrdersItems (
OrderId INT not null,
ItemId INT not null,
primary key (OrderId, ItemId)
)
Is it possible to use HQL/criteria API to contruct a query that results in the following SQL:
SELECT
[OrderId], [ItemId]
FROM
[OrdersItems]
I've tried both of these approaches:
var hqlResults = session
.CreateQuery("select order.id, item.id from Order order inner join order.Items item")
.List();
var criteriaResults = session
.CreateCriteria<Order>()
.CreateAlias("Items", "item", NHibernate.SqlCommand.JoinType.None)
.SetProjection(Projections.Property("id"), Projections.Property("item.id"))
.List();
But both approaches insist on generating a join (or fail because the join isn't present, in using criteria), resulting in SQL such as:
select order.Id,
item.Id
from Orders order
inner join OrdersItems ordersItems
on order.Id = ordersItems.ArticleId
inner join Items item
on ordersItems.CategoryId = item.Id
Is there any way to have NHibernate generate a query that selects columns only from the join table, without requiring a join?

I doubt that there's a way to do it using HQL, because HQL deals witn NHibernate entities and OrderItems is not an entity. In this case it looks like you're not actually using any ORM features, so you can simply do a SQL query - via NHibernate if you wish. Just call ISession.CreateSQLQuery().
Edit:
I suspect that the reason NHibernate insists on doing the join is this: you've asked it for the Id properties of Order and Item entities, so it must ensure that there are actually rows in the Order and Item table for those IDs. It's possible that a row exists in the OrderItems table with IDs that don't exist in Order or Item. Sure, it would be bad database design to do that and it's unlikely, but NHibernate can't be sure that this is not the case unless it looks at the table schema and sees the appropriate foreign keys - but I doubt that it does things like that.
This is just my speculation, though. You could ask on the NHibernate forum for a more definitive answer from the developers.

Related

SQL, trying to get rid of large IN cluase

I have a table,
Contacts:
Contact_ID (int)
ContactName (nvarchar)
I am given a list of contact IDs to select from. Usually, i would just do
SELECT *
FROM Contacts
WHERE IN (List of contact ID)
The problem is, the list of contact IDs could potentially get very big, like 50k or more.
So my question is, Is there a way to deal with a large list of contact IDs without having to use the IN clause?
EDIT: I'm using Microsoft sql server. The query and the contact IDs are built during runtime and passed on to sqlCommand class (c#) to be executed.
I'd create a table type with a single column and a clustered primary key.
CREATE TYPE dbo.ContactId AS TABLE
(
ContactId INT NOT NULL PRIMARY KEY
);
Pass the values into the query using a table valued parameter.
Change your query to
SELECT *
FROM Contacts
WHERE contactID IN (SELECT y.contactID FROM #yourtablevaluedparameter y)
OPTION (RECOMPILE)
The OPTION (RECOMPILE) is there to get the number of rows taken into account as the optimal plan for 50K may well be different than for 1.
You can find some example C# code for populating a TVP here
If you want performance, I would use the EXISTS clause.
SELECT c.Contact_ID, c.ContactName
FROM Contacts c
WHERE EXISTS (List of contact ID)
Create a temp table and populate your contact id in the form of rows.
Do an inner join between your table and temp table like the below.
SELECT c.*
FROM Contacts c
join #temptable t
on c.id=t.id
If you introduce index on the Join column in your temp table then your query will be more faster.

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.

SQL - Check for consistency between the table data without using Cursors

I am using SQL and I have 4 tables.
tbl_project(project_id is primary key)
tbl_task (Contains task_id as primary key and project_id as foreign key)
tbl_assignment (Contains assignment_id as primary key task_id as foreign key)
tbl_document (Contains document_id as primary key and assignment_id as foreign key),
A project can have one or more tasks
A task can have one or more assignemnts
An assignemnt can have one or more documents
Now i need to write an stored procedure which will validate a project and returns the validation results, where project id will be passing as parameter.
Need to check project having atleast one task.
All the Task under this project is having atleast 1 assignment
All the assignment under this project/task having atleast 1 document
I am having a temp table where I need to insert the validation errors like "Task - Task1 doesnt contains any assignment", "Assignemnt - Assignment2 doesnt contains any documnets " etc.
Is there any way to implement this logic without using Cursors
You don't need cursors for this type validation. Here are three queries that do this:
Projects with no tasks:
select p.*
from tbl_projects p left outer join
tbl_tasks t
on p.project_id = t.project_id
where t.project_id is null;
Tasks have at least one assignment:
select p.*
from tbl_tasks t left outer join
tbl_assignments a
on a.task_id = t.task_id
where a.task_id is null;
All assignments have at least one document:
select p.*
from tbl_assignments a left outer join
tbl_documents d
on a.assignment_id = d.assignment_id
where d.assignment_id is null;

What is the purpose of the extra table in a many to many relationship?

I am trying to understand the need for the "extra" table in a many to many relationship.
For example if my ERD has: Product m----- <makes> ----n Factory, the following code for the tables might look like this:
CREATE TABLE factory(
factory_id INTEGER NOT NULL,
address VARCHAR(30) NOT NULL,
CONSTRAINT PK_factory_factory_id PRIMARY KEY(factory_id)
);
CREATE TABLE product(
prod_id INTEGER NOT NULL,
prod_name VARCHAR(30) NOT NULL,
price FLOAT NOT NULL,
CONSTRAINT PK_product_prod_id PRIMARY KEY(prod_id)
);
CREATE TABLE makes(
prod_id INTEGER NOT NULL,
factory_id INTEGER NOT NULL,
CONSTRAINT PK_makes_prod_id_factory_id PRIMARY KEY(prod_id, factory_id),
CONSTRAINT FK_product_prod_id FOREIGN KEY(prod_id) REFERENCES product(prod_id),
CONSTRAINT FK_factory_factory_id FOREIGN KEY(factory_id) REFERENCES factory(factory_id)
);
What is the purpose of the makes table? I can't think of a time when you might use it or why it is necessary.
A has-and-belongs-to-many relationship implies that both sides can belong to multiples of one another.
In this case:
Multiple factories can make a single product
One factory can make multiple products
The only way to represent this is to have a "join" table, since each item in the factory table and product table only represent one distinct product or factory, there's no way to link it to multiple other items without such a table.
How would you record the fact that a single product can be made by each of N factories without it?
You can't store a single factory number in the product table; there are lots of possible values.
You can't store a single product number in the factory table; there are lots of possible values.
So, you need the third table to store the information accurately.
Could you provide an example of how you would then create a query to view the multiple factories that could make a single product?
SELECT f.*
FROM Factory AS f
JOIN Makes AS m ON m.factory_id = f.factory_id
WHERE f.prod_id = 123456;
Or:
SELECT f.*
FROM Factory AS f
JOIN Makes AS m ON m.factory_id = f.factory_id
JOIN Product AS p ON m.prod_id = p.prod_id
WHERE p.prod_name = "HyperCubic Widget"
Which you use depends on whether you know the product ID or whether you need to look the product name or other attributes.
The queries are almost symmetric for if you need to know which products a factory can make:
SELECT p.*
FROM Product AS p
JOIN Makes AS m ON m.prod_id = f.prod_id
WHERE f.factory_id = 321;
Or:
SELECT p.*
FROM Product AS p
JOIN Makes AS m ON m.prod_id = p.prod_id
JOIN Product AS p ON m.factory_id = f.factory_id
WHERE f.address = "123 North St, Bigtown"
Note that because you will have referential integrity constraints on the tables, you can use inner joins rather than outer joins, which are generally more efficient.
Here are some questions that you would answer with it:
What products does a single factory make?
What factories make a particular product?
What factories make the same product as factory F?
What factories can be used to fulfill an order that that has products P1, P2, and P3?
Where is the closest factory to the customer who wants production P?
Where is the next closest factory?
And so on.

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)