turn two sql joins into a double join? possible? - mysql

I currently have two separate SQL JOINS. One is getting a project name and the other a class name from two different tables from the same ID.
A table called PUPILPROJECT holds an ID, projectID and ClassID which are also in a PROJECT table with the PROJECT name and a CLASS table with the CLASS name.
Is there a way of doing this with a single query? All help is much appreciated!
//GET PROJECT NAME
$getproject = mysql_query("SELECT project_name FROM projects JOIN pupilproject USING (project_id) WHERE pupil_project_id = '".$id."'");
while ($row = mysql_fetch_array($getproject)) {
$thisproject = $row['project_name'];
}
//GET PROJECT CLASS NAME
$classname = mysql_query("SELECT class_name FROM class JOIN pupilproject USING (class_id) WHERE pupil_project_id = '".$id."'");
while ($row = mysql_fetch_array($classname)) {
$pclass = $row['class_name'];
}

Use the following SQL in your code:
"SELECT project_name, class_name
FROM pupilproject
LEFT JOIN projects USING (project_id)
LEFT JOIN class USING (class_id)
WHERE pupil_project_id = '".$id."'"

Related

SQL - can a table get its own name via alias column/field?

I have two tables:
product:
id
image:
id
imageable_type
imageable_id
I am trying to execute a query to the effect of:
SELECT *
FROM product p
INNER JOIN image i
ON i.imageable_id = p.id
AND i.imageable_type = "product" # <--this
But due to the constraints of my application framework, I must append a field name to the alias p., so I am looking for something to the effect of:
AND i.imageable_type = p.TABLE_NAME # <--this
where TABLE_NAME is some built in "universal special field" so-to-speak that references name of the parent table. Is there such as feature in MySQL or other SQL db's?
Would your framework accept this?
SELECT *
FROM product p
INNER JOIN image i
ON i.imageable_id = p.id
WHERE i.imageable_type = "product"

Retrieve PRIMARY KEY value from SELECT DISTINCT with INNER JOIN

I have a set of joined tables that I am querying in the following way:
$query = $this->mysqli->query("
SELECT DISTINCT name, website, email
FROM sfe
INNER JOIN ef ON sfe.ID_SFE = ef.ID_SFE
INNER JOIN f ON f.ID_F = ef.ID_F
INNER JOIN ad ON ad.ID_SFE = ef.ID_SFE
WHERE name LIKE '%{$sanitized}%' OR
website LIKE '%{$sanitized}%' OR
business_name LIKE '%{$sanitized}%' OR
email LIKE '%{$sanitized}%'
");
where ID_SFE is the primary key of table sfe and also the foreign key of ef.
When I make this query, I then echo the list of results with the following:
while ($result = $query->fetch_object()) {
$query_result = $result->"name";
echo "$query_result"
}
Because now I would like to also find the value of ID_SFE inside the same while loop, I tried to add ID_SFE in the list of SELECT DISTINCT together with name, website, email, however, I get ERROR: There was a problem with the query.
How can I get the value of ID_SFE and store it to another variable inside the while loop?
Thanks
You can not simply add ID_SFE to the list of fields to retrieve, because such field exists in the ad and ef tables.
You will be able to add ad.ID_SFE and/or ad.ID_SFE fields -note you need to specify the table name when specifying the field, as the fields needs to be referenced unequivocally.

Get Unique rows using Entity framework

I am using 3 tables to set roles for users.
1. module
Id, Name
2. actions
Id , Name ,ModuleId (Foreign key with modules)
3. userActions
Id,UserId,ActionId (Foreign key with actions)
I want to get the unique list of modules for a user from the userActions table . I am using Entity Framework and my database is Mysql
I used the query
var result = (from p in my_accountEntities.useractions
where p.UserId == item.Id
select p.action.module).ToList();
List<module> modules = new List<module>();
if (result != null)
{
modules = (List<module>)result;
}
Its not returning a Unique list , but its returning all the rows in Useraction table.
How can i get the unique list of modules(based on moduleId)
try using the linq .Distinct() extension method
var result = (from p in my_accountEntities.useractions
where p.UserId == item.Id
select p.action.module)
.Distinct().ToList();

Per-row dynamic sql

I have a database representing something like a bookstore. There's a table containing the categories that books can be in. Some categories are defined simply using another table that contains the category-item relationships. But there are also some categories that can be defined programmatically -- a category for a specific author can be defined using a query (SELECT item_id FROM items WHERE author = "John Smith"). So my categories table has a "query" column; if it's not null, I use this to get the items in the category, otherwise I use the category_items table.
Currently, I have the application (PHP code) make this decision, but this means lots of separate queries when we iterate over all the categories. Is there some way to incorporate this dynamic SQL into a join? Something like:
SELECT c.category, IF(c.query IS NULL, count(i.items), count(EXECUTE c.query)
FROM categories c
LEFT OUTER JOIN category_items i
ON c.category = i.category
EXECUTE requires a prepared statement, but I need to prepare a different statement for each row. Also, EXECUTE can't be used in expressions, it's just a toplevel statement. Suggestions?
What happens when you want to list books by publisher? Country? Language? You'd have to throw them all into a single "category_items" table. How would you pick which dynamic query to execute? The query-within-a-query method is not going to work.
I think your concept of "category" is too broad, which is resulting in overly complicated SQL. I would replace "category" to represent only "genre" (for books). Genres are defined in their own table, and item_genres connects them to the items table. Books-by-author and books-by-genre should just be separate queries at the application level, rather than trying to do them both with the same (sort of) query at the database/SQL level. (If you have music as well as books, they probably shouldn't all be stored in a single "items" table because they're different concepts ... have different genres, author vs. artist, etc.)
I know this does not really solve your problem in the way you'd like, but I think you'll be happier not trying to do it that way.
Here's how I finally ended up solving this in the PHP client.
I decided to just keep the membership in the category_items table, and use the dynamic queries during submission to update this table.
This is the function in my script that's called to update an item's categories during submission or updating. It takes a list of user-selected categories (which can only be chosen from categories that don't have dynamic queries), and using this and the dynamic queries it figures out the difference between the categories that an item is currently in and the ones it should be in, and inserts/deletes as necessary to get them in sync. (Note that the actual table names in my DB are not the same as in my question, I was using somewhat generic terms.)
function update_item_categories($dbh, $id, $requested_cats) {
$data = mysql_check($dbh, mysqli_query($dbh, "select id, query from t_ld_categories where query is not null"), 'getting dynamic categories');
$clauses = array();
while ($row = mysqli_fetch_object($data))
$clauses[] = sprintf('select %d cat_id, (%d in (%s)) should_be_in',
$row->id, $id, $row->query);
if (!$requested_cats) $requested_cats[] = -1; // Dummy entry that never matches cat_id
$requested_cat_string = implode(', ', $requested_cats);
$clauses[] = "select c.id cat_id, (c.id in ($requested_cat_string)) should_be_in
from t_ld_categories c
where member_type = 'lessons' and query is null";
$subquery = implode("\nunion all\n", $clauses);
$query = "select c.cat_id cat_id, should_be_in, (member_id is not null) is_in
from ($subquery) c
left outer join t_ld_cat_members m
on c.cat_id = m.cat_id
and m.member_id = $id";
// printf("<pre>$query</pre>");
$data = mysql_check($dbh, mysqli_query($dbh, $query), 'getting current category membership');
$adds = array();
$deletes = array();
while ($row = mysqli_fetch_object($data)) {
if ($row->should_be_in && !$row->is_in) $adds[] = "({$row->cat_id}, $id)";
elseif (!$row->should_be_in && $row->is_in) $deletes[] = "(cat_id = {$row->cat_id} and member_id = $id)";
}
if ($deletes) {
$delete_string = implode(' or ', $deletes);
mysql_check($dbh, mysqli_query($dbh, "delete from t_ld_cat_members where $delete_string"), 'deleting old categories');
}
if ($adds) {
$add_string = implode(', ', $adds);
mysql_check($dbh, mysqli_query($dbh, "insert into t_ld_cat_members (cat_id, member_id) values $add_string"),
"adding new categories");
}
}

LINQ AssociateWith for data context generating LEFT JOIN instead of INNER JOIN

I'm trying to create an "AssociateWith" data load option for my context to filter rows that the current user has access to. My simple data model is as such:
Tables
UserPermissions
PK: ID
FK: RelatedUserID nullable
FK: RelatedItemID not nullable
Users
PK: ID
Items
PK: ID
FK: RelatedCategoryID not nullable
Categories
PK: ID
On my datacontext, I'm trying to do something like:
//dbContext is my LINQ context
//current_user_id is the id of my current logged in user
var dlo = new DataLoadOptions();
//issue->UsersPermissions is one->many relationship
// if I omit this LoadWith line, no associatewith filter
// is created in the generated sql code
dlo.LoadWith<Items>(i=>i.UserPermissions);
dlo.AssociateWith<Items>(i=>
i.UserPermissions.Where(p=>
p.RelatedUserID.HasValue && p.RelatedUserID.Value == current_users_id));
dbContext.LoadOptions = dlo;
Everything seems reasonable to me, but the query generated in SQL looks like this:
SELECT [t0].[ID], [t1].[ID] AS [ID2], [t1].[RelatedUserID], [t1].[RelatedItemID]
(SELECT COUNT(*)
FROM [dbo].[UserPermissions] AS [t2]
WHERE (([t2].[RelatedUserID] = #p0)) AND ([t2].[RelatedItemID] = [t0].[ID])
) AS [value]
FROM [dbo].[Items] AS [t0]
LEFT OUTER JOIN [dbo].[UserPermissions] AS [t1] ON ([t1].[RelatedUserID] = #p0)
AND
([t1].[RelatedItemID] = [t0].[ID])
ORDER BY [t0].[ID], [t1].[ID]
How can I force an INNER JOIN instead of the LEFT OUTER JOIN? Am I modeling my relationship incorrectly on the backend?
Thanks for any help.
MMAS
LINQ to SQL generates the LEFT OUTER JOIN simply because UserPermissions.RelatedUserID column is nullable. The underlying LINQ provider just isn't 'smart' enough to further optimize that query to a simple INNER JOIN. I don't think there is anything you can do about this.