I wrote a product price/stock update script for Magento. I load the csv into an array and then iterate through it. The current code takes around 10 minutes to complete for 5,000 products, is there a faster way to do this? I've already bypassed Magento's API as that was extremely slow and switched to updating the database directly since its not many tables and its faster. Using timers to record the time, it takes about 10 minutes for the foreach loop and two minutes for the reindexALL
$con = mysql_connect("localhost","root","");
$selected = mysql_select_db("magento",$con);
$processes = Mage::getSingleton('index/indexer')->getProcessesCollection();
$processes->walk('setMode', array(Mage_Index_Model_Process::MODE_MANUAL));
$processes->walk('save');
foreach($all_rows as $final)
{
$sql = mysql_query("SELECT entity_id from catalog_product_entity where sku = '".$final[ITEM]."'");
if ($row = mysql_fetch_array($sql)) {
//update price
$pricenew = $final['PRICE'] + ($final['PRICE']*.30);
mysql_query("UPDATE catalog_product_entity_decimal SET value = '$pricenew' where attribute_id = 75 AND entity_id = '".$row[entity_id]."' ");
//update retail price
$retailprice = $final['RETAIL'];
mysql_query("UPDATE catalog_product_entity_decimal SET value = '$retailprice' where attribute_id = 120 AND entity_id = '".$row[entity_id]."' ");
//update stock quantity and is in stock
$stockquantity = $final['QTY'];
$stockquantity = number_format($stockquantity, 4, '.', '');
mysql_query("UPDATE cataloginventory_stock_item SET qty = '$stockquantity', SET is_in_stock = 1 where product_id = '".$row[entity_id]."' ");
}
$processes->walk('reindexAll');
$processes->walk('setMode', array(Mage_Index_Model_Process::MODE_REAL_TIME));
$processes->walk('save');
mysql_close($con);
If your table catalog_product_entity_decimal has index, that covers id (obviously it is) - then you have no other ways to speed it up. Since the slowest thing here is physical changing of the value.
Probably you can put a WHERE clause to to avoid of updating the price to the same value.
Other thoughts:
While most people look at performance optimizations for SELECT statements, UPDATE and DELETE statements are often overlooked. These can benefit from the principles of analyzing the Query Execution Plan (QEP). You can only run an EXPLAIN on a SELECT statement, however it’s possible to rewrite an UPDATE or DELETE statement to perform like a SELECT statement.
To optimize an UPDATE, look at the WHERE clause. If you are using the PRIMARY KEY, no further analysis is necessary. If you are not, it is of benefit to rewrite your UPDATE statement as a SELECT statement and obtain a QEP as previously detailed to ensure optimal indexes are used. For example:
UPDATE t
SET c1 = ‘x’, c2 = ‘y’, c3 = 100
WHERE c1 = ‘x’
AND d = CURDATE()
You can rewrite this UPDATE statement as a SELECT statement for using EXPLAIN:
EXPLAIN SELECT c1, c2, c3 FROM t WHERE c1 = ‘x’ AND d = CURDATE()
You should now apply the same principles as you would when optimizing SELECT statements.
Related
My problem is explained below.
This is my PHP code running on my server right now :
$limit = 10000;
$annee = '2017';
//Counting the lines I need to delete
$sql = " SELECT COUNT(*) FROM historisation.cdr_".$annee." a
INNER JOIN transatel.cdr_transatel_v2 b ON a.id_cdr = b.id_cdr ";
$t = $db_transatel->selectAll($sql);
//The number of lines I have to delete
$i = $t[0][0];
do {
if ($i < $limit) {
$limit = $i;
}
//The problem is comming from that delete
$selectFromHistoryAndDelete = " DELETE FROM transatel.cdr_transatel_v2
WHERE id_cdr IN (
SELECT a.id_cdr FROM historisation.cdr_".$annee." a
INNER JOIN (SELECT id_cdr FROM historisation.cdr_transatel_v2) b ON a.id_cdr = b.id_cdr
)
LIMIT " . $limit;
$delete = $db_transatel->exec($selectFromHistoryAndDelete, $params);
$i = $i - $limit;
} while ($i > 0);
The execution of the query.
As you can see on the picture, in the first 195 loops the execution time was between 13 and 17 seconds.
It increased to 73 seconds on the 195th loop and to 1305 seconds on the 196th loop.
Now the query is running for 2000 seconds.
The query is deleting rows in a test table that no one is using right.
I'm deleting row 10,000 by 10,000 for the query to be quick and not overload the server.
I am wondering why is the execution time increasing like that, I though it will be quicker at the end because I though the inner join would be much quicker as they are less rows in the table.
Does anyone has an idea ?
Edit : The tables engine is MyISAM.
Based on your latest comment the inner join is redundant, since you're deleting from the table that contains the values you're joining on. In essence you're having to process b.id_cdr = a.id_cdr twice, since the number of values compared on cdr_2017 are not changed by the inner join, just the number of values queried to be deleted.
As for the cause of the incremental slowness, it is because you are manually performing the same function as SELECT cdr_id FROM cdr_2017 LIMIT 10000 OFFSET x.
That is to say, your query has to perform a full-table scan on cdr_2017 to determine the id values to delete. As you delete the values, the SQL optimizer has to move further through the cdr_2017 table to retrieve the values.
Resulting in
DELETE FROM IN(1,2,3,...10000)
DELETE FROM IN(1,2,3,...20000)
...
DELETE FROM IN(1,2,3,...1000000)
Assuming cdr_id is the incremental primary key, to resolve the issue you could use the last index retrieved from cdr_2017 to filter the selected values.
This will be much faster, as a full-table scan is no longer required to validate the joined records, since you're now utilizing an indexed value on both sides of the query.
$sql = " SELECT COUNT(a.cdr_id) FROM historisation.cdr_".$annee." a
INNER JOIN transatel.cdr_transatel_v2 b ON a.id_cdr = b.id_cdr ";
$t = $db_transatel->selectAll($sql);
//The number of lines I have to delete
$i = $t[0][0];
//set starting index
$previous = 0;
do {
if ($i < $limit) {
$limit = $i;
}
$selectFromHistoryAndDelete = 'DELETE d
FROM transatel.cdr_transatel_v2 AS d
JOIN (
SELECT #previous := cdr_id AS cdr_id
FROM historisation.cdr_2017
WHERE cdr_id > ' . $previous . '
ORDER BY cdr_id
LIMIT 10000
) AS a
ON a.cdr_id = d.cdr_id';
$db_transatel->exec($selectFromHistoryAndDelete, $params);
//retrieve last id selected in cdr_2017 to use in next iteration
$v = $db_transatel->selectAll('SELECT #previous'); //prefer fetchColumn
$previous = $v[0][0];
$i = $i - $limit;
} while ($i > 0);
//optionally reclaim table-space
$db_transatel->exec('OPTIMIZE TABLE transatel.cdr_transatel_v2', $params);
You could also refactor to use cdr_id > $previous AND cdr_id < $last to remove the order by limit clauses, which should also improve performance.
Though I would like to note, that a table lock on cdr_transatel_v2 is performed during this operation by the MyISAM database engine. Due to the way MySQL handles concurrent sessions and queries, there is not much gain from a batch delete in this manner, and is really only applicable to InnoDB and transactions. Especially when using PHP with FastCGI, as opposed to Apache mod_php. Since other queries not on cdr_transatel_v2 will still be executed and write operations on cdr_transatel_v2 will still be queued. If using mod_php I would reduce the limit to 1,000 records to reduce queue times.
For more information see https://dev.mysql.com/doc/refman/5.7/en/internal-locking.html#internal-table-level-locking
Alternative approach.
Considering the large number of records that need to be deleted, when the records deleted exceed those that are kept, it would be more beneficial to invert the operation by using INSERT instead of DELETE.
#ensure the storage table doesn't exist already
DROP TABLE IF EXISTS cdr_transatel_temp;
#duplicate the structure of the original table
CREATE TABLE transatel.cdr_transatel_temp
LIKE transatel.cdr_transatel_v2;
#copy the records that are not to be deleted from the original table
INSERT transatel.cdr_transatel_temp
SELECT *
FROM transatel.cdr_transatel_v2 AS d
LEFT JOIN historisation.cdr_2017 AS b
ON b.cdr_id = d.cdr_id
WHERE b.cdr_id IS NULL;
#replace the original table with the storage table
RENAME TABLE transatel.cdr_transatel_v2 to transatel.backup,
transatel.cdr_transatel_temp to cdr_transatel_v2;
#remove the original table
DROP TABLE transatel.backup;
I need to synchronize specific information between two databases (one mysql, the other a remote hosted SQL Server database) for thousands of rows. When I execute this php file it gets stuck/timeouts after several minutes I guess, so I wonder how I can fix this issue and maybe also optimize the way of "synchronizing" it.
What the code needs to do:
Basically I want to get for every row (= one account) in my database which gets updated - two specific pieces of information (= 2 SELECT queries) from another SQL Server database. Therefore I use a foreach loop which creates 2 SQL queries for each row and afterwards I update those information into 2 columns of this row. We talk about ~10k Rows which needs to run thru this foreach loop.
My idea which may help?
I have heard about things like PDO Transactions which should collect all those queries and sending them afterwards in a package of all SELECT queries, but I have no idea whether I use them correctly or whether they even help in such cases.
This is my current code, which is timing out after few minutes:
// DBH => MSSQL DB | DB => MySQL DB
$dbh->beginTransaction();
// Get all referral IDs which needs to be updated:
$listAccounts = "SELECT * FROM Gifting WHERE refsCompleted <= 100 ORDER BY idGifting ASC";
$ps_listAccounts = $db->prepare($listAccounts);
$ps_listAccounts->execute();
foreach($ps_listAccounts as $row) {
$refid=$row['refId'];
// Refsinserted
$refsInserted = "SELECT count(username) as done FROM accounts WHERE referral='$refid'";
$ps_refsInserted = $dbh->prepare($refsInserted);
$ps_refsInserted->execute();
$row = $ps_refsInserted->fetch();
$refsInserted = $row['done'];
// Refscompleted
$refsCompleted = "SELECT count(username) as done FROM accounts WHERE referral='$refid' AND finished=1";
$ps_refsCompleted = $dbh->prepare($refsCompleted);
$ps_refsCompleted->execute();
$row2 = $ps_refsCompleted->fetch();
$refsCompleted = $row2['done'];
// Update fields for local order db
$updateGifting = "UPDATE Gifting SET refsInserted = :refsInserted, refsCompleted = :refsCompleted WHERE refId = :refId";
$ps_updateGifting = $db->prepare($updateGifting);
$ps_updateGifting->bindParam(':refsInserted', $refsInserted);
$ps_updateGifting->bindParam(':refsCompleted', $refsCompleted);
$ps_updateGifting->bindParam(':refId', $refid);
$ps_updateGifting->execute();
echo "$refid: $refsInserted Refs inserted / $refsCompleted Refs completed<br>";
}
$dbh->commit();
You can do all of that in one query with a correlated sub-query:
UPDATE Gifting
SET
refsInserted=(SELECT COUNT(USERNAME)
FROM accounts
WHERE referral=Gifting.refId),
refsCompleted=(SELECT COUNT(USERNAME)
FROM accounts
WHERE referral=Gifting.refId
AND finished=1)
A correlated sub-query is essentially using a sub-query (query within a query) that references the parent query. So notice that in each of the sub-queries I am referencing the Gifting.refId column in the where clause of each sub-query. While this isn't the best for performance because each of those sub-queries still has to run independent of the other queries, it would perform much better (and likely as good as you are going to get) than what you have there.
Edit:
And just for reference. I don't know if a transaction will help here at all. Typically they are used when you have several queries that depend on each other and to give you a way to rollback if one fails. For example, banking transactions. You don't want the balance to deduct some amount until a purchase has been inserted. And if the purchase fails inserting for some reason, you want to rollback the change to the balance. So when inserting a purchase, you start a transaction, run the update balance query and the insert purchase query and only if both go in correctly and have been validated do you commit to save.
Edit2:
If I were doing this, without doing an export/import this is what I would do. This makes a few assumptions though. First is that you are using a mssql 2008 or newer and second is that the referral id is always a number. I'm also using a temp table that I insert numbers into because you can insert multiple rows easily with a single query and then run a single update query to update the gifting table. This temp table follows the structure CREATE TABLE tempTable (refId int, done int, total int).
//get list of referral accounts
//if you are using one column, only query for one column
$listAccounts = "SELECT DISTINCT refId FROM Gifting WHERE refsCompleted <= 100 ORDER BY idGifting ASC";
$ps_listAccounts = $db->prepare($listAccounts);
$ps_listAccounts->execute();
//loop over and get list of refIds from above.
$refIds = array();
foreach($ps_listAccounts as $row){
$refIds[] = $row['refId'];
}
if(count($refIds) > 0){
//implode into string for use in query below
$refIds = implode(',',$refIds);
//select out total count
$totalCount = "SELECT referral, COUNT(username) AS cnt FROM accounts WHERE referral IN ($refIds) GROUP BY referral";
$ps_totalCounts = $dbh->prepare($totalCount);
$ps_totalCounts->execute();
//add to array of counts
$counts = array();
//loop over total counts
foreach($ps_totalCounts as $row){
//if referral id not found, add it
if(!isset($counts[$row['referral']])){
$counts[$row['referral']] = array('total'=>0,'done'=>0);
}
//add to count
$counts[$row['referral']]['total'] += $row['cnt'];
}
$doneCount = "SELECT referral, COUNT(username) AS cnt FROM accounts WHERE finished=1 AND referral IN ($refIds) GROUP BY referral";
$ps_doneCounts = $dbh->prepare($doneCount);
$ps_doneCounts->execute();
//loop over total counts
foreach($ps_totalCounts as $row){
//if referral id not found, add it
if(!isset($counts[$row['referral']])){
$counts[$row['referral']] = array('total'=>0,'done'=>0);
}
//add to count
$counts[$row['referral']]['done'] += $row['cnt'];
}
//now loop over counts and generate insert queries to a temp table.
//I suggest using a temp table because you can insert multiple rows
//in one query and then the update is one query.
$sqlInsertList = array();
foreach($count as $refId=>$count){
$sqlInsertList[] = "({$refId}, {$count['done']}, {$count['total']})";
}
//clear out the temp table first so we are only inserting new rows
$truncSql = "TRUNCATE TABLE tempTable";
$ps_trunc = $db->prepare($truncSql);
$ps_trunc->execute();
//make insert sql with multiple insert rows
$insertSql = "INSERT INTO tempTable (refId, done, total) VALUES ".implode(',',$sqlInsertList);
//prepare sql for insert into mssql
$ps_insert = $db->prepare($insertSql);
$ps_insert->execute();
//sql to update existing rows
$updateSql = "UPDATE Gifting
SET refsInserted=(SELECT total FROM tempTable WHERE refId=Gifting.refId),
refsCompleted=(SELECT done FROM tempTable WHERE refId=Gifting.refId)
WHERE refId IN (SELECT refId FROM tempTable)
AND refsCompleted <= 100";
$ps_update = $db->prepare($updateSql);
$ps_update->execute();
} else {
echo "There were no reference ids found from \$dbh";
}
The following code runs incredibly slowly when performing a WHILE LOOP using data from table product and updating another table stock_figures within the same database.
The code loops through each row in product taking the value from product_id and wholesale_price and then performs some calculations on the product table before updating the stock_figures table with the values.
I'd be grateful of any suggestions which would improve the performance of my queries.
PHP WHILE LOOP
<?
// Retrieve data from database
$loop = " SELECT product_id, wholesale_price FROM product";
$query= mysql_query($loop);
while($rows=mysql_fetch_assoc($query))
{
$row = mysql_fetch_row($query);
$id = $row[0];
$price = $row[1];
?>
QUERIES WITHIN WHILE LOOP
<?
$bawtry_stock = "
SELECT product_id,
( kids_uk_j_105 + kids_c_17 + kids_c_18 + kids_c_19 + ... etc )
AS SUM FROM product WHERE product_id = '$id'";
$result_bawtry = mysql_query($bawtry_stock) or die (mysql_error());
$line = mysql_fetch_row($result_bawtry);
$bawtry = $line[1];
$chain_stock = "
SELECT product_id,
(quantity_c_size_26_chain + quantity_c_size_28_chain + quantity_c_size_30_chain +
... etc )
AS SUM FROM product WHERE product_id = '$id'";
$result_chain = mysql_query($chain_stock) or die (mysql_error());
$line = mysql_fetch_row($result_chain);
$chain = $line[1];
/*
* Declare the total value of all pairs from Bawtry, Chain
*/
$totalpairs = $chain + $bawtry;
/*
* Insert values for stock to write to databse
* Total stock for Bawtry, Chain
* Total value of stock for Bawtry, Chain
*
*/
$bawtry_value = (float)($bawtry * $price);
$chain_value = (float)($chain * $price);
$total_value = (float)($price * ($bawtry + $chain));
$sql2="
UPDATE stock_figures SET
bawtry_stock='$bawtry',
chain_stock='$chain',
totalstock='$totalpairs',
bawtry_value='$bawtry_value',
chain_value='$chain_value',
totalvalue='$total_value'
WHERE id='$id'";
$result2=mysql_query($sql2) or die (mysql_error());
?>
// close while loop
<? } ?>
UPDATED CODE
$sql = "SELECT product_id, wholesale_price,
(kids_uk_j_105 + kids_c_17 + kids_c_18 + kids_c_19 + kids_c_20 + kids_c_21 +
... )
AS bawtry,
(quantity_c_size_26_chain + quantity_c_size_28_chain + quantity_c_size_30_chain +
... )
AS chain from product";
$result = mysql_query($sql) or die (mysql_error());
while ($line=mysql_fetch_assoc($result))
{
$id = $line['product_id'];
$price = $line['wholesale_price'];
$bawtry = $line['bawtry'];
$chain = $line['chain'];
/*
* Declare the total value of all pairs from Bawtry, Chain
*/
$totalpairs = $chain + $bawtry;
/*
* Insert values for stock to write to database
* Total stock for Bawtry, Chain
* Total value of stock for Bawtry, Chain
*
*/
$bawtry_value = (float)($bawtry * $price);
$chain_value = (float)($chain * $price);
$total_value = (float)($price * ($bawtry + $chain));
$sql2="
UPDATE stock_figures SET
bawtry_stock='$bawtry',
chain_stock='$chain',
totalstock='$totalpairs',
bawtry_value='$bawtry_value',
chain_value='$chain_value',
totalvalue='$total_value'
WHERE id='$id'";
$result2=mysql_query($sql2) or die (mysql_error());
However, it's still taking an absolute age to complete. It seems to run really fast when I comment out the UPDATE statement at the end. Obviously this needs to remain in the code, so I'll probably run the whole thing as a cronjob.
Unless any further improvements can be suggested?
It seems you doing a lot of wasted selects.
You first select some data from table products, then for each row you select again from the same table. Twice. Then finally inserting this into another table, stock_figures.
And the only operation you are doing is adding lots of figures together.
All of this can be done in a single query.
select product_id,
whole_sale_price,
sum(kids_uk_j_105,
kids_c_17,
...) as bawtry,
sum(quantity_c_size_26_chain,
quantity_c_size_28_chain,
...) as chain
from products;
If this still is taking lots of time you need to check some server settings and also number of rows
Every write you make is a transaction and depending on your ACID-level it might be slow to do commits. Change innodb-flush-log-at-trx-commit to 2 will speed up writes.
You are doing a full table scan on products-table. I guess this is intended but if that table is big reading it will take a while, and writing all those rows back to stock_figures is going to take even longer.
Consider another approach. For each write (insert, update or delete) to products have a trigger update the corresponding row in stock_figures. Not only will it eliminate the batch job, it will also make stock_figures be correct at any given time.
The first thing is:
$row = mysql_fetch_row($query);
$id = $row[0];
$price = $row[1];
I don't know if it does work for you, but you already take $rows in your while condition so probably you should change it into:
$id = $rows['product_id'];
$price = $row['wholesale_price'];
Then the next 2 queries you can combine info:
SELECT product_id,
( kids_uk_j_105 + kids_c_17 + kids_c_18 + kids_c_19 + ... etc )
AS `SUM` FROM product WHERE product_id = '$id'
UNION ALL
SELECT product_id,
(quantity_c_size_26_chain + quantity_c_size_28_chain + quantity_c_size_30_chain +
... etc )
AS `SUM` FROM product WHERE product_id = '$id'
or even:
SELECT product_id,
( kids_uk_j_105 + kids_c_17 + kids_c_18 + kids_c_19 + ... etc )
AS `SUM1`,
(quantity_c_size_26_chain + quantity_c_size_28_chain + quantity_c_size_30_chain +
... etc )
AS `SUM2`
FROM product WHERE product_id = '$id'
because those 2 queries are run on the same table.
But in fact you can use just one query to get everything about your products as Andreas Wederbrand pointed in his answer.
But there are more problems:
You use old mysql_ function instead of mysqli_ or PDO and your code is vulnerable to SQL Injection
For each product you run 2 extra queries (select with union all if you go my way and update).
I don't know how many products you have, but if you have for example 1000 products or 10000 products you cannot expect it will be very fast. In that case you should somehow run your script in cron or refresh the page and do the job for small amount of products (for example for 10 or 100 at one time)
You should also consider if your database structure is the best one. Usually using many columns as you here kids_uk_j_105, kids_c_17, kids_c_18 is not the best choice.
I hope you have set key primary_id at product_id column as least.
When executing many SQL commands, parsing them takes some time. You can reduce this overhead by using http://php.net/manual/en/mysqli.quickstart.prepared-statements.php
How much you gain, depends on case.
Prepared statements are also good for security reasons.
This answer does not void other answers here. Try to gain efficiency by reducing number of queries, analyzing their work, merging them if possible etc.
I am trying to update 3 column values in a row in mysql only if any of the 3 values is different.
Say I have a table of
x,y,z,id columns
I have currently,
Method A
update foo set x = 'x_value', y = 'y_value', z = 'z_value' where id = 'unique_id'
and ((x <> 'x_value') or (y <> 'y_value') or (z <> 'z_value'))
I don't know much about the theoretical benchmarking/architecture of mysql, and I was wondering if the statements
Method B
update foo set x ='x_value' where id = 'unique_id' and ((x <> 'x_value'));
update foo set y ='y_value' where id = 'unique_id' and ((y <> 'y_value'));
update foo set z ='z_value' where id = 'unique_id' and ((z <> 'z_value'));
is better or superior.
I realize that Method B will only do one write and 3 reads if only one column has changed, vs 3 writes and 3 reads for the Method A. I just don't know if it is more time intensive because method B requires looking up the index row 3 times.
Thanks in advance!
Based on what I've read in the comments, I agree with octern that you should simply run an update. It will use significantly less resources and based on your table engine, it will free up your table/ row lock for less time, making your table perform a lot better.
However, if you insist on doing a check before doing a write, do so through PHP. Simply do a select statement, compare the code in PHP and then update the appropriate table(s). For example:
$res = mysql_query("SELECT * FROM table1 WHERE PK_ID = '0'");
$arr = mysq_fetch_assoc($res);
$update = false;
if ($arr["field_1"] != $_POST["field_1"])
{
$update = true;
}
if ($arr["field_2"] != $_POST["field_2"])
{
$update = true;
}
if ($update)
{
mysql_query(sprintf("UPDATE table1 SET field_1 = '%s', field_2 = '%s'", $_POST["field_1"], $_POST["field_2"]));
}
if (
Method B will of course be more costly, because you do 3 different selects vs Method A's single select / update on condition.
Its pretty much a comparison of 1 statement to 3 statements. 1 will be faster as they are both update statements.
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