JOIN query not behaving as expected - mysql

I'm working on a query that doesn't behave as expected, and would appreciate any help on pointing me in the right direction.
TABLES
I have three central tables consists of the following.
table_categories
- id (int)
- name (varchar)
table_events
- id (int)
- name (varchar)
- created (date time)
table_taxonomy
- cat_id (id from table_categories)
- event_id (id from table_events)
- type (varchar in this example always 'category')
GOAL
Count events created after a certain date in each category. The result should be something like:
COUNT NAME ID
3 Rock 1
2 Punk 3
QUERY
This is the query that I've come up with. The problem is that the result doesn't care about the created date, and grabs all events regardles of when they where created.
SELECT COUNT(*) AS count,
            table_categories.name,
            table_categories.id
            FROM table_taxonomy
            INNER JOIN table_categories
            ON table_categories.id = table_taxonomy.cat_id
            LEFT JOIN table_events
            ON table_events.id = table_taxonomy.events_id
            WHERE table_taxonomy.type = 'category'
            AND table_taxonomy.cat_id IN(2,3)
            AND table_events.created > '2012-10-07 05:30:00'
            GROUP BY (table_categories.name)

try this one, just small change while comparing date :
SELECT COUNT(*) AS count,
table_categories.name,
table_categories.id
FROM table_taxonomy
INNER JOIN table_categories
ON table_categories.id = table_taxonomy.cat_id
LEFT JOIN table_events
ON table_events.id = table_taxonomy.events_id
WHERE table_taxonomy.type = 'category'
AND table_taxonomy.cat_id IN(2,3)
AND Date(table_events.created) > '2012-10-07 05:30:00'
GROUP BY (table_categories.name)

Try this one,
SELECT c.ID, c.Name, COUNT(*)
FROM table_categories a
INNER JOIN table_taxonomy b
ON a.id = b.cat_id
INNER JOIN table_events c
ON b.event_ID = c.ID
WHERE c.created > 'dateHere'
-- AND ..... -- other conditions here
GROUP BY c.ID, c.Name

Related

Need count of transactional table based on other tables including zeros where there are no matches

I have four tables, three of which are pretty static: haul_types, dumpster_type_team (the dumpster_type_team has the many-to-many relationship between dumpster_types and teams), and users. The fourth table, hauls, has transactional data.
haul_types:
id
name
dumpster_type_team:
id
dumpster_type_id
team_id
users:
id
first_name
last_name
is_driver
team_id
hauls:
haul_type_id
haul_status_id
set_dumpster_type_id
completed_driver_id
team_id
I would like a query that has a combination of dumpster_types, haul_types, and drivers (users) and a count of the hauls they were involved in. In some cases, there should be a count of zero because some drivers haven't completed hauls for every haul_type / dumpster type combination.
Here's the query I have so far that seems to be behaving as if it is an inner join because the records are getting filtered to only show where there are matches:
SELECT
c.haul_type_id,
c.dumpster_type_id,
c.driver_id,
count(h.id) AS haul_count
FROM
hauls h
RIGHT JOIN ( SELECT DISTINCT
ht.id AS haul_type_id,
dtt.dumpster_type_id AS dumpster_type_id,
dtt.team_id AS team_id,
u.id AS driver_id
FROM
haul_types ht
CROSS JOIN dumpster_type_team dtt
CROSS JOIN users u
WHERE
u.team_id = dtt.team_id
AND u.is_driver = TRUE) c ON c.haul_type_id = h.haul_type_id
AND c.dumpster_type_id = h.set_dumpster_type_id
AND c.driver_id = h.completed_driver_id
AND c.team_id = h.team_id
WHERE
h.team_id = 9
AND h.haul_status_id = 3
AND h.completed_driver_id IS NOT NULL
GROUP BY
c.haul_type_id, c.dumpster_type_id, c.driver_id
When I run the subquery in isolation:
SELECT DISTINCT
ht.id AS haul_type_id,
dtt.dumpster_type_id AS dumpster_type_id,
dtt.team_id AS team_id,
u.id AS driver_id
FROM
haul_types ht
CROSS JOIN dumpster_type_team dtt
CROSS JOIN users u
WHERE
u.team_id = dtt.team_id
AND u.is_driver = TRUE
I get the results I want: a row for each permutation of haul_type, dumpster_type, driver_id, and team_id. However, when I run the entire query, I get filtered results despite the right join.
What I would like to have is the following:
If I have 4 haul_types: delivery, swap, live, pickup
and 2 dumpster_types: 10YD, 15YD
and 2 drivers: 1, 2
I would like a haul count for the combination of haul_type, dumpster_type, and driver. If there are no hauls matching the row, show 0:
Any help is appreciated. Thank you
The description of the question and the query seem to have little to do with each other. I don't know what a "pivot table" is supposed to be.
I would like a query that has a combination of dumpster_types, haul_types, and drivers (users) and a count of the hauls they were involved in.
This sounds like a cross join to generate the rows and then a left join/group by to calculate the results:
select d.dumpster_id, ht.haul_type_id, d.driver_id, count(h.driver_id)
from dumpster_types d cross join
haul_types ht cross join
drivers d left join
hauls h
on h.dumpster_id = d.dumpster_id and
h.haul_type_id = ht.haul_type_id and
h.driver_id = d.driver_id
group by d.dumpster_id, ht.haul_type_id, d.driver_id;
Running the query #GordonLinoff provided, exposed the issue I was facing - when applying a where clause on the top level query, the results were getting filtered to only matches. I moved the where clause to individual subqueries and now I am getting all expected results.
Not sure if this is the most efficient way to write it but it yields the correct results:
SELECT
d.dumpster_type_id,
ht.id AS haul_type_id,
u.id AS driver_id,
count(h.id) AS haul_count
FROM (
SELECT
dumpster_type_id,
team_id
FROM
dumpster_type_team
WHERE
team_id = 9) d
CROSS JOIN haul_types ht
CROSS JOIN (
SELECT
users.id
FROM
users
WHERE
users.is_driver = TRUE
AND users.team_id = 9) u
LEFT JOIN (
SELECT
id, set_dumpster_type_id, haul_type_id, completed_driver_id, team_id
FROM
hauls
WHERE
haul_status_id = 3
AND team_id = 9) h ON h.set_dumpster_type_id = d.dumpster_type_id
AND h.haul_type_id = ht.id
AND h.completed_driver_id = u.id
AND h.team_id = d.team_id
GROUP BY
d.dumpster_type_id,
ht.id,
u.id

SQL SELECT from 3 tables

ft_pupils
- id //Primary Key
- name
- start_date
ft_entries
- id
- pupil_id //Foreign Key
- aol_id
ft_aol
- id
- title
For every entry in the ft_entries table I want to use a SELECT to select every entry in the ft_entries table but with the aol_id replaced with the title inside of ft_aol.
I have managed to get:
SELECT name, aol_id FROM ft_pupils, ft_entries WHERE pupil_id = ft_pupils.id
to work fine.
I want the WHERE to be WHERE pupil_id = ft_pupils.id
I am so confused right now.
You can try this to get what you want by join 3 table.
SELECT Name,title FROM ft_entries
INNER JOIN ft_pupils ON pupil_id = ft_pupils.id
LEFT JOIN ft_aol ON ft_aol.id = aol_id
This will show you how to get the data you're looking for and display all the ids. You can remove the ids if you don't care to see them:
SELECT a.id,
b.pupil_id,
c.id,
a.NAME,
a.start_date,
c.title
FROM ft_pupils a
JOIN ft_entries b ON a.id = b.pupil_id
JOIN ft_aol c ON c.id = b.aol_id

What is wrong with this MySQL query (formatting left join)?

I have a query as follows:
SELECT
staff_names.staff_ID AS sid
staff_names.name AS name,
staff_names.rec_type AS rec_type,
prod_staff.specialized AS specialized,
compspec.name AS compspec_name
FROM staff_names JOIN prod_staff USING (staff_ID)
LEFT JOIN (prod_staff_compspec JOIN company_list USING (comp_ID)) compspec
USING (prod_ID, staff_ID, role_ID)
WHERE prod_staff.role_ID = 2
AND prod_staff.prod_ID = 27
AND prod_staff.asst = 'n'
AND episode IS NOT NULL
ORDER BY name
Running this as-is says there's an error near the 'compspec' alias. Removing that and changing 'compspec' to 'company_list' in the SELECT clause returns no rows, even though it should return 1 with the given values. The left join seems to be the problem, but I don't how it should be formatted.
The prod_staff table has prod_ID, staff_ID and role_ID fields. prod_staff_compspec has these and a comp_ID field. prod_staff may or may not have a matching prod_staff_compspec row, but prod_staff_compspec always has a matching company_list row.
What I want to do is retrieve a list of all staff names associated with a given role_ID and prod_ID in the prod_staff table, as well as a company name from the company_list table, if a link to such exists in the prod_staff_compspec table (only a small minority have one).
Switched to ON to define the table relations. LEFT JOIN (prod_staff_compspec JOIN company_list USING (comp_ID)) compspec is switched to 2 left join.
select a.staff_id sid, a.name, a.rec_type, b.specialized, d.name compspec_name
from staff_names a
join prod_staff b on a.staff_id = b.staff_id
left join prod_staff_compspec c on b.prod_id = c.prod_id and b.staff_id = c.staff_id and b.role_id = c.role_id
left join company_list d on c.comp_id = d.comp_id
where b.role_id = 2 and b.prod_id = 27 and b.asst = 'n' and episode is not null
order by a.name;

SQL: Get latest entries from history table

I have 3 tables
person (id, name)
area (id, number)
history (id, person_id, area_id, type, datetime)
In this tables I store the info which person had which area at a specific time. It is like a salesman travels in an area for a while and then he gets another area. He can also have multiple areas at a time.
history type = 'I' for CheckIn or 'O' for Checkout.
Example:
id person_id area_id type datetime
1 2 5 'O' '2011-12-01'
2 2 5 'I' '2011-12-31'
A person started traveling in area 5 at 2011-12-01 and gave it back on 2011-12-31.
Now I want to have a list of all the areas all persons have right now.
person1.name, area1.number, area2.number, area6.name
person2.name, area5.number, area9.number
....
The output could be like this too (it doesn't matter):
person1.name, area1.number
person1.name, area2.number
person1.name, area6.number
person2.name, area5.number
....
How can I do that?
This question is, indeed, quite tricky. You need a list of the entries in history where, for a given user and area, there is an 'O' record with no subsequent 'I' record. Working with just the history table, that translates to:
SELECT ho.person_id, ho.area_id, ho.type, MAX(ho.datetime)
FROM History AS ho
WHERE ho.type = 'O'
AND NOT EXISTS(SELECT *
FROM History AS hi
WHERE hi.person_id = ho.person_id
AND hi.area_id = ho.area_id
AND hi.type = 'I'
AND hi.datetime > ho.datetime
)
GROUP BY ho.person_id, ho.area_id, ho.type;
Then, since you're really only after the person's name and the area's number (though why the area number can't be the same as its ID I am not sure), you need to adapt slightly, joining with the extra two tables:
SELECT p.name, a.number
FROM History AS ho
JOIN Person AS p ON ho.person_id = p.id
JOIN Area AS a ON ho.area_id = a.id
WHERE ho.type = 'O'
AND NOT EXISTS(SELECT *
FROM History AS hi
WHERE hi.person_id = ho.person_id
AND hi.area_id = ho.area_id
AND hi.type = 'I'
AND hi.datetime > ho.datetime
);
The NOT EXISTS clause is a correlated sub-query; that tends to be inefficient. You might be able to recast it as a LEFT OUTER JOIN with appropriate join and filter conditions:
SELECT p.name, a.number
FROM History AS ho
JOIN Person AS p ON ho.person_id = p.id
JOIN Area AS a ON ho.area_id = a.id
LEFT OUTER JOIN History AS hi
ON hi.person_id = ho.person_id
AND hi.area_id = ho.area_id
AND hi.type = 'I'
AND hi.datetime > ho.datetime
WHERE ho.type = 'O'
AND hi.person_id IS NULL;
All SQL unverified.
You're looking for results where each row may have a different number of columns? I think you may want to look into GROUP_CONCAT()
SELECT p.`id`, GROUP_CONCAT(a.`number`, ',') AS `areas` FROM `person` a LEFT JOIN `history` h ON h.`person_id` = p.`id` LEFT JOIN `area` a ON a.`id` = h.`area_id`
I haven't tested this query, but I have used group concat in similar ways before. Naturally, you will want to tailor this to fit your needs. Of course, group concat will return a string so it will require post processing to use the data.
EDIT I thikn your question has been edited since I began responding. My query does not really fit your request anymore...
Try this:
select *
from person p
inner join history h on h.person_id = p.id
left outer join history h2 on h2.person_id = p.id and h2.area_id = h.area_id and h2.type = 'O'
inner join areas on a.id = h.area_id
where h2.person_id is null and h.type = 'I'

MySQL SELECT DISTINCT ORDER BY problem

To begin with I have 4 tables I am dealing with.
I have a classes table that is a 1->N relationship with a sections table which also has a 1->N relationship with a lessons table.
So to put it in perpective:
Classes
Sections
Lessons
The last table is an activityLog, when the student accesses a lesson this is recorded using the following:
ActivityLog Row -> actorID (user ID), classID, sectionID, lessonID
I want to pull out the last 5 unique lessons the student has visited. I tried using both DISTINCT and GROUP BY without success.
The same records are being returned each time, not the latest classes that they have visited.
Using GROUP BY
SELECT activityLog.actorID, activityLog.activityDate,
strClasses.classID, strClasses.className,
strSections.sectionID, strSections.sectionName,
strLessons.lessonID, strLessons.lessonName
FROM activityLog
LEFT JOIN strClasses ON strClasses.classID = activityLog.classID
LEFT JOIN strSections ON strSections.sectionID = activityLog.sectionID
LEFT JOIN strLessons ON strLessons.lessonID = activityLog.lessonID
WHERE activityLog.activityTypeID = 6 AND activityLog.actorID = 3
GROUP BY activityLog.lessonID
ORDER BY activityLog.activityDate DESC
LIMIT 5
Using DISTINCT
SELECT DISTINCT activityLog.actorID,
strClasses.classID, strClasses.className,
strSections.sectionID, strSections.sectionName,
strLessons.lessonID, strLessons.lessonName
FROM activityLog
LEFT JOIN strClasses ON strClasses.classID = activityLog.classID
LEFT JOIN strSections ON strSections.sectionID = activityLog.sectionID
LEFT JOIN strLessons ON strLessons.lessonID = activityLog.lessonID
WHERE activityLog.activityTypeID = 6 AND activityLog.actorID = 3
ORDER BY activityLog.activityDate DESC
LIMIT 5
I cannot figure out why the latest records are not being displayed.
Based on your change, how does this suit you?
SELECT activityLog.actorID, activityLog.activityDate,
strClasses.classID, strClasses.className,
strSections.sectionID, strSections.sectionName,
strLessons.lessonID, strLessons.lessonName
FROM activityLog
LEFT JOIN strClasses ON strClasses.classID = activityLog.classID
LEFT JOIN strSections ON strSections.sectionID = activityLog.sectionID
LEFT JOIN strLessons ON strLessons.lessonID = activityLog.lessonID
WHERE activityLog.activityTypeID = 6 AND activityLog.actorID = 3
AND activityLog.activityDate = (SELECT MAX(activityDate) FROM activityLog AS lookup WHERE lessonID = activityLog.lessonID)
ORDER BY activityLog.activityDate DESC
LIMIT 5
Based on your description, I'm not sure why you're using LEFT JOIN, but I've left it in just in case.
Try group by like below
GROUP BY activityLog.classID,activityLog.sectionID,activityLog.lessonID
I think it will work, or just sent me create scripts for these I will create that query
Well, there's got to be a datetime in the ActivityLog I hope... so Try this:
Select s.Name, c.ClassName
From Students s
left Join On Classes c
On c.ClassId In
(Select Distinct ClassId From Classes
Where (Select Count(Distinct ClassId) From Classes ic
Join ActivityLog l On l.UserId = s.UserId
And l.ClassId = c.ClassId
Where classId = c.ClassId
And activityDateTime > l.activityDateTime)
< 5)