MySQL Select using one table's data as column for query - mysql

I have a database table which each row represents a column of data I'd like pulled for a query. My table is:
tracked_pages
page_id | page_url | page_name
-------------------------------------
1 | users.php | Users
2 | auctions.php | Auctions
3 | receipts.php | Receipts
So essentially, I have another table that tracks each time a page is accessed by a logged-in user. But these 3 pages are the only ones I'm really interested in right now.
This is not my actual query, but just to show how I'd write it if I just hand-wrote it, I'd do something like:
Select user.id, count(clicks1.id) As user_clicks, count(clicks2.id) As auction_clicks, count(clicks3.id) As receipt_clicks
From users
Left Join clicks As clicks1 On (users.id = clicks1.id And clicks1.page = 'users.php')
Left Join clicks As clicks2 On (users.id = clicks2.id And clicks2.page = 'auctions.php')
Left Join clicks As clicks3 On (users.id = clicks3.id And clicks.page = 'receipts.php')
Group By users.id
So I'd expect output similar to:
Output
user_id | user_clicks | auction_clicks | receipt_clicks
-------------------------------------------------------
1 | 54 | 16 | 16
2 | 27 | 22 | 12
3 | 32 | 24 | 38
However, I'd like the query to pull each column from the database instead of having to join the same table manually. The reason is because the tracked_pages table can be updated through a management area and I don't want to have to go update the code each time they want to track a different page.
I know I could just perform a query to get the users, then as I loop through that I could perform another query to get the page counts, but I'm really wanting to make it just one query.
Any help would be appreciated.
Thanks,
James

Related

Using nested SELECT result for IN statement of another nested SELECT

Be gentle. I'm a high school principal coding on the side for our school site.
I have looked at answers, here, here, and here. I might just not know enough to ask the right question.
We have events that have multiple sessions and there are workshops that can be associated with multiple sessions in multiple events.
I'm trying to get a csv result, later to be put into an array, for the associated sessions and events for my Workshops.
The query below works without the second nested Select statement.
In the Alt_Events statement, I need to pull the Event_IDs that are associated with the Session_IDs that are pulled from the first nested Select.
Events
ID | Name | Description
1 | Flex Learning | A day of flexible learning.
2 | Moonshot Expo | A day to join partners to solve problems.
Event_Sessions
ID | Event_ID | Name | Description
1 | 1 | Morning Session | The first session of the day.
2 | 1 | Afternoon Session | The afternoon session.
3 | 1 | Tutoring Session | A chance to get help from teachers.
4 | 2 | Partner Field Trip | The first session of the day.
5 | 2 | Brainstorming Session | The afternoon session.
6 | 2 | Tutoring Session | A chance to get help from teachers.
Event_Workshops
ID | Name | Description
1 | Math Tutorial | Get help from your math teachers.
Event_Workshop_Links
ID | Workshop_ID | Session_ID
1 | 1 | 3
2 | 1 | 6
Output Table:
ID | Name | Description | ... | Alt_Sessions | Alt_Events
1 | Math Tutorial | Get help... | ... | 3,6 | 1,2
Here is my query.
SELECT
ws.ID, ws.Name, ws.Description, ws.Location, ws.Owner_ID, ws.Max_Attendees,
ws.Eng_Major_Allowed, ws.Eng_Minor_Allowed,
ws.HC_Major_Allowed, ws.HC_Minor_Allowed,
ws.IT_Major_Allowed, ws.IT_Minor_Allowed,
u.LastName as Owner_LastName, u.FirstName AS Owner_FirstName, u.Email AS Owner_Email,
(SELECT group_concat(SESSION_ID) FROM Events_Workshops_Links WHERE Workshop_ID = ws.ID) AS Alt_Sessions,
(SELECT group_concat(Event_ID) FROM Event_Sessions WHERE Session_ID IN Alt_Sessions) AS Alt_Events
FROM Event_Workshops as ws
LEFT JOIN users AS u
ON ws.Owner_ID = u.ID
WHERE ws.ID = ?
ORDER BY ws.Name
I need to be able to pull the all event_ids that are in the Alt_Sessions result.
I'm guessing I can't use the result of the first nested query in the second nested query. If that's the problem, how can I pull that list of event ids?
Any and all help is greatly appreciated.
(Updated to show expected output. Also one error in transcribing the query. Session_ID instead of Event_ID in second nested statement.
Use the subquery instead of Alt_Sessions in the IN predicate like below.
(SELECT group_concat(SESSION_ID) FROM Events_Workshops_Links WHERE Workshop_ID = ws.ID) AS Alt_Sessions,
(SELECT group_concat(Event_ID) FROM Event_Sessions WHERE Session_ID IN (SELECT SESSION_ID FROM Events_Workshops_Links WHERE Workshop_ID = ws.ID)) AS Alt_Events
Also, there is a way to make combinations of Alt_Sessions and Alt_Events first and then join to Event_Workshops.
SELECT * FROM Event_Workshops ws
JOIN
(
SELECT
wsl.Workshop_ID,
GROUP_CONCAT(wsl.Session_ID) Alt_Sessions,
GROUP_CONCAT(wsl.ID) Alt_Events
FROM Event_Workshop_Links wsl
GROUP BY wsl.Workshop_ID
) w
ON ws.ID = w.Workshop_ID

MySQL Intermediate-Level Table Relationship

Each row in Table_1 needs to have a relationship with one or more rows that might come from any number of other tables in the database (Table_X). So I set up an intermediate table (Table_2) where each row contains an id from Table_1, and the id from Table_X. It also has its own auto increment id since none of the relationships will be exclusive and therefore both the other ids will not be unique in the table.
My problem now is that when I retrieve the row from Table_1 and would like to see the information from each related row from Table_X, I don't know how to get it. At first I thought I could create a column for the exact name of Table_X for each row in Table_2 and have a second SELECT statement using that information, but I've been seeing inklings about things such as foreign keys and join statements that I think I need to get into. I'm just having trouble sorting it all out. Do I even need Table_2?
This probably isn't overly complicated, but I'm just getting into MySQL and this is the first real challenge I've encountered.
Edit to include requested information: If I understand correctly, I think I'm dealing with a many to many relationship. Table_3 has games; Table_1 has articles. An article can be about multiple games, and a game can also have multiple articles written about it. The only other possibly pertinent information I can see is that when a new article is made, every game that will be related to it is decided all at once. But the list of articles related to a given game can grow over time as more articles are written. That's probably not especially important, however.
If I understood correctly You are talking about one to many relationship in database (for example: one person can have multiple phone numbers), You can store data in two separate tables persons and phones.
Persons:
|person_id|person_name |person_age |
| 1 | Bodan Kustan| 28 |
Phones:
|phone_id |person_id |phone_number|
| 1 | 1 | 31337 |
| 2 | 1 | 370 |
Then you can execute query with Join:
SELLECT * FROM `persons`
LEFT JOIN `phones` ON `persons`.`person_id` = `phones`.`person_id`
WHERE `persons`.`person_id` = 1;
And it will return to You list of persons with phone numbers:
|person_id|person_name |person_age |phone_id |person_id |phone_number|
| 1 | Bodan Kustan| 28 | 1 | 1 | 31337 |
| 1 | Bodan Kustan| 28 | 2 | 1 | 370 |
Another possibility is Many to Many relationship (for example: Any person can love pizza, and pizza is not unique for that person), then You need third table to join tables together person_food
Persons:
|person_id|person_name |person_age |
| 1 | Bodan Kustan| 28 |
Food:
|food_id |food_name |
| 1 | meat |
| 2 | pizza |
Person_Food
|person_id |food_id |
| 1 | 2 |
Then you can execute query with Join:
SELLECT * FROM `persons`
LEFT JOIN `person_food` ON `person`.`person_id` = `person_food`.`person_id`
LEFT JOIN `food` ON `food`.`food_id` = `person_food`.`food_id`
WHERE `persons`.`person_id` = 1;
And it will return data from all tables:
|person_id|person_name |person_age |person_id |food_id |food_name |
| 1 | Bodan Kustan| 28 | 1 | 2 | pizza |
However sometimes you need to join n amount of tables to join, then You could use separate table to hold information about relation. My approach (I don't think it's the best) would be to store table name next to relation (for example split mobile phones and home phones into two separate tables):
Persons:
|person_id|person_name |person_age |
| 1 | Bodan Kustan| 28 |
Mobile_Phone:
|mobile_phone_id |mobile_phone_number |
| 1 | 31337 |
Home_Phone:
|home_phone_id |home_phone_number |
| 1 | 370 |
Person_Phone:
|person_id |related_id |related_column |related_table |
| 1 | 1 | mobile_phone_id | mobile_phone |
| 1 | 1 | home_phone_id | home_phone |
Then query middle table to get all relations:
SELECT * FROM person_phone WHERE person_id = 1
Then build dynamic query (pseudo code, not tested -- might not work):
foreach (results as result)
append_to_final_sql = "LEFT JOIN {related_table}
ON {related_table}.{related_column} = `person_phone`.`related_id`
AND `person_phone`.`related_table` = {related_table}"
final_sql = "SELECT * FROM `persons` "
+ append_to_final_sql +
" WHERE `persons`.`person_id` = 1"
So Your final SQL would be:
SELECT * FROM `persons`
LEFT JOIN `person_phone` ON `person_phone`.`person_id` = `person`.`person_id`
LEFT JOIN `mobile_phone` ON `mobile_phone`.`mobile_phone_id` = `person_phone`.`related_id` AND `person_phone`.`related_table` = 'mobile_phone'
LEFT JOIN `home_phone` ON `home_phone`.`home_phone_id` = `person_phone`.`related_id` AND `person_phone`.`related_table` = 'home_phone'
You only need Table2 if entries in Table_x can be related to multiple rows in Table1 - otherwise a simple key for Table1 will suffice.
Look into joins - very powerful, flexible and fast.
select * from Table1 left join Table2 on Table1_id = Table2_table_1_id
left join Table_X on Tablex_id = Table2_table_x_id
Look at the output and you'll see that it returns all table_x rows with copies of the Table1 and Table2 fields.

MySQL - Is it possible to sort data from one table based on the presence of data in another?

I have a table that I want to be able to sort based on the existence of data in another, related table, but I'm not sure what I want to do is possible in a single query.
For example, say I have a Products table and a Notifications table. Each table has a bunch of columns, but the important ones for this purpose is an Active column, and a foreign key in the Notifications table that references the Products table. Each row in the Products table may be referenced 0 to N times in the Notifications table.
Products Notifications
ProductID | Active NotificationID | ProductID | Active | Type
----------+------- ---------------+-----------+--------+-----
1 | 1 1 | 2 | 1 | 2
2 | 1 2 | 3 | 0 | 1
3 | 1 3 | 3 | 1 | 1
4 | 1 4 | 5 | 1 | 1
5 | 1 5 | 3 | 1 | 1
One use case I'd like to support is to sort the data from the Products table based on whether or not there is an active Notification of a particular Type (Type=1) for the Product. So in the above example, Products 3 and 5 to be collated first or last, but all five products should still be in the result set.
I haven't been able to figure out a way to manage this in a single SELECT statement. I can easily pull just the Products that do or don't have an active Notification of a certain type, but I can't figure out a way to get them all at once and sort them based on that. Is it possible or do I just need to run a couple of separate queries?
What you want is accomplished through a join and aggregation. I would suggest summarizing the notifications table as a subquery to get what you want:
select p.*
from products p left join
(select productId, count(*) as cnt
from notifications n
where active = 1
group by productid
) n
on p.productid = n.productid
order by (n.productid is not null) desc;
This structure gives you the flexibility of using the existence (as shown above), or the count, or including the count in the select list.

Select userids from mysql table if user has at least 3 fields filled

I have a mysql user table that holds user data like that:
userid | title | content
----------------------------------
1 | about | I am from ...
1 | location | Norway
1 | name | Mark
1 | website |
2 | about |
2 | location |
2 | name |
2 | website |
3 | ...
As you see the content is empty for userid 2, and also for many more users in the table.
My goal is to select only the userids that have at least 3 fields filled. All others should be ignored.
As my mysql knowledge is still weak I could not find a solution for this. I only found the opposite and just with count: Find the count of EMPTY or NULL columns in a MySQL table
What is the magic mysql query? Any help appreciated, thank you.
You would use aggregation and a having clause for this:
select u.userId
from users u
where content > '' and content is not null
group by u.userId
having count(*) >= 3;
I added the non-blank check as well as the null check. The null check is redundant, but it makes the intention clearer.

Finding shared list IDs in a MySQL table using bitwise operands

I want to find items in common from the "following_list" column in a table of users:
+----+--------------------+-------------------------------------+
| id | name | following_list |
+----+--------------------+-------------------------------------+
| 9 | User 1 | 26,6,12,10,21,24,19,16 |
| 10 | User 2 | 21,24 |
| 12 | User 3 | 9,20,21,26,30 |
| 16 | User 4 | 6,52,9,10 |
| 19 | User 5 | 9,10,6,24 |
| 21 | User 6 | 9,10,6,12 |
| 24 | User 7 | 9,10,6 |
| 46 | User 8 | 45 |
| 52 | User 9 | 10,12,16,21,19,20,18,17,23,25,24,22 |
+----+--------------------+-------------------------------------+
I was hoping to be able to sort by the number of matches for a given user id. For example, I want to match all users except #9 against #9 to see which of the IDs in the "following_list" column they have in common.
I found a way of doing this through the "SET" datatype and some bit trickery:
http://dev.mysql.com/tech-resources/articles/mysql-set-datatype.html#bits
However, I need to do this on an arbitrary list of IDs. I was hoping this could be done entirely through the database, but this is a little out of my league.
EDIT: Thanks for the help everybody. I'm still curious as to whether a bit-based approach could work, but the 3-table join works nicely.
SELECT a.following_id, COUNT( c.following_id ) AS matches
FROM following a
LEFT JOIN following b ON b.user_id = a.following_id
LEFT JOIN following c ON c.user_id = a.user_id
AND c.following_id = b.following_id
WHERE a.user_id = ?
GROUP BY a.following_id
Now I have to keep convincing myself not to prematurely optimize.
If you normalised your following_list column into a separate table with user_id and follower_id, then you'd find that COUNT() was extremely easy to use.
You'd also find the logic for selecting a list of followers, or a list of user's being followed much easier
Your problem would be simplified if you could split your following_list column off into a child table, e.g.
TABLE id_following_list:
id | following
--------------
10 | 21
10 | 24
46 | 45
...| ...
You can read more here.
Normalize the table, drop the column following_list, create a table following:
user_id
following_id
Which leads to the easy-peasy query (untested, you get the point):
SELECT b.user_id, COUNT(c.following)
FROM following a
JOIN following b -- get followings of <id>
ON b.following_id = a.following_id
AND b.user_id = a.following_id
JOIN following c -- get all (other) followings of <id> again, match with followings of b
ON b.following_id = c.following_id
AND c.user_id = a.user_id
WHERE a.user_id = <id>
GROUP BY b.user_id
ORDER BY COUNT(b.following) DESC
Performance may very well very based on indexes & size of dataset, maybe add a 'similarity' column which is updated at regular intervals or changes just for fast data retrieval.