we have a script that has been provided from our developers for php to generate a best sellers list from our database, however we need it to be in coldfusion!
Is there a simple way to convert or will this need rewriting completely?
Thanks in advance for any advice :-)
// // ---------- // Get Top Selling Products (by sku) // ---------- //
function CWgetBestSelling($max_products=5, $sub_ids=0)
{
$productQuery = '';
$returnQuery = '';
$idList = '0';
$itemsToAdd = '';
if (!is_numeric($idList[0])) {
$idList = '0';
}
$q_productQuery = mysql_query( "
SELECT count(*) as prod_counter,
p.product_id,
p.product_name,
p.product_preview_description,
p.product_date_modified
FROM cw_products p
INNER JOIN cw_order_skus o
INNER JOIN cw_skus s
WHERE o.ordersku_sku = s.sku_id
AND s.sku_product_id = p.product_id
AND NOT p.product_on_web = 0
AND NOT p.product_archive = 1
AND NOT s.sku_on_web = 0
GROUP BY product_id
ORDER BY prod_counter DESC
LIMIT ".$max_products
,$_ENV["request.cwapp"]["db_link"]);
$productQuery = array();
while ($qd = mysql_fetch_assoc($q_productQuery)) {
$productQuery[] = $qd;
}
// add values to list
foreach ($productQuery as $values) {
$idList = $values['product_id'] . "," . $idList;
}
// if not enough results, fill in from sub_ids
if (count($productQuery) < $max_products) {
// number needed
$itemsToAdd = $max_products - count($productQuery);
for ($i = 1; $i <= $itemsToAdd; $i++) {
if (substr_count($sub_ids, ',') >= $i) {
$idListArray = explode(',', $sub_ids);
$idList .= "," . $idListArray[$i];
}
}
$q_resultsQuery = mysql_query("
SELECT 0 as prod_counter,
p.product_id,
p.product_name,
p.product_preview_description,
p.product_date_modified
FROM cw_products p
WHERE p.product_id in(".CWqueryParam($idList).")
AND NOT p.product_on_web = 0
AND NOT p.product_archive = 1
ORDER BY product_date_modified DESC
",$_ENV["request.cwapp"]["db_link"]);
} else {
$q_resultsQuery = mysql_query("
SELECT count(*) as prod_counter,
p.product_id,
p.product_name,
p.product_preview_description,
p.product_date_modified
FROM cw_products p
INNER JOIN cw_order_skus o
INNER JOIN cw_skus s
WHERE o.ordersku_sku = s.sku_id
AND s.sku_product_id = p.product_id
AND NOT p.product_on_web = 0
AND NOT p.product_archive = 1
AND NOT s.sku_on_web = 0
GROUP BY product_id
ORDER BY prod_counter DESC, product_date_modified
",$_ENV["request.cwapp"]["db_link"]);
}
while ($qd = mysql_fetch_assoc($q_resultsQuery)) {
$returnQuery[] = $qd;
}
return $returnQuery;
}
Code conversion questions don't tend to stay open long because they're viewed as lazy. So, Here are some references to get you started. I'm no PHP pro but after a quick glance at your code I think this list of links will give you a good head start.
CFFunction
CFQuery
CFArgument
valueList()
CFLoop
CFif
In the interest of not doing your work for you and give you the opportunity to learn, I'll provide some samples but not the entire code so you get an idea where your PHP fits into CF. This is also the tag version, not the script version.
<cffunction name = "CWgetBestSelling" ...>
<cfargument name = "max_products" default = "5" ...>
<cfargument ...>
<cfset var local.productQuery = "">
<cfset var local.returnQuery = "">
<cfset ...>
<cfset ...>
<cfquery name = "q_productQuery " datasource = "yourDatasource">
SELECT
count(*) as prod_counter,
p.product_id,
p.product_name,
p.product_preview_description,
p.product_date_modified
FROM
cw_products p
INNER JOIN cw_order_skus o
INNER JOIN cw_skus s
WHERE
o.ordersku_sku = s.sku_id
AND s.sku_product_id = p.product_id
AND NOT p.product_on_web = 0
AND NOT p.product_archive = 1
AND NOT s.sku_on_web = 0
GROUP BY
product_id
ORDER BY
prod_counter DESC
LIMIT #arguments.max_products#
</cfquery>
...
...
...
<cfreturn yourReturnVariable>
</cffunction>
Related
I have 3 queries which I run which are nearly identical, the latter two have an AND condition.
Main query:
$mess = $mysqli->prepare("SELECT * from ( SELECT cm.id ,cm.userid,cm.message,cm.voteup,cm.votedown,cm.date
FROM chat_messages cm
INNER JOIN members m ON m.id =cm.userid
INNER JOIN chat_settings cs ON cs.id = cm.room_id
WHERE cm.setting_id = ?
ORDER BY cm.date DESC LIMIT 30 ) ddd
ORDER BY date ASC ");
$mess->bind_param("i", $room);
$mess->execute();
$mess->store_result();
$mess->bind_result($chatid,$chat_userid,$message,$voteup,$votedown,$date);
while($row = $mess->fetch()){
//im fetching here in my <div class='div1' >
}
Then, in the second div I have to add an AND condition:
$mess2 = $mysqli->prepare("SELECT * from ( SELECT cm.id ,cm.userid,cm.message,cm.voteup,cm.votedown,cm.date
FROM chat_messages cm
INNER JOIN members m ON m.id =cm.userid
INNER JOIN chat_settings cs ON cs.id = cm.room_id
WHERE cm.setting_id = ? AND voteup - votedown >= 5
ORDER BY cm.date DESC LIMIT 30 ) ddd
ORDER BY date ASC ");
$mess2->bind_param("i", $room);
$mess2->execute();
$mess2->store_result();
$mess2->bind_result($chatid,$chat_userid,$message,$voteup,$votedown,$date);
while($row2 = $mess2->fetch()){
//im fetching here in my <div class='div2' >
}
Lastly, in the third div I have a slightly different AND condition:
$mess3 = $mysqli->prepare("SELECT * from ( SELECT cm.id ,cm.userid,cm.message,cm.voteup,cm.votedown,cm.date
FROM chat_messages cm
INNER JOIN members m ON m.id =cm.userid
INNER JOIN chat_settings cs ON cs.id = cm.room_id
WHERE cm.setting_id = ? AND votedown - voteup >= 5
ORDER BY cm.date DESC LIMIT 30 ) ddd
ORDER BY date ASC ");
$mess3->bind_param("i", $room);
$mess3->execute();
$mess3->store_result();
$mess3->bind_result($chatid,$chat_userid,$message,$voteup,$votedown,$date);
while($row3 = $mess3->fetch()){
//im fetching here in my <div class='div3' >
}
Everything works BUT doing this near-same query seems clumsy. Is it possible to construct the same thing with only one query? I have used $mess->data_seek(0); but its not helping because I didn't add my condition to the query.
Just go for PhP to filter your data instead of triple query your database. In this case you can figure out to go for this solution because you call 3 times your query with the same parameter :
$mess3 = $mysqli->prepare(" SELECT *
FROM ( SELECT cm.id ,
cm.userid,
cm.message,
cm.voteup,
cm.votedown,
cm.date
FROM chat_messages cm
INNER JOIN members m ON m.id =cm.userid
INNER JOIN chat_settings cs ON cs.id = cm.room_id
WHERE cm.setting_id = ?
AND votedown - voteup >= 5
ORDER BY cm.date DESC LIMIT 30 ) ddd
ORDER BY date ASC ");
$mess3->bind_param("i", $room);
$mess3->execute();
$mess3->store_result();
$mess3->bind_result($chatid,$chat_userid ,$message,$voteup,$votedown ,$date);
while($row = $mess3->fetch()){
$voteup = $row['voteup'];
$votedown = $row['votedown'];
addToDiv1($row);
if( $voteup - $votedown >= 5 ) {
addToDiv2($row);
}
if( $votedown - $voteup >= 5 ) {
addToDiv3($row);
}
}
I will just give an answer based specifically on cleaning up your code. Technically you will still make the 3 calls in this scenario, but it will be cleaner because you include one function only, you don't see the script behind it.
As I mentioned, I am not an SQL aficionado so I can not give a good solution there (maybe you can use GROUP BY and perhaps an OR clause...I don't really know...). If I were to do this, I would do a function that can return all the options:
/core/functions/getChatMessages.php
function getChatMessages($settings,$mysqli)
{
$id = (!empty($settings['id']))? $settings['id'] : false;
$type = (!empty($settings['type']))? $settings['type'] : false;
$max = (!empty($settings['max']))? $settings['max'] : 30;
$mod = '';
// No id, just stop
if(!is_numeric($id))
return false;
// equation one
if($type == 'up')
$mod = ' AND voteup - votedown >= 5';
// equation two
elseif($type == 'down')
$mod = ' AND votedown - voteup >= 5';
$mess = $mysqli->prepare("SELECT * from ( SELECT cm.id ,cm.userid,cm.message,cm.voteup,cm.votedown,cm.date
FROM chat_messages cm
INNER JOIN members m ON m.id =cm.userid
INNER JOIN chat_settings cs ON cs.id = cm.room_id
WHERE cm.setting_id = ? {$mod}
ORDER BY cm.date DESC LIMIT {$max} ) ddd
ORDER BY date ASC");
$mess->bind_param("i", $id);
$mess->execute();
$mess->store_result();
$mess->bind_result($chatid, $chat_userid, $message, $voteup, $votedown, $date);
while($mess->fetch()){
$result[] = array(
'chatid'=>$chatid,
'chat_userid'=>$chat_userid,
'message'=>$message,
'voteup'=>$voteup,
'votedown'=>$votedown
);
}
// Send back the data
return (!empty($result))? $result : array();
}
To use:
// Include our handy function
require_once('/core/functions/getChatMessages.php');
// Store our id for use
$settings['id'] = 100;
// Should get 30 from first select
$voteGen = getChatMessages($settings,$mysqli);
// Should get 30 from second select
$settings['type'] = 'up';
$voteUp = getChatMessages($settings,$mysqli);
// Should get 15 from third select
// Just for the heck of it, I added in a limit settings
$settings['max'] = 15;
$settings['type'] = 'down';
$voteDown = getChatMessages($settings,$mysqli);
Now that you have these stored, just use a foreach loop to place them into your view. The good side of this is that you can call this where ever and when ever since the function only returns data. It allows you to work with the data in a view or non-view situation. Side note, I use PDO, so if there is something ineffective with the way the mysqli is fetching, that will be why. It's probably just best to fetch an assoc array to return...
I have a query that returns close to a 1000 records. Using pagination, I'm showing a 100 records per page. Great...no problem. I can also sort by last name or first name in either ascending of descending order. ok so far. The first page returns records for last name starting with A to C. The problem I'm having is that when I click last name to descend I get records with last name starting with Z. The records at the end of my query, I want to get results going from C to A (what is shown on my first page...repeating the same functionality in each page.
Here is what I got...
$orderColumn = 'lastName';
$orderDirection = 'ASC';
if( isset($_POST["oc"]) && $_POST["oc"] !== '' ) { $orderColumn = $_POST["oc"]; }
if( isset($_POST["od"]) && $_POST["od"] !== '' ) { $orderDirection = $_POST["od"]; }
$per_page = 100;
$query = "SELECT * FROM table as t
LEFT JOIN table_2 as t2 ON t.pk_uID = t2.fk_uID
LEFT JOIN table_3 as t3 ON t3.fk_utID = t2.pk_utID
WHERE t3.fk_utID = 7 and t.interviewed = 0";
$result = $db->query($query);
$count = mysql_num_rows($result);
$total = ceil($count/$per_page);
if ($_GET['page']) {
$page = $_GET['page'];
}
$offset = (($page-1)*$per_page);
$query2 = "SELECT firstName as first, lastName as last FROM table
LEFT JOIN table_2 as t2 ON t.pk_uID = t2.fk_uID
LEFT JOIN table_3 as t3 ON t3.fk_utID = t2.pk_utID
WHERE t3.fk_utID = 7 and interviewed = 0 order by $orderColumn $orderDirection LIMIT $offset, $per_page";
$res = $db-> query($query2);
while($row = mysql_fetch_array($res)){
echo "<span style='display: inline-block; width: 15%;'>$row[first]</span>";
echo "<span style='display: inline-block; width: 15%;'>$row[last]</span>";
}
To what I was saying in comment.. BTW I'm on my mobile phone so this may be unformatted and or take a while...
Select what_you_need
From
( select your_inner_select
From table t
LEFT JOIN table_2 as t2 ON t.pk_uID = t2.fk_uID
LEFT JOIN table_3 as t3 ON t3.fk_utID = t2.pk_utID
WHERE t3.fk_utID = 7 and interviewed = 0 LIMIT $offset, $per_page
ORDER BY $orderColumn ASC
)t
order by $orderColumn $orderDirection
I currently getting a 2 million records from different tables to generate a url to create a sitemap. The script eat too much resources and use 100% of the servers performance
query
SELECT CONCAT("/url/profile/id/",u.id,"/",nickname) as url FROM users AS u
UNION ALL
Select CONCAT("url/city/", c.id, "/paramId/",p.id,"/",Replace(p.title, " ", "+"),"/",r.region_Name,"/",c.city_Name) AS url
From city c
Join region r On r.id = c.id_region
Join country country On country.id = c.id_country
cross join param p
Where country.used = 1
And p.active = 1
//i store it on an array $url_list then process for creating a sitemap..but it takes time and to much resources
//i tried to get the data by batch using LIMIT 0,50000
but getting the maxrow for paging takes time. also the code doesn't look good for i have to run a two query that has a large data
$url_list = array();
$maxrow = SELECT COUNT(*) AS max from (
SELECT CONCAT("/url/profile/id/",u.id,"/",nickname) as url FROM users AS u
UNION ALL
Select CONCAT("url/city/", c.id, "/paramId/",p.id,"/",Replace(p.title, " ", "+"),"/",r.region_Name,"/",c.city_Name) AS url
From city c
Join region r On r.id = c.id_region
Join country country On country.id = c.id_country
cross join param p
Where country.used = 1
And p.active = 1) as tmp
$limit = 50,000;
$bybatch = ceil($maxrow/$limit);
$start = 0;
for($i = 0;$i < $bybatch; $i++){
// run query and store to $result
(SELECT CONCAT("/url/profile/id/",u.id,"/",nickname) as url FROM users AS u
UNION ALL
Select CONCAT("url/city/", c.id, "/paramId/",p.id,"/",Replace(p.title, " ", "+"),"/",r.region_Name,"/",c.city_Name) AS url
From city c
Join region r On r.id = c.id_region
Join country country On country.id = c.id_country
cross join param p
Where country.used = 1
And p.active = 1 LIMIT $start,$limit);
$start += $limit;
//push to $url_list
$url_list = array_push($result);
}
//when finish i use this to create a site map
$linkCount = 1;
$fileNomb = 1;
$i = 0;
foreach ($url_list as $ul) {
$i += 1;
if ($linkCount == 1) {
$doc = new DOMDocument('1.0', 'utf-8');
$doc->formatOutput = true;
$root = $doc->createElementNS('http://www.sitemaps.org/schemas/sitemap/0.9', 'urlset');
$doc->appendChild($root);
}
$url= $doc->createElement("url");
$loc= $doc->createElement("loc", $ul['url']);
$url->appendChild($loc);
$priority= $doc->createElement("priority",1);
$url->appendChild($priority);
$root->appendChild($url);
$linkCount += 1;
if ($linkCount == 49999) {
$f = fopen($this->siteMapMulti . $fileNomb .'.xml', "w");
fwrite($f,$doc->saveXML());
fclose($f);
$linkCount = 1;
$fileNomb += 1;
}
}
Any better way to do this? or to speed up the performance?
Added
Why is this faster than sql query but consumes 1 hundred percent of the servers resources and performance
$this->db->query('SELECT c.id, c.city_name, r.region_name, cr.country_name FROM city AS c, region AS r, country AS cr WHERE r.id = c.id_region AND cr.id = c.id_country AND cr.id IN (SELECT id FROM country WHERE use = 1)');
$arrayCity = $this->db->recordsArray(MYSQL_ASSOC);
$this->db->query('SELECT id, title FROM param WHERE active = 1');
$arrayParam = $this->db->recordsArray(MYSQL_ASSOC);
foreach ($arrayCity as $city) {
foreach ($arrayParam as $param) {
$paramTitle = str_replace(' ', '+', $param['title']);
$url = 'url/city/'. $city['id'] .'/paramId/'. $param['id'] .'/'. $paramTitle .'/'. $city['region_name'] .'/'. $city['city_name'];
$this->addChild($url);
}
}
I suggest you not to use UNION and just issue two separated queries. It will speed up a query itself.
Also as you mentioned above it's good idea to get data by batches.
And finally, don't collect all data in memory. Immediately write it to file in your loop.
Just open file in beginning, write each URL entry in loop and close file in end.
— open file for writing
— count query users table
— do several selects with LIMIT in loop (as you already done)
— right here in loop while ($row = mysql_fetch_array()) write each row to file
and than repeat such algorithm for another table.
It would be useful to implement a function for writing data to file, so you can call that function and adhere to the DRY principle.
Ok so my query is this:
select distinct `rate_id`, `p_id`, p_rate from shipping_rates inner join products_to_categories ptc inner join customers_basket cb on ptc.products_id = cb.products_id where cb.customers_id='1' and p_status = '1' and p_free = '0' group by p_id
which returns
rate_id p_id p_rate
1 1 10.00
2 22 11.00
which is what i want however when i add that to an array with
$p_rate[] = $sInfo->p_rate;
$rate = array_sum($p_rate);
this returns 10.00 not 21.00 like it should
by the way here is my code for the objectInfo class
function objectInfo($object_array) {
reset($object_array);
while (list($key, $value) = each($object_array)) {
$this->$key = tep_db_prepare_input($value);
}
}
}
}
and the code right below is
$sRate = tep_db_fetch_array($status_query);
if ($sRate !=''){//error checking for empty query
$sInfo = new objectInfo($sRate);
}
trp_db_fetch_array() is just a function calling up mysql_fetch_array
Are you doing the array_sum() INSIDE the fetch loop? e.g.
while($sInfo = fetch_from_db($result)) {
$p_rate[] = $sInfo->p_rate;
$rate = array_sum($p_rate);
}
? If so, then you'll have to move the summation to OUTSIDE the loop, so it only sums up after you've retrieve all of the data from the DB:
while($sInfo = fetch_from_db($result)) {
$p_rate[] = $sInfo->p_rate;
}
$rate = array_sum($p_rate);
$sInfo only contains one row. You need to fetch the whole result set first.
You seem to use a framework and not the native php functions. For this reason I cannot give the code used to do that.
Once you have the result set, you use :
foreach($sInfoTable as $sInfo){
$p_rate[] = $sInfo->p_rate;
}
$rate = array_sum($p_rate);
Ok so i found out the answer by hunting and pecking here is the query
$status_query = tep_db_query("select `rate_id`, `p_id`, `p_rate`, cb.customers_id from " . TABLE_SHIPPING_RATES ." inner join ". TABLE_PRODUCTS_TO_CATEGORIES . " ptc inner join ". TABLE_CUSTOMERS_BASKET . " cb on cb.products_id = p_id where cb.customers_id='" . $customer_id ."' and p_status = '1' and p_free = '0' group by cb.customers_id,p_id");
and here is where i got all rows:
while($row = mysql_fetch_assoc($status_query)){
$p_rate[] = $row["p_rate"];
}
and here is where i summed up the rows:
$rate = array_sum($p_rate);
so now this doesn't need to count the rows or etc and is a lot cleaner
I have to output some products from table 'products', along with the lowest price from the table 'product_licenses', which is the only column I need from that table in this query.
However, when I try to use the sql function MIN(), my loop only runs through the code once and gets the first result and then it stops, so I am a bit lost here.
This is the query using min() :
$mysql->query("
SELECT pd.*, min(lc.price) AS price
FROM `products` AS pd, product_licenses AS lc
WHERE pd.`status` = '1' AND lc.product_id = pd.id
ORDER BY pd.`id` ASC
$limitQuery
");
I'm using this function to get the products, but this, unfortunately, fetches the highest price:
public function getAllProducts($start = 0, $limit = 0, $order = '`datetime` ASC') {
global $mysql;
$limitQuery = '';
if ($limit != 0) {
$limitQuery = " LIMIT $start,$limit ";
}
**// Not working if I use min() on lc.price**
$mysql->query("
SELECT pd.*, lc.price
FROM `products` AS pd, product_licenses AS lc
WHERE pd.`status` = '1' AND lc.product_id = pd.id
ORDER BY pd.`id` ASC
$limitQuery
");
if ($mysql->num_rows() == 0) {
return false;
}
$this->usersWhere = '';
$return = array();
while ($d = $mysql->fetch_array()) {
$categories = explode(',', $d['category_id']);
unset($d['category_id']);
foreach ($categories as $c) {
$c = trim($c);
if ($c != '') {
$d['category_id'][$c] = $c;
}
}
$return[$d['id']] = $d;
}
$this->foundRows = $mysql->getFoundRows();
return $return;
}
Add GROUP BY in your query. your current query returns only one result since your are using aggregate function (MIN) but not grouping it.
SELECT pd.col1,
pd.col2, min(lc.price) AS PRICE
FROM `products` AS pd
INNER JOIN product_licenses AS lc
ON lc.product_id = pd.id
WHERE pd.`status` = '1'
GROUP BY pd.col1, pd.col2, pd.col3
ORDER BY pd.`id` ASC
$limitQuery
PS: post the structure of your database with records. It will the community understands your question clearly :)
You have no group by clause in your query, so the query is returning the first row only.
SELECT pd.col1, pd.col2, pd.col3, min(lc.price) AS price
FROM `products` AS pd, product_licenses AS lc
WHERE pd.`status` = '1' AND lc.product_id = pd.id
group by pd.col1, pd.col2, pd.col3
ORDER BY pd.`id` ASC
$limitQuery
You'll need a GROUP BY in there, like so:
SELECT pd.*, min(lc.price) AS price
FROM `products` AS pd, product_licenses AS lc
WHERE pd.`status` = '1' AND lc.product_id = pd.id
GROUP BY pd.`id`
ORDER BY pd.`id` ASC
$limitQuery
Note that with MySQL, you only need to group by the id column even though you are selecting other columns from the products table.