Multiple Joins with Count on the same table - mysql

I'm trying to run a usage report. Basically there is a transaction log table that records every action taken against the database from the website.
i have three tables:
**organization:**
id
name
**people:**
id
organization_id
**transaction_log:**
id
people_id
table
action
Now, lets say I have 4 other tables that users create records in, those would look like this in the transaction_log:
{id: 1, people_id: 33, table: "tablea", action: "create"}
{id: 2, people_id: 44, table: "tableb", action: "create"}
What I want:
I want a result that looks something like this:
{organization_id: 1, name: "some org", tbla_count: 2000, tblb_count: 3000},
{organization_id: 2, name: "other org", tbla_count: 100, tblb_count:100}
Currently i've been doing this one query at a time (one for each table), but we have a lot more than 4 tables so it'd be nice if i could make it run all at once. Here is what I already have:
select
p.organization_id,
count(tl.action) as tbla_count
from
people p
LEFT JOIN
transaction_log tl ON p.id = tl.people_id AND tl.table = 'tablea' and tl.action = 'create'
GROUP BY
p.organization_id
ORDER BY
p.organization_id
When i try and add just another left join the numbers get way wrong. I did that by doing:
select
p.organization_id,
count(tl.action) as tbla_count
count(t2.action) as tblb_count
from
people p
LEFT JOIN
transaction_log tl ON p.id = tl.people_id AND tl.table = 'tablea' and tl.action = 'create'
LEFT JOIN
transaction_log t2 ON p.id = t2.people_id AND t2.table ='tableb' and t2.action = 'create
GROUP BY
p.organization_id
ORDER BY
p.organization_id

I believe it would work to first count the actions per person for each table in subqueries. Left join to each subquery result and summarize on total action count. I think your issue is that there could be many actions per people_id on each table.
select
p.organization_id,
sum(tl.action_count) as tbla_count
sum(t2.action_count) as tblb_count
from
people p
LEFT JOIN
(select people_id, count(action) as action_count
from transaction_log
where table = 'tablea' and action = 'create' group by people_id) tl
ON p.id = tl.people_id
LEFT JOIN
(select transaction_log people_id, count(action) as action_count
from transaction_log
where table = 'tableb' and action = 'create' group by people_id) t2
ON p.id = t2.people_id
GROUP BY
p.organization_id
ORDER BY
p.organization_id

Related

SQL: count rows from 3rd table

I have 3 tables, the first table is the account_has_account1 where i store the relation between accounts and it's columns are account_id, account_id1, status where account_id is the account doing following to account_id1 and status is an enum type with values active, inactive where active indicates if the account is actually following, if it's inactive, then account stopped following.
the second table is named account_has_photos which i store the photos one account has stored in the database, so it's columns are account_id, photos_id, so i need this table to get all photos from one account which another account is following.
But all these have messages posted on them, and here is where the 3rd table comes which is named photos_has_message_photos, from this table i only need a count of all posted messages in one photo, the columns are photos_id, message_photos_id
for now my query is this:
SELECT account_has_photos.photos_id as id, "photos" as type, account_has_photos.update_at, account_has_photos.account_id
FROM account_has_account1
JOIN account_has_photos
ON (account_has_photos.account_id = account_has_account1.account_id1 AND account_has_photos.type_id = 17)
WHERE account_has_account1.account_id = 7 AND account_has_account1.`status` = "Active"
it shows all photos from accounts on which account id 7 is following, but on my attempts on getting the total messages have failed, i thought on doing an INNER JOIN like this:
INNER JOIN (
SELECT photos_has_message_photos.photos_id, count(photos_has_message_photos.photos_id) as total
FROM photos_has_message_photos
) posts
ON(posts.photos_id = account_has_photos.photos_id)
and then i select from main posts.total, but it does not show any row, not even the photos, the result is empty at this point and i have no idea why and what to do.
the complete query is like this:
SELECT account_has_photos.photos_id as id, "photos" as type, account_has_photos.update_at, account_has_photos.account_id, posts.total
FROM account_has_account1
JOIN account_has_photos
ON (account_has_photos.account_id = account_has_account1.account_id1 AND account_has_photos.type_id = 17)
INNER JOIN (
SELECT photos_has_message_photos.photos_id, count(photos_has_message_photos.photos_id) as total
FROM photos_has_message_photos
) posts
ON(posts.photos_id = account_has_photos.photos_id)
WHERE account_has_account1.account_id = 7 AND account_has_account1.`status` = "Active"
again, i only need a total of rows which are messages from each photos found
Try this query updated inner select
SELECT ahp.photos_id as id, "photos" as type, ahp.update_at, ahp.account_id,posts.total
FROM account_has_account1
JOIN account_has_photos
ON (ahp.account_id = account_has_account1.account_id1 AND ahp.type_id = 17) INNER JOIN (
SELECT phmp.photos_id, count(*) as total FROM photos_has_message_photos GROUP BY phmp.photos_id
) posts
ON(posts.photos_id = ahp.photos_id) WHERE account_has_account1.account_id = 7 AND account_has_account1.`status` = "Active"

Cross Referencing Multiple Tables

What I was trying to do is to get data from multiple tables, supposed that I have the following results in my query:
The numbers in the column ticket_item_type represents certain table. For example, 2 is for tbl_company and 3 is for tbl_lease. Then the details represents the id of a certain record in that table.
Suppose that I want to get the title of those records using ticket_item_type and details. Is it possible to embed it to the results? Or should I make separate queries for each.
I know JOIN, but I is it only for single table?
Here's my MYSQL query for the image above:
SELECT *
FROM
(SELECT *
FROM ticket_items
WHERE hs_customer = 1
AND ticket IN
(SELECT id
FROM tickets
WHERE hs_customer='1'
AND ticket_status = 'dispatch_reviewed')
AND ticket IN
(SELECT ticket
FROM ticket_items
WHERE ticket_item_type = 5
AND details = '159')) AS TB1
WHERE ticket_item_type IN (3,
2,
8)
You could try something like this:
SELECT
TB1.*,
CASE
WHEN TB1.ticket_item_type = 2 THEN t2.title
WHEN TB1.ticket_item_type = 3 THEN t3.title
WHEN TB1.ticket_item_type = 8 THEN t8.title
ELSE 'NA'
END as title
FROM
(
SELECT *
FROM ticket_items
WHERE hs_customer = 1
AND ticket IN (SELECT id FROM tickets WHERE hs_customer='1' AND ticket_status = 'dispatch_reviewed')
AND ticket IN (SELECT ticket FROM ticket_items WHERE ticket_item_type = 5 AND details = '159')
) AS TB1
LEFT JOIN tbl_company t2 ON TB1.details = t2.id
LEFT JOIN tbl_lease t3 ON TB1.details = t3.id
LEFT JOIN tbl_next t8 ON TB1.details = t8.id
WHERE ticket_item_type IN (3, 2, 8)
However, this is not a design that I would prefer. Without looking at details of your database it's going to be hard to write a query to cover multiple types of ticket_item_type. I hope this query works for you, though.

Obtain distinct row and * from one table

I'm trying obtain one tabla of cars which have three rows in relations with other three columns (make, model and group) and I only want obtain one car by model.
Here a image of MySQL table:
You will see three rows with same model_id (model_id is foreign key the other table, the other table is called models)
My SQL query for obtain those cars are:
SELECT *
FROM gm_cars AS cars
INNER JOIN gm_cars_makes AS makes
ON (cars.make_id = makes.make_id)
INNER JOIN gm_cars_models AS models
ON (cars.model_id = models.model_id)
INNER JOIN gm_cars_groups AS groups
ON (cars.group_id = groups.group_id) AND
makes.make_visible = 1
ORDER BY cars.model_id;
but I wish obtain one row for one model, here one example (I have used Photoshop):
Some like: SELECT *, DISTINCT(model_id) FROM cars
If you still want to return all columns, you can create sub-query to return only one Car per Model and then write you query as before:
SELECT * FROM gm_cars AS cars
INNER JOIN (SELECT model_id, MAX(car_Id) AS car_Id FROM gm_cars GROUP BY model_id) AS grp_cars ON grp_cars.car_Id = cars.car_Id
INNER JOIN gm_cars_makes AS makes ON (cars.make_id = makes.make_id)
INNER JOIN gm_cars_models AS models ON (cars.model_id = models.model_id)
INNER JOIN gm_cars_groups AS groups ON (cars.group_id = groups.group_id) AND makes.make_visible = 1 ORDER BY cars.model_id;
You can also add GROUP BY in you main query, and add aggregation function to all other columns. But it can return you columns from different cars with the same model:
SELECT cars.model_id,
MAX(car_passengers),
MAX(car_suitcases),
....
FROM gm_cars AS cars
INNER JOIN (SELECT model_id, MAX(car_Id) AS car_Id FROM gm_cars GROUP BY model_id) AS grp_cars ON grp_cars.car_Id = cars.car_Id
INNER JOIN gm_cars_makes AS makes ON (cars.make_id = makes.make_id)
INNER JOIN gm_cars_models AS models ON (cars.model_id = models.model_id)
INNER JOIN gm_cars_groups AS groups ON (cars.group_id = groups.group_id) AND makes.make_visible = 1
GROUP BY cars.model_id
ORDER BY cars.model_id;
SELECT *
FROM gm_cars AS cars
INNER JOIN gm_cars_makes AS makes ON (cars.make_id = makes.make_id)
INNER JOIN gm_cars_models AS models ON (cars.model_id = models.model_id)
INNER JOIN gm_cars_groups AS groups ON (cars.group_id = groups.group_id)
AND makes.make_visible = 1
group by (model_id)
ORDER BY cars.model_id
is this helpful?
;with cte
AS
(
Select *,row_number() OVER(partition by model_id order by car id) rn from gm_cars
)
select * from cte where rn=1

JOIN query not behaving as expected

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

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)