To filter a table output of selected entries from a single table i would need something like a multiple JOIN request through several tables.
I want to filter a table of people by a special column in the table. Lets say this column is "tasks." Now tasks is also another table with the column "people" and the values between those two tables are connected with an existant "join" table in the database, which is matching several IDs of one table to each ID of the other table.
Now if this would be simple as that i could just filter with an INNER JOIN and a special condition. The problem is, that the entries of the table "tasks" are connected to another table over a "join" table in the database. To simplify things lets say it is "settings". So each "task" consists of several "settings" which are connected via a join table in their IDs.
So what is the input?
I got an array of IDs, which are representing the settings-ids i do not want to be shown.
What should be the output?
As already said i want a filtered output of "people" while the filter is "settings."
I want the sql request to return each entry of the table "people" with only joined tasks that are not joining any of the "setting-ids" from the array.
I hope you can help me with that.
Thanks in advance!
Example
Settings-Table:
1. Is in progress
2. Is important
3. Has unsolved issues
Tasks-Table: (settings.tasks is the join table between many tasks to many settings)
1. Task from 01.01.2012 - JOINS Settings in 1 and 3 (In progress + unsolved issues)
2. Task from 02.01.2012 - JOINS Settings in 2 (Is important)
3. Task from 03.01.2012 - JOINS Settings in 1 and 2 (...)
People-Table: (people.tasks is the join table between many people to many tasks)
1. Guy - JOINS Tasks in 1, 2, 3 (Has been assigned to all 3 tasks)
2. Dude - JOINS Tasks in 1 (Has been assigned to the Task from 01.01.2012)
3. Girl - JOINS Tasks in 2, 3 (...)
Now there is an array passed to a sql query
[2,3] should return noone because every person is assigned in a task that was either important or had unsolved issues!
[3] would return me only the person "Girl" because it is the only one that is assigned to tasks (2 and 3) that had no unsolved issues.
I hope it is clear now. :)
SELECT DISTINCT PEOPLE.*
FROM PEOPLE INNER JOIN PEOPLE_TASKS ON PEOPLE.PERSON_ID = PEOPLE_TASKS.PERSON_ID
WHERE TASK_ID NOT IN (SELECT DISTINCT TASK_ID
FROM TASK_SETTINGS
WHERE SETTING_ID = <Id you don't want>)
EDIT (for supplying multiple setting ids you don't want)
SELECT DISTINCT PEOPLE.*
FROM PEOPLE INNER JOIN PEOPLE_TASKS ON PEOPLE.PERSON_ID = PEOPLE_TASKS.PERSON_ID
WHERE TASK_ID NOT IN (SELECT DISTINCT TASK_ID
FROM TASK_SETTINGS
WHERE SETTING_ID IN (<Id you don't want>))
First you have to join table people and table tasks with the join table, let's call it people_tasks.
select distinct p.* from people p
inner join people_tasks pt on p.people_id = pt.people_id
inner join tasks on t.tasks_id = pt.tasks_id
Then you have to join table tasks and table settings with the join table, let's call it tasks_settings. You have to join them in the current select.
select distinct p.* from people p
inner join people_tasks pt on p.people_id = pt.people_id
inner join tasks on t.tasks_id = pt.tasks_id
inner join tasks_settings ts on t.tasks_id = ts.tasks_id
inner join settings s on s.settings_id = ts.settings_id
and now you have all people connected with its tasks and its settings. Finally you need the restriction. With the people with the settings selected, you choose the others like this:
select distinct p.people_id from people p
inner join people_tasks pt on p.people_id = pt.people_id
where p.people_id not in (
select distinct p2.people_id from people p2
inner join people_tasks pt2 on p2.people_id = pt2.people_id
inner join tasks t2 on t2.tasks_id = pt2.tasks_id
inner join tasks_settings ts2 on t2.tasks_id = ts2.tasks_id
inner join settings s2 on s2.settings_id = ts2.settings_id
where s2.settings_id in (list of ids)
)
Related
i have a problem in MySQL where I use the COUNT function for conditions. However, when combining this with joins, although I use grouping, the COUNT values include ALL rows, even the ones filtered out.
I'm providing a minimal working example, which however maybe does not make a practical sense or is designed smartly.
So assume I have 3 tables:
products with fields: productId, name, active (boolean)
teams with fields: teamId, name
rel_production with fields: teamId, productId
So basically I have products and teams with ids and names. Products can be active (lets say that means that they are still in production or so).
And then I have a relation which team is working on which product.
To explain my problem, assume the following minimal amount of data to clarify the problem is contained inside the tables:
products
teams
rel_production
Now the query that I want to do is, in plain english: "I want all teams that are working on exactly 2 products while atleast one product must be active."
The query in general works and is the following in mysql:
SELECT
teams.*,
"r_count:",
r_count.*,
COUNT(r_count.productId),
"r_active:",
r_active.*,
"p_active:",
p_active.*
FROM teams
INNER JOIN rel_production r_active ON r_active.teamId = teams.teamId
INNER JOIN products p_active ON p_active.productId = r_active.productId AND p_active.active
INNER JOIN rel_production r_count ON r_count.teamId = teams.teamId
GROUP BY teams.teamId, r_active.teamId
HAVING COUNT(r_count.productId) = 2 #4 is the problem!!!!!!!!!!!!!
Now them problem is with team 1. Because it is working on 2 active products, COUNT(r_count.productId) will be 4 and not 2. So my query will filter it out.
Here is the screenshot with the result without the HAVING clause:
I see why this happens, because the two inner joins on rel_production will cause 4 rows to be generated. But then they are merged always together to one using the GROUP BY. So what I need is the COUNT after the GROUP and not before.
How can I fix this?
Perform the filtering on teams in a separate subquery, and then join to that:
SELECT
t1.teamId,
t1.name
FROM teams t1
INNER JOIN
(
SELECT t1.teamId
FROM rel_production t1
INNER JOIN products t2
ON t1.productId = t2.productId
GROUP BY t1.teamId
HAVING COUNT(DISTINCT t1.productId) = 2 AND SUM(t2.active) > 0
) t2
ON t1.teamId = t2.teamId;
SQLFiddle
So I always have trouble with this type of query. I have 2 tables and I need to combine them as 3 but I need the query to see if a record does not exist in one connection.
The design is this:
Table 1 connects to Table 2 looking for existing records. Table 1 then needs to connect to table 2 again on a different record id but looking for not existing.
My current code looks like this:
select p.ProfileId, p.OrgInd, p.OrgName, p1.OrgInd, p1.ProfileId
from profile as p inner join
ProfileRelations as pa on p.ProfileId = pa.ProfileID inner join
profile as p1 on p1.ProfileId = pa.RelProfileID
I need profile(p) to connect to PRofileRelations(pa) as it states but I then need to connect ProfileRelations(pa) back on Profile(p1) where p1.profileid does not exist in ProfileRelations(pa) looking specifically at the column RelProfileID.
This is an association table to look at related records. The Profile table holds all of my customers and I can relate them as connections. What I am looking for is show me any profile that has no related records. The code given shows me those who do have related records, I just now want to see those who have none.
Thanks.
Jayson
Visual understanding of Joins: https://blog.codinghorror.com/a-visual-explanation-of-sql-joins/
Use a left join:
All profiles without a related relation. In this case we join the tables but we know the values for p1 and pa will be null becuase the left join will include all profiles and only keep those with a related record in the relations table.
SELECT p.ProfileId, p.OrgInd, p.OrgName, p1.OrgInd, p1.ProfileId
FROM profile as p
LEFT join ProfileRelations as pa
on p.ProfileId = pa.ProfileID
LEFT join profile as p1
on p1.ProfileId = pa.RelProfileID
WHERE PA.ProfileID is null
In the above P1 values will always be null because of the where clause.
OR uses not exists and correlated subquery
All profiles w/o a related profile relation. since we know there is no relation there is no data we need from the other tables; thus we can exclude them from the select simplifying the query and gaining performance.
SELECT p.ProfileId, p.OrgInd, p.OrgName
FROM profile as p
WHERE NOT EXISTS (SELECT *
FROM ProfileRelations as pa
WHERE p.ProfileId = pa.ProfileID)
In this we don't select the P1 table at all since there is no relation record.
First of all: You want to select profiles. So do this; select from the profiles table. You want to apply criteria (missing relations); so put this in the WHERE clause. Criteria belongs in the WHERE clause. There is no need to join.
All profiles that have no related profiles:
select *
from profile
where ProfileId not in (select ProfileID from ProfileRelations)
and ProfileId not in (select RelProfileID from ProfileRelations);
or
select *
from profile p
where not exists
(
select *
from ProfileRelations pr
where p.ProfileId in (pr.ProfileId, pr.RelProfileID)
);
All profile pairs that are not related:
select *
from profile p1
join profile p2 on p2.ProfileID <> p1.ProfileID
where not exists
(
select *
from ProfileRelations pr
where (pr.ProfileId = p1.ProfileId and pr.RelProfileID = p2.ProfileId)
or (pr.ProfileId = p2.ProfileId and pr.RelProfileID = p1.ProfileId)
);
Per your problem statement, please try this:
select p.ProfileId, p.OrgInd, p.OrgName, p1.OrgInd, p1.ProfileId
from profile as p
inner join
ProfileRelations as pa
on p.ProfileId = pa.ProfileID
where pa.RelProfileID not in (select ProfileId from profile);
I have two Tables 1st
Users
SELECT users.*,tasks.*
from users
join tasks on users.id=tasks.assign_to
2nd is
Tasks
SELECT users.*,tasks.*
from users
join tasks on users.id=tasks.created_by
where tasks.created_by=5
here i want to show result this result is is not correct it shows only created_by correct but assign_to not correct.
I need Name of both created by and assign to users in one query
Try two joins of the users table.
SELECT
tasks.*,
created_by_user.name AS created_name,
assigned_to_user.name AS assigned_name
FROM tasks
INNER JOIN users created_by_user
ON created_by_user.id = tasks.created_by
INNER JOIN users assigned_to_user
ON assigned_to_user.id = tasks.assign_to
WHERE tasks.created_by = 5
I posted a question about 2 weeks ago about 'one to many' relation between SQL tables. Now I have a bit of a different scenario. Basically, there are two tables - coffee_users and coffee_product_registrations. The latter is connected to coffee_users table with 'uid' column. So basically coffee_users.uid = coffee_product_registrations.uid
A single user can have multiple products registered.
What I want to do is to display some product information (from coffee_product_registrations) along with some user information (from coffee_users), BUT retrieve only those rows that have more than 1 product registrations.
So to simplify, here are the steps I need to take:
Join two tables
Select users that have multiple products registered
Display all their products along with their names and stuff
My current SQL query looks like this:
SELECT c.uid, c.name, cpr.model
FROM coffee_users c
JOIN coffee_product_registrations cpr on c.uid = cpr.uid
GROUP BY c.uid
HAVING COUNT(cpr.uid) > 1
This joins the two tables on 'uid' column but displays only 1 row for each user. It selects just users that have multiple products registered.
Now I need to take these IDs and select ALL the products from coffee_product_registrations based on them.
I cannot figure out how to put this in one query.
Replace cpr.*, c.* with columns which you want to extract feom the query
Try this:
SELECT cpr.*, c.*
FROM coffee_product_registrations cpr
INNER JOIN coffee_users c ON c.uid = cpr.uid
INNER JOIN (SELECT cpr.uid
FROM coffee_product_registrations cpr
GROUP BY cpr.uid
HAVING COUNT(DISTINCT cpr.productId) > 1
) AS A ON c.uid = A.uid;
I've to add a notification module on an existing project.
My table structure is like on the pic.
Picture :
as you see on the picture every notification has a type and a releatedID.
Type 1 = reservation is cancelled, releated ID is the id on "reservations" table
Type 3 = account balance is under the given min. limit so releatedID is the id on "account_movements"
table
what I'm trying to do is a conditional join to avoid 2 different sql queries;
Get all notifications which are belong to the person
Get notification details from different tables based on "notification.Type"
So the question is can I do it in one query ?
Something like the following should work. Basically you specify which records in your notifications table join with records in your reservations table or your account_movements table when you join those in. Use a LEFT JOIN so that ALL of your notification records make it through, and only those records in the reservations table OR the account_movements that have a match make it through.
SELECT
n.id,
n.type,
n.companyid,
n.personid,
n.relatedid,
n.description,
r.details as reservation_details,
am.details as account_movement_details,
COALESCE(r.details, am.details) AS combined_detail
FROM
notifications n
LEFT OUTER JOIN reservations r ON
n.relatedid = r.id AND
n.type = 1
LEFT OUTER JOIN account_movements am ON
n.relatedid = am.id AND
n.type = 3
Here is a SQL FIDDLE with the solution as well.
I added in the COALESCE() just to show that since the JOINS are mutually exclusive, you can safely combine columns from your reservations table and your account_movements table into a single column without fear of missing or duplicating any data.
If you do a left join, you won't have to have any condition :
SELECT * FROM notifications n
LEFT JOIN reservations r ON n.releatedID = r.id
LEFT JOIN account_movements m ON m.releatedID = m.id