MySQL secondary query is too performance intensive - php

I have a query where I want to pull in ID's from another table based on the ID of the item selected in another table. I'm currently doing this with an additional query based on the results that I get from the main query. It's resulting in many many additional queries. Is there a way to condense this into 1 query?
SELECT music.id,
SUM(linked_tags.weight) AS total_weight
FROM (music)
INNER JOIN linked_tags ON linked_tags.track_id = music.id
AND linked_tags.tag_id IN (7,56,59)
GROUP BY music.id
ORDER BY total_weight DESC
Then the additional query comes from running the results from the main query through a foreach loop, where 2713 is the ID of an item in the music table.
SELECT tag_id,
weight
FROM (linked_tags)
JOIN tags_en ON tags_en.id = linked_tags.tag_id
WHERE track_id = '2713'
This results in this object, where all_tags is the data that comes from the 2nd query:
[id] => 1500
[name] => Some Track Name
[total_weight] => 10
[all_tags] => Array
(
[0] => 20
[1] => 28
[2] => 4
[3] => 13
[4] => 16
[5] => 7
[6] => 42
[7] => 56
[8] => 61
)
Is there a way to pull this all into 1 query?

You can combine them directly using join:
select tag_id, weight
from (SELECT music.id,
SUM(linked_tags.weight) AS total_weight
FROM music join
linked_tags
ON linked_tags.track_id = music.id AND linked_tags.tag_id IN (7,56,59)
GROUP BY music.id
) m join
linked_tags
on m.id = linked_tags.track_id join
tags_en
ON tags_en.id = linked_tags.tag_id;
EDIT:
If I understand the query correctly, you are trying to get all tags on "tracks" (or "music") that have one or more tags in the set of (7,56,59). And, you want to get the sum of the weights of those three tags.
You can do this in one pass, if you don't mind have the tags in a comma-delimited list:
SELECT m.id,
SUM(case when lt.tag_id IN (7,56,59) then lt.weight end) AS total_weight,
sum(lt.tag_id IN (7, 56, 59)) as NumSpecialTags,
group_concat(lt.tag_id) as AllTags
FROM music m join
linked_tags lt
ON lt.track_id = m.id
GROUP BY m.id
having NumSpecialTags > 0
order by total_weight desc;
You then have to parse the AllTags list at the application layer.

Related

Issue with mysql joins

I have two tables
Meetings:
m_id ProjectName
1 Test
2 Test2
Meeting_next:
id fk_m_id Meetingdate status
1 1 9-1-2018 0
1 1 10-1-2018 0
1 1 13-1-2018 1
I want to join this two tables when I left join it I will get duplicate value
Expected output
Array
(
[0] => Array
(
[m_id] => 1
[ProjectName] => test
[meetingDate] =>13-1-2018
)
[1] => Array
(
[m_id] => 2
[ProjectName] => test2
[meetingDate] =>
)
)
I tried -
select * from meetings left join meeting_next on meetings.m_id= meeting_next.fk_m_id where meeting_next.status=1 order by m_id desc
myOutput:
Array
(
[0] => Array
(
[m_id] => 1
[ProjectName] => test
[meetingDate] =>13-1-2018
) )
Bad luck I got only first Project name. I need second too. Please help me. Any help would be appreciated.
Your WHERE condition filters the number of rows to only the row of the first project.
If you want to show both projects, even if there are no meetings with status 1, you need to move the condition to the join condition:
select *
from meetings
left join meeting_next
on meetings.m_id= meeting_next.fk_m_id
and meeting_next.status=1
order by m_id desc
Now you will get all rows from meetings with only the matching entries from meeting_next.

Connecting tables when querying in MySQL

I have 3 tables: tco_articles, tco_module_eostext and tco_articles_modules. My tco_articles has unique id key. One for each article. My tco_module_eostext has unique instance_id that belongs to each article.
My tco_articles_modules contains all article_ids, but have 9 times as much instance_ids that are used in other tables.
So I can have article_id with instance_id that when you query in the tco_module_eostext will return empty.
I'd like to make a query that will return correct body text for the correct article.
So far I have:
global $wpdb;
$posts = array();
$ids = $wpdb->get_results('SELECT DISTINCT instance_id, article_id FROM tco_articles_modules', ARRAY_A);
This returns array with all the instances and ids like:
Array
(
[0] => Array
(
[instance_id] => 928615
[article_id] => 129396
)
[1] => Array
(
[instance_id] => 928616
[article_id] => 129396
)
[2] => Array
(
[instance_id] => 928617
[article_id] => 129396
)
[3] => Array
(
[instance_id] => 928618
[article_id] => 129396
)
You can see that the article_ids are the same but instance_id. When you put
$wpdb->get_results('SELECT body FROM tco_module_eostext WHERE instance_id=928617 ', ARRAY_A);
You may get empty, but for
$wpdb->get_results('SELECT body FROM tco_module_eostext WHERE instance_id=928618 ', ARRAY_A);
You could have some body text.
This is my problem. I need to go through all of them and filter out the not empty ones and assign them correct article. I managed to output the articles
foreach ($ids as $key => $value) {
$instance_ID = $value['instance_id'];
$article_ID = $value['article_id'];
$article_out = $wpdb->get_results('SELECT * FROM tco_articles WHERE id='.$article_ID.' ', ARRAY_A);
$posts[$article_ID] = $article_out[0];
}
Which returns something like:
Array
(
[129396] => Array
(
[id] => 129396
[headline] => Bla bla bla title
[intro] => This is cool article intro
[needs_review] => 0
[published] => 2014-12-16 09:17:00
[unpublished] =>
[hidden] => 0
[no_single_page] => 0
[permalink] => link-perma-link-here
[internal_slug] =>
[type_id] => 3
[thread_id] => 0
[news_id] => 0
[header_game_id] => 0
[puff_hh_id] => 0
[puff_title] =>
[hdrcol_id] => 900
[review_queued] =>
[lock_timeout] => 0
[created] => 2014-12-16 09:17:00
[updated] => 2015-01-16 13:51:30
[created_by] => 84142
[updated_by] => 84142
)
...
Now I'd like to append the body text from the tco_module_eostext table.
Is there a query I can use to do this automatically or to do this one at the time and then append to the $posts array?
The foreach method of querying is kinda slow when you have 180000+ posts.
Any help is appreciated.
If you are sure that there is always only one row in tco_module_eostext against each article_id, you can use JOIN (inner join), which will only show one row for each article_id.
SELECT a.*, t.body
FROM tco_articles a
JOIN tco_articles_modules m ON m.article_id = a.id
JOIN tco_module_eostext t ON m.instance_id = t.instance_id
//WHERE .....
But, this will not show any row of some articles if there is no entry in other two tables for that article_id. But there is still way to solve this. We can use LEFT OUTER JOIN and then make sure we only make the join if there is any row in tco_module_eostext for any instace_id. This will make sure you get at least the article info from tco_articles table when there is no data in other tables.
SELECT a.*, t.body
FROM tco_articles a
LEFT OUTER JOIN tco_articles_modules m ON m.article_id = a.id AND EXISTS ( SELECT NULL FROM tco_module_eostext WHERE instance_id = m.instance_id )
LEFT OUTER JOIN tco_module_eostext t ON m.instance_id = t.instance_id
//WHERE .....
Why not use a query with join?
Not tested!:
SELECT
article.*,
text.body as bodytext
FROM
tco_articles_modules AS modules LEFT OUTER JOIN
tco_module_eostext AS text ON
modules.instance_id = text.instance_id LEFT OUTER JOIN
tco_articles AS article ON
article.id = modules.article_id
it should get all articles with the assigned article_id from tco_articles_modules
Have a look at OUTER Join - you may want to replace this with an INNER JOIN for faster Queries. Also you ma want an WHERE condition for filtering in the query. Watch also out for the right indexing in mysql table - each joining column should be indexed - this will get much more faster results.

Using LEFT OUTER JOIN to build query results

There are three tables like this:
store_stock:
ID ItemWeaveType ItemModel Cost Price
7 3 4 10.00 15.00
store_item_weaves:
ID WeaveID
3 MC
store_item_models:
ID ModelID
4 HV
I am trying to do a query to gather all of the data for item with the stock ID of 7. As a finished result, I would like an array like:
Array ( [ID] => 7 [ItemWeaveType] => MC [ItemModel] => HV [Cost] => 10.00 [Price] => 15.00)
So, I need to join the data from the tables store_item_weaves and store_item_models.
Here is what I have so far:
$query = $db->query("SELECT * FROM `store_stock` s
left outer join `store_item_weaves` w on w.`ID`=s.`ItemWeaveType`
left outer join `store_item_models` m on m.`ID`=s.`ItemModel`
where s.`ID`=7");
This returns an array like:
Array ( [ID] => 7 [ItemWeaveType] => 3 [ItemModel] => 4 [Cost] => 10.00 [Price] => 15.00 [WeaveID] => MC [ModelID] => HV )
So, I'm almost there. Instead of using the values of WeaveID and ModelID for ItemWeaveType and ItemModel, it is adding it onto the array.
Any ideas?
Use a columns list instead of just * to make sure you get the values you want:
$query = $db->query("SELECT s.ID, w.WeaveId, m.ModelId, s.Cost, s.Price FROM `store_stock` s
left outer join `store_item_weaves` w on w.`ID`=s.`ItemWeaveType`
left outer join `store_item_models` m on m.`ID`=s.`ItemModel`
where s.`ID`=7");

Query with 2 selected columns returns only 1 column per row

I am running the following query.
I expect to get both user.name and activities.name for every row in my result, but I only seem to get activities.name.
Why am I not seeing the names of each user that did the actual activity?
SELECT user.name, activities.name
FROM user_activities
LEFT JOIN user ON user.userid = user_activities.user_id
LEFT JOIN activities ON activities.activityid = user_activities.activity_id
Ouput:
stdClass Object
(
[0] => Array
(
[name] => Bought a House.
)
[1] => Array
(
[name] => Purchased a game.
)
[2] => Array
(
[name] => Purchased a game.
)
)
Since they share the column name, try to add an alias for that column:
SELECT user.name AS user_name , activities.name AS activity_name
If I'm not wrong to understand your question you might need to use alias to identify names of a user that'll result into non-ambiguous fields. Just Update your query like as below it'll result as you desired
SELECT u.name as user_name, a.name as activites_name FROM user_activities ua LEFT JOIN user u ON
u.userid = ua.user_id LEFT JOIN activities a ON a.activityid
= ua.activity_id

Select filtered sums with joins MySQL and PHP

I am developing a bets system and basically there is a set of matches (or games). The matches always has 2 teams on which the users can bet.
I have the following DB structure:
matches:
teams:
bets:
What I am trying to do with my query is bring from the database the matches and the bets's amounts sum, related to each team, in each match and list all the matches with all the information related to it.
The far I was:
$query="SELECT
a.*,
SUM(b.amount) AS sumA,
SUM(c.amount) AS sumB,
d.name AS teamNameA,
e.name AS teamNameB
FROM matches AS a
LEFT JOIN bets AS b ON(a.teamA = b.team_id AND a.id = b.match_id)
LEFT JOIN bets AS c ON(a.teamB = c.team_id AND a.id = c.match_id)
LEFT JOIN teams AS d ON(a.teamA = d.id)
LEFT JOIN teams AS e ON(a.teamB = e.id) GROUP BY id"
$result = mysql_query($query);
$resultArray = array();
while($row = mysql_fetch_assoc($result)){
$resultArray[] = $row;
}
print_r($resultArray);
exit();
What is printed:
Array
(
[0] => Array
(
[id] => 1
[teamA] => 1
[teamB] => 2
[teamNameA] => "Team 1"
[teamNameB] => "Team 2"
[sumA] => 400
[sumB] => 200
)
[1] => Array
(
[id] => 1
[teamA] => 1
[teamB] => 2
[teamNameA] => "Team 1"
[teamNameB] => "Team 2"
[sumA] =>
[sumB] =>
)
[2] => Array
(
[id] => 1
[teamA] => 1
[teamB] => 2
[teamNameA] => "Team 1"
[teamNameB] => "Team 2"
[sumA] =>
[sumB] =>
)
)
It is almost perfect, but the amounts in "sumA" and "sumB" itens of the first row is wrong. "sumA" should be equal 200 and "sumB" should be equal 100. Of course it is duplicating the sum process. I tried to use the "DISTINCT" statement within the SUM's but them, it eliminates the amount's equal values and I get 100 for "sumA" and 50 for "sumB", what still wrong as well.
I created a solution for you that includes sum(amount), ID of the team, and the team Name.
SELECT SUM(amount), bets.id, teams.name from bets
INNER JOIN teams ON bets.team_id = teams.id GROUP BY teams.id;
It is not the same as what PHP printed, but I find it more useful.
You might as well look at this SQLFiddle I wrote.
Good luck.
http://sqlfiddle.com/#!2/7ffc6/17
try this sql
SELECT A.id as match_id, A.teamA as teamA, A.teamB as teamB,
(SELECT B.name FROM teams B WHERE B.id=A.teamA LIMIT 1) AS teamNameA,
(SELECT C.name FROM teams C WHERE C.id=A.teamB LIMIT 1) AS teamNameB,
(SELECT sum(D.amount) FROM bets D WHERE D.match_id=A.id AND D.team_id=A.teamA) as sumA ,
(SELECT sum(E.amount) FROM bets E WHERE E.match_id=A.id AND E.team_id=A.teamB) as sumB
FROM `matches` A
this is too lengthy and get some time to execute but you will get output according to your choice
Using the hint of #SKRocks, I finally found the correct solution, substituting the bets table's LEFT JOINs for subqueries as following:
$query="SELECT
a.*,
(SELECT sum(D.ammount) FROM bets AS D WHERE D.match_id=a.id AND D.team_id=a.teamA) AS sumA,
(SELECT sum(E.ammount) FROM bets AS E WHERE E.match_id=a.id AND E.team_id=a.teamB) AS sumB,
d.name AS teamNameA,
e.name AS teamNameB
FROM matches AS a
LEFT JOIN teams AS d ON(a.teamA = d.id)
LEFT JOIN teams AS e ON(a.teamB = e.id) GROUP BY id";
Now I get the correct results and maintained the LEFT JOINS for performance. I am choosing this answer as the final solution, because it is what I am actually using. But you gave me the right hint to build this solution.

Categories