SQL Query Optimization help - php

my foundation on SQL is pretty weak so I hope you could bear with me. I have three tables: contents, categories, and categorization. The setup was chosen since some content will belong to one or more categories.
I want to fetch contents and its corresponding categories.
This is an overly-simplified version of the current script, without error-checking routines:
$q = "SELECT * FROM contents WHERE contents.foo = 'bar'"
$resource = mysql_query($q);
$categoryFilter = array();
$q2 = "SELECT * FROM categorization WHERE ";
while($content = mysql_fetch_assoc($resource))
{
$categoryFilter[] = "content_id='" . $content["id"] . "'";
}
if(count($categoryFilter))
{
$q2 .= implode(" OR ", $categoryFilter);
mysql_query($q2);
}
That's the gist of it. I hope you get what I am trying to do. I don't know if I can actually use JOINS the content_id may be present in multiple rows in categorization. So what I did was to simply append multiple OR's, trying to fetch items one by one. I really would not like to use multiple queries in this scenario. I hope anyone could suggest an approach
Thanks for your time

One query should be enough to fetch data from all three tables:
SELECT categories.category_id #, other fields
FROM contents
INNER JOIN categorization ON contents.content_id = categorization.content_id
INNER JOIN categories ON categorization.category_id = categories.category_id
WHERE contents.content_id = 1 # AND other filters
Tweak the columns in the SELECT clause and/or conditions in WHERE clause according to your needs.

This should do the same thing as in your example:
$q = "
SELECT *
FROM
contents c
categorization ctg ON ctg.content_id = c.id
WHERE c.foo = 'bar'
";
$result = mysql_query($q);

If I understand it correctly, you can do this in one sql statement
SELECT *
FROM contents t1
JOIN categorization t2
WHERE t1.content_id = t2.content_id AND t1.foo = 'bar'
Also ensure that content_id is indexed both in 'content' and 'categorization'. You may find it worthwhile indexing 'foo' aswell, but it depends on how you are actually searching.

Related

Left join MySql/PHP

Whilst populating a table based on ids and labels from different tables, it appeared apparent there must potentially be a better way of achieving the same result with less code and a more direct approach using LEFT JOIN but i am puzzled after trying to work out if its actually capable of achieving the desired result.
Am i correct in thinking a LEFT JOIN is usable in this instance?
Referencing two tables against one another where one lists id's related to another table and that other table has the titles allocated for each reference?
I know full well that if theres independent information for each row LEFT JOIN is suitable, but where theres in this case only several ids to reference for many rows, i just am not clicking with how i could get it to work...
The current way i am achieving my desired result in PHP/MySQL
$itemid = $row['item_id'];
$secid = mysql_query(" SELECT * FROM item_groups WHERE item_id='$itemid' ");
while ($secidrow = mysql_fetch_assoc($secid)) {
//echo $secidrow["section_id"]; //testing
$id = $secidrow["section_id"];
$secnameget = mysql_query(" SELECT * FROM items_section_list WHERE item_sec_id='$id' ");
while ($secname = mysql_fetch_assoc($secnameget)) {
echo $secname["section_name"];
}
}
Example of the data
Item groups
:drink
:food
:shelf
Item List
itemId, groupId
Group List
groupId, groupTitle
The idea so outputting data to a table instead of outputting "Item & Id Number, in place of the ID Number the title actually appears.
I have achieved the desired result but i am always interested in seeking better ways to achieve the desired result.
If I've deciphered your code properly, you should be able to use the following query to get both values at the same time.
$itemid = $row['item_id'];
$secid = mysql_query("
SELECT *
FROM item_groups
LEFT JOIN items_section_list
ON items_section_list.item_sec_id = item_groups.section_id
WHERE item_id='$itemid'
");
while ($secidrow = mysql_fetch_assoc($secid)) {
//$id = $secidrow["section_id"];
echo $secidrow["section_name"];
}

Mysql Query with two tables php

I was wondering how to do a query with two tables in php?
I have this single query
?php
$sQuery = "Select * From tb_columnas Where col_Status='activo' Order by col_ID DESC";
$result = mysql_query($sQuery, $cnxMySQL) or die(mysql_error());
$rows_result = mysql_fetch_assoc($result);
$total_rows_result = mysql_num_rows($result);
if ($total_rows_result > 0){
do {
$id_columnas = $rows_result ['col_ID'];
$col_Titulo = $rows_result ['col_Titulo'];
$col_Resumen = $rows_result ['col_Resumen'];
$col_Fecha = $rows_result ['col_Fecha'];
$col_Autor = $rows_result ['col_Autor'];
?>
But I'd like to compare the col_Autor with au_Nombre which is in another table (tb_autores) and get au_Photo and other values from it, how can I do that?
You can do a simple join query without using the JOIN keyword by specifying the two tables in the FROM clause and establishing a relationship in the where clause.
For example
SELECT columns
FROM table1, table2
WHERE table1.field = table2.field
You are asking about SQL Joins, the practicing of putting two or more tables together in an SQL statement to return data from more than 1 table. You join the tables on a common column, such as author.authorid = book.authorid. I suggest looking up JOINS on google, there are many good articles.
A great article on it: http://www.sitepoint.com/understanding-sql-joins-mysql-database/
It sounds like you are looking for a join. Try something like the following:
SELECT * FROM tb_columnas JOIN tb_autores ON tb_columnas = col_Autor WHERE col_Status='activo' ORDER BY col_ID DESC
You need to understand joins for this.
Here you will find very good explanation of the same:
http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html

What is the query statement to write in order to solve the followin database problem?

I have the following 3 tables in the database.
Programs_Table
Program_ID (Primary Key)
Start_Date
End_Date
IsCompleted
IsGoalsMet
Program_type_ID
Programs_Type_Table(different types of programs, supports a dropdown list in the form)
Program_type_ID (Primary Key)
Program_name
Program_description
Client_Program_Table
Client_ID (primary key)
Program_ID (primary key)
What is the best way to find out how many clients are in a specific program (program type)?
Would the following SQL statement be the best way, or even plausible?
SELECT Client_ID FROM Client_Program_Table
INNER JOIN Programs_Table
ON Client_Program_Table.Program_ID = Programs_Table.Program_ID
WHERE Programs_Table.Program_type_ID = "x"
where "x" is the Program_type_ID of the specific program we're interested in.
OR is the following a better way?
$result = mysql_query("SELECT Program_ID FROM Programs_Table
WHERE Program_type_ID = 'x'");
$row = mysql_fetch_assoc($result);
$ProgramID = $row['Program_ID'];
$result = mysql_query("SELECT * FROM Client_Program_Table
WHERE Program_ID = '$ProgramID'");
mysql_num_rows($result) // returns how many rows of clients we pulled.
Thank you in advance, please excuse my inexperience and any mistakes that I've made.
Here is how you can do it:
<?php
// always initialize a variable
$number_of_clients = 0;
// escape the string which will go in an SQL query
// to protect yourself from SQL injection
$program_type_id = mysql_real_escape_string('x');
// build a query, which will count how many clients
// belong to that program and put the value on the temporary colum "num_clients"
$query = "SELECT COUNT(*) `num_clients` FROM `Client_Program_Table` `cpt`
INNER JOIN `Programs_Table` `pt`
ON `cpt`.`Program_ID` = `pt`.`Program_ID`
AND `pt`.`Program_type_ID` = '$program_type_id'";
// execute the query
$result = mysql_query($query);
// check if the query executed correctly
// and returned at least a record
if(is_resource($result) && mysql_num_rows($result) > 0){
// turn the query result into an associative array
$row = mysql_fetch_assoc($result);
// get the value of the "num_clients" temporary created column
// and typecast it to an intiger so you can always be safe to use it later on
$number_of_clients = (int) $row['num_clients'];
} else{
// query did not return a record, so we have no clients on that program
$number_of_clients = 0;
}
?>
If you want to know how many clients are involved in a program, you'd rather want to use COUNT( * ). MySQL (with MyISAM) and SQL Server have a fast way to retrieve the total number of lines. Using a SELECT(*), then mysql_num_rows leads to unnecessary memory ressources and computing time. To me, this is the fastest, though not the "cleanest" way to write the query you want:
SELECT
COUNT(*)
FROM
Client_Program_Table
WHERE
Program_ID IN
(
SELECT
Program_ID
FROM
Programs_Table
WHERE
Program_type_ID = 'azerty'
)
Why is that?
Using JOIN make queries more readable, but subqueries often prove to be computed faster.
This returns a count of the clients in a specific program type (x):
SELECT COUNT(cpt.Client_ID), cpt.Program_ID
FROM Client_Program_Table cpt
INNER JOIN Programs_Table pt ON cpt.Program_ID=pt.Program_ID
WHERE pt.Program_type_ID = "x"
GROUP BY cpt.Program_ID

How to select how many columns have the same value from mysql

I have query that selects goes something like this:
$query = "SELECT members.*
FROM members
JOIN $nombre
ON members.member_ID=$nombre.friends";
$result = mysql_query($query) or die(mysql_error());
$number = mysql_num_rows($result);
$i = 0;
while($msg = mysql_fetch_assoc($result))
{
//store data in arrays
$peer_id[$i] = $msg['member_ID'];
$peer_state[$i] = $msg['home_state'];
$pro_political_views[$i] = $msg['political_views'];
$peer_district[$i] = $msg['district'];
$peer_first[$i] = $msg['first_name'];
$peer_last[$i] = $msg['last_name'];
$peer_issue[$i] = $msg['first_issue'];
$peer_second[$i] = $msg['second_issue'];
$peer_third[$i] = $msg['third_issue'];
$peer_stand[$i] = $msg['iStand'];
$peer_mail[$i] = $msg['email'];
$peer_pic[$i] = $msg['photo'];
++$i;
}
What this essentially does is get all the values from columns in the rows in the members from the members table where the member_ID is present in the $nombre table.
In the members table, there are two columns called "state" and "district". I want to make it so that php could tell me how many different values there are in the state and district columns for this query.
So, how could I go about writing a query or use php to tell how many how many peers are from a given state. I don't want to have to query the db once for each of the fifty states because that would take way too much time for the page to load. So, is there an efficient way to do this?
Thanks
...to tell how many how many peers are
from a given state
For states:
SELECT state, COUNT(m.member_ID) cnt
FROM members m
JOIN $nombre n ON m.member_ID=n.friends
GROUP BY state
and for districts:
SELECT 'district', COUNT(m.member_ID) cnt
FROM members m
JOIN $nombre n ON m.member_ID=n.friends
GROUP BY 'district'
This queries returns distinct values for states and districts and count of peers from them
You could use two queries, like so:
SELECT DISTINCT members.state
FROM members
JOIN $nombre
ON members.member_ID=$nombre.friends
and
SELECT DISTINCT members.district
FROM members
JOIN $nombre
ON members.member_ID=$nombre.friends
This will give you all of the unique states and districts back, which you can use or count or whatever.
Alternatively, add this somewhere in your while loop:
$all_states[$msg['state']] = 1;
$all_districts[$msg['district']] = 1;
Then, after the loop you can:
$state_count = count($all_states);
$district_count = count($all_districts);
The query approach would be faster by itself (assuming an index on the state and district columns), but the latter approach will be faster if you are already executing this loop anyway.
EDIT: To count unique districts in each state, replace
$all_districts[$msg['district']] = 1;
with something like
$all_districts[$msg['state'] . '--' . $msg['district']] = 1;
This will include the state in the array key too, and the count() call later will return all of the unique district-state combinations.
you can save the district in additional array like
$district[] = $msg['district'];
after the loop print_r using a function array_count_values
print_r(array_count_values($district));

Enumerating tables used in mysql query?

Is there any way to enumerate tables used in mysql query?
Lets say I have query :
SELECT * FROM db_people.people_facts pf
INNER JOIN db_system.connections sm ON sm.source_id = pf.object_id
INNER JOIN db_people.people p ON sm.target_id = p.object_id
ORDER BY pf.object_id DESC
And I want in return array:
$tables = array(
[0] => 'db_people.people_facts',
[1] => 'db_system.connections',
[2] => 'db_people.people',
);
Yes, you can get information about tables and columns that are part of a query result. This is called result set metadata.
The only PHP solution for MySQL result set metadata is to use the MySQLi extension and the mysqli_stmt::result_metadata() function.
$stmt = $mysqli->prepare("SELECT * FROM db_people.people_facts pf
INNER JOIN db_system.connections sm ON sm.source_id = pf.object_id
INNER JOIN db_people.people p ON sm.target_id = p.object_id
ORDER BY pf.object_id DESC");
$meta = $stmt->result_metadata();
$field1 = $meta->fetch_field();
echo "Table for field " . $field1->name . " is " . $field1->table . "\n";
You'll have to build the array of distinct tables used in the query yourself, by looping over the fields.
Depending on what you're using it for, MySQL's EXPLAIN could do the trick for you:
http://dev.mysql.com/doc/refman/5.0/en/explain.html
The solution marked as good will return only the result tables. But if you do the next query it will fail:
SELECT users.* FROM users, cats, dogs WHERE users.id = cats.user_id
Will return only users and not cats and dogs tables.
The best solution is find a good parser, another solution is using REGEX and EXPLAIN query (more info in the next link):
Get mysql tables in a query
But I think that another good solution is list all tables and search them inside the query, you can cache the list of tables.
EDIT: When searching for tables, better use a preg like:
// (`|'|"| )table_name(\1|$)
if(preg_match('/(`|\'|"| )table_name(\1|$)/i', $query))
// found
If not, it can return false positives with for example "table_name2", "table_name3"... table_name will return FOUND two times.

Categories