Hoping For Help in Writing This Query in PDO - php

I'm really new to PHP but I've been learning all I can, but I'm also aware that I have a lot to learn so go easy on me.
I'm currently working on a classifieds script and although the code is severely dated, It was a lot worse before I began cleaning it up and now I'm just trying to get the classifieds stable again, then I plan on going back and updating things to a more modern language (PDO) but for now I have NO IDEA on where to begin (and don't want to be reading for months while getting nowhere as I have been while learning HTML, CSS, PHP and JS)
The code below originally displayed the Title column from the table it was fetching the data from (tt_%s) but I have since modified the form which inserts the data into the DB so now there is no longer a "Title" column and in it's place there is now Year and Model from one table and Manufacturer from another table but this is where I get confused and am asking for help.
Presently this is what I am looking at; (Below the code I'll explain more)
<?php
echo "<div class='mostPopular'>";
echo "Popular<br>Listings";
$node = new sqlNode();
$node->table = "types";
$node->select = "ID";
$node->orderby = "ORDER BY rand()";
if(($typeRS = $mysql->select($node)) === false )
die('Unable to Retrieve Ad Type');
$sql = array();
$typeID = 0;
while( $adType = mysql_fetch_assoc($typeRS) ){
$typeID = sprintf("`tt_%s`", abs(intval($adType['ID'])));
$sql[] = sprintf("SELECT %s.Year, %s.Hits, %s.CategoryID, %s.ID, CONCAT('','%s') AS TypeID FROM %s WHERE (`ExpireDate` > NOW()) ",
$typeID,
$typeID,
$typeID,
$typeID,
intval($adType['ID']),
$typeID);
}
$sqlStr = #implode("union ",$sql);
$sqlStr .= " ORDER BY Hits DESC LIMIT 5";
if(($adRS = $mysql->exSql($sqlStr)) === false)
die('Unable to Retrieve Most Popular Ads');
while( $ad = mysql_fetch_assoc($adRS) ){
echo "<p>";
echo "<a href='detail.php?fatherID=".$ad['CategoryID']."&TypeID=".$ad['TypeID']."&ListingID=".$ad['ID']."'>";
echo $ad['Title'];
echo "</a>";
echo "</p>";
}
echo "</div>";
?>
For starters, I don't know what CONCAT('','%s') is all about, nor do I know what $sqlStr = #implode("union ",$sql); is all about.
In addition to these issues, considering that this code is outdated and I should be switching to PDO, I was hoping someone could show me exactly how to perform these queries with PDO as then I could learn by example and apply the same procedures to other queries throughout the site.
If anyone wouldn't mind conversing with me to the point where I can perform the same outcome with PDO I'd be most appreciative and I thank you all in advance while I eagerly await your replies.
BTW, I ask this after studying every PHP and MySql tutorial I can find for the past several months
Thanks

Although "Please rewrite this code to PDO" being too localized one, the real problem you have require different solution.
First of all, you need to fix your database architecture. It's terrible.
Premature optimization, being the root of all evil, depraved you from the right ways. There is no point in splitting your data into small chunks if you going to union them anyway.
You have to have one table for the ads.
It will make all this code obsolete, leaving only one regular query which can be run in one conventional PDO call.
Next, this code has been written by some sprintf() maniac, who made it overcomplicated out of nowhere. As a matter of fact, not a single sprintf() is really needed here.
$sql = array();
while( $row = mysql_fetch_assoc($typeRS) ){
$typeID = abs($row['ID']);
$sql[] = "SELECT Year, Hits, CategoryID, ID, '$typeID' AS TypeID
FROM tt_$typeID WHERE ExpireDate > NOW()";
}
$sqlStr = implode("union ",$sql)." ORDER BY Hits DESC LIMIT 5"
is enough.
But again, no need for this union query. Everything have to be performed via single ordinary query like this one
SELECT Year, Hits, CategoryID, ID, TypeID FROM ads ORDER BY Hits DESC LIMIT 5
which you no doubt will be able to run using whatever PDO example you have

Related

Efficient 2 dimensional array search in MySql

I am trying to design an application and part of it is to show users new articles in different categories after the last visit of the user to the webapp. To this I use MySql and have a table that keeps track of last visits and I can query the table to get a php array like below:
$array =[[user1,category1, datetime1],[user1,category2, datetime2],[user1,category3,datetime3]];
Where user is the user id and datetime is the visited datetime and category is the article category.
Having the setup above, I am trying to get new articles from the article table where the publish date is after user last visited to categories.
I can achieve this by multiple OR in a query like below, however it is not really a good and nice looking query, and probable not scalable. Is there any other way of doing this which is simpler and faster?
$multiwhere=[];
foreach($array as $a){
$multiwhere[]="select article_id from articles where category=".$a[1]." and publish_date>".$a[2];
}
And the final query would be like this:
"Select * from articles where article_id in (".implode(" or ".$multiwhere.")";
I deeply appreciate any suggestion to improve the query above.
Your query is almost correct, apart from the fact that you first retrieve all the article_id you want, and then use them to query for those articles. You can do that in one step, like so:
$multiwhere = [];
foreach ($array as $a) {
$multiwhere[] = "(category = " . $a[1] . " AND publish_date >= " . $a[2] .")";
}
$query = "SELECT * FROM articles";
if (count($multiwhere) > 0) {
$query = " WHERE " . implode(" OR ", $multiwhere);
}
One query will do.
I kept the way you use the $array, but it looks weird to me. Especially around publish_date. I cannot change that because I don't know the type of the field. And, of course, $array is quite a bad name. It tells you what the type of the variable is, not what it contains, as it should. A better name would be: $lastCategoryVisits, or something like that. Your loop should look something like this:
foreach ($lastCategoryVisits as $lastCategoryVisit) {
$category = $lastCategoryVisit["category"];
$lastVisit = $lastCategoryVisit["lastVisit"];
$QueryConditions[] = "(category = '$category' AND publish_date >= '$lastVisit')";
}
Don't be afraid to write out what your code actually does. It might be a bit longer, but now you can see what is going on. This will not slow down the execution of your code at all.
Finally, it would be better to always use prepared statements to prevent the possibility of SQL-injection. If you get into the habit of always doing this you don't have to use excuses like: "It is not important in this project.", "I'll to it later when the code works." or "The data for this query doesn't come from an user.".

PHP SQL Request: select only the lowest value of time where name = $name and stage = $stage

Hi so I've already got a php script working working for this but it use a massive amount of data for a really easy task so I really would like to improve it.
My problem is the following: I have a SQL database build like this
and I would to do request like this one:
http://myphppage.php?username=Whataname&stage=1
and I would like it to echo the result so that if I read my php page content I can read the following:
21
so basicly all I want is to do a request with the username and the stage as parameters and I would like it to return the lowest value of the column "time" WHERE name=the name parameter entered AND stage = the stage parameter entered
I'm not really good in sql but I'm pretty I can make this kind of sql request in a single line or two instead of this massive script I have right now.
here's the current script I have:
<?php
$q2 = "SELECT username FROM DB WHERE stage='$stage' GROUP BY username ORDER BY time ASC";
$result2 = mysql_query($q2);
$times = array();
$userusernames = array();
$usernames = mysql_fetch_assoc($result2);
while($rows=mysql_fetch_assoc($result2))
{
$temp = $rows['username'];
$q3 = "SELECT time FROM DB WHERE stage='$stage' AND username='$temp' GROUP BY time ORDER BY time ASC";
$result3 = mysql_query($q3);
while($aaa = mysql_fetch_assoc($result3))
{
array_push($times, $aaa['time']);
array_push($userusernames, $rows['username']);
if($rows['username']==$username)
{
echo $aaa['time'];
}
break;
}
}
?>
may someone please help me figuring out how to do this
EDIT: I have been looking around on internet but I can't find what I'm looking for, I'm pretty sure there's the answer somewhere so maybe you can just help me reformulate my question and I could find the answer by myself. I'm pretty stuck due to my lack of english vocabulary...
thx in advance
You can archive the wanted result with only one query:
SELECT username, MIN(time) AS lowertime
FROM test_table
WHERE stage='$stage' AND username='$temp'
GROUP BY username
ORDER BY time

Number of records returned after using prepared statement

I am currently taking an IT course in which people can bring in their computer(s) and the class works on them to get experience. Right now, the instructor has the customers fill out a sheet of paper giving their name, phone number and the computer's issue(s). However, he would like to use a PHP page to allow the students or himself look back to see what this person's previous issues were (if any). I am using PDO and prepared statements to query the database, but I am having trouble figuring out how to get the number of records returned by the prepared statement. I've tried using stmt_num_rows, but it doesn't appear to be working. Here is the code I have so far:
$custID = $_GET["id"];
$compID = $_GET["compID"];
$stmtIssues = $db->prepare("SELECT IssueID, DateRequested, Issue, ActionsTaken FROM ISSUES WHERE ComputerID=:compID AND CustomerID=:custID ORDER BY DateRequested");
$stmtIssues->bindParam(":custID", $custID);
$stmtIssues->bindParam(":compID", $compID);
$stmtIssues->execute();
$numIssues = stmt_num_rows($stmtIssues);
Am I doing this right?
Any and all help is greatly appreciated.
Chris
You could do a few things:
Solution one, if you just want the count, let the database count for you:
$stmtIssues = $db->prepare("SELECT COUNT(IssueID) FROM ISSUES WHERE ComputerID=:compID AND CustomerID=:custID ORDER BY DateRequested");
$stmtIssues->bindParam(":custID", $custID);
$stmtIssues->bindParam(":compID", $compID);
$stmtIssues->execute();
$numIssues = $stmtIssues->fetchColumn();
Solution two, if you're going to display the results in addition to the count:
$stmtIssues = $db->prepare("SELECT IssueID, DateRequested, Issue, ActionsTaken FROM ISSUES WHERE ComputerID=:compID AND CustomerID=:custID ORDER BY DateRequested");
$stmtIssues->bindParam(":custID", $custID);
$stmtIssues->bindParam(":compID", $compID);
$stmtIssues->execute();
$rows = $db->fetchAll();
$numIssues = count($rows);
foreach ($rows as $row) {
echo $row['IssueID']; // you can add other columns and/or formatting here too.
}
Solution one is quicker and returns only the count. Solution two, on the other hand, returns all details you may want to display.

Complicated Database Setup: Trying to display two field values that correspond to three other specific fields

I am creating a page to display a bunch of information from a database, but the database that I am working with is set up funky.
While there are about 15 columns, the ones I am concerned with are:
Config_Name, Config_Type, Seq_Nbr, Question, and, Answer.
Each Config_Type has a different set of Seq_Nbr Values.
So I have a Config_Type called "_Hosting" which has about 12 different "Seq_Nbr" values. Each value corresponds to a different "Question" and "Answer" field. For example,
Examples:
When any given Config_Name, Config_Type = _Hosting & Seq_Nbr = 60; the Question field will be
"Control Panel URL" and the Answer field will be
"www.examplesite.com/cpanel"
When any given Config_Name, Config_Type = _Hosting & Seq_Nbr = 70; the Question field will be
"Control Panel Username" and the Answer field will be "someusername"
What I am trying to do is get all of the information displayed on one page.
I was thinking that I could use the code and query individual values for each section (from the examples):
<?php
$sql = "SELECT Question, Answer FROM configs WHERE Config_Name='Company', Config_Type = '_Hosting', Seq_Nbr = '60'";
$result = mysql_query($sql)or die(mysql_error());
$content = $result
?>
And then echo it into each individual field:
<p>
<h2><? echo "Control Panel URL"; ?><h2>
<br></br>
<h4><? echo $content; ?><h4>
</p></br>
But because I have to do this for 10+ "Seq_Nbr" values, this entire process seems tedious and unnecessarily repetitive. I know I could probably implement a switch() and have it go through different SQL queries, but I am trying to find a more efficient way to do this.
Any ideas would be greatly appreciated!
(I know the code isn't entirely correct, I am just trying to convey the method)
Thanks!
Why not just query all and display all??
And use an ORDER BY to organize it however you want....
//Google PDO to learn more, and how to connect
//Connect
$conn = new PDO("...");
$sql = "SELECT * FROM configs ORDER BY Seq_Nbr";
foreach($conn->query($sql) as $val){
echo "Name :". $val['Config_Name']."<br>";
echo "Type :".$val['Config_Type']."<br>";
echo "Question :". $val['Question']." | Answer :".$val['Answer']."<br>";
}
Forgive me if Im misunderstanding what you wanna do...
Also, Im using PDO in the example, just adapt what you need to if you're still gonna use mysql_*
if you want them all returned in one select, you might try an "in" clause.
$seq = "'60','70','80'";
$sql = "SELECT Question, Answer FROM configs WHERE Config_Name='Company' and Config_Type = '_Hosting' and Seq_Nbr in ( " . $seq . " ) ";

Query on large mysql database

i've got a script which is supposed to run through a mysql database and preform a certain 'test'on the cases. Simplified the database contains records which represent trips that have been made by persons. Each record is a singel trip. But I want to use only roundway trips. So I need to search the database and match two trips to each other; the trip to and the trip from a certain location.
The script is working fine. The problem is that the database contains more then 600.000 cases. I know this should be avoided if possible. But for the purpose of this script and the use of the database records later on, everything has to stick together.
Executing the script takes hours right now, when executing on my iMac using MAMP. Off course I made sure that it can use a lot of memory etcetare.
My question is how could I speed things up, what's the best approach to do this?
Here's the script I have right now:
$table = $_GET['table'];
$output = '';
//Select all cases that has not been marked as invalid in previous test
$query = "SELECT persid, ritid, vertpc, aankpc, jaar, maand, dag FROM MON.$table WHERE reasonInvalid != '1' OR reasonInvalid IS NULL";
$result = mysql_query($query)or die($output .= mysql_error());
$totalCountValid = '';
$totalCountInvalid = '';
$totalCount = '';
//For each record:
while($row = mysql_fetch_array($result)){
$totalCount += 1;
//Do another query, get all the rows for this persons ID and that share postal codes. Postal codes revert between the two trips
$persid = $row['persid'];
$ritid = $row['ritid'];
$pcD = $row['vertpc'];
$pcA = $row['aankpc'];
$jaar = $row['jaar'];
$maand = $row['maand'];
$dag = $row['dag'];
$thecountquery = "SELECT * FROM MON.$table WHERE persid=$persid AND vertpc=$pcA AND aankpc=$pcD AND jaar = $jaar AND maand = $maand AND dag = $dag";
$thecount = mysql_num_rows(mysql_query($thecountquery));
if($thecount >= 1){
//No worries, this person ID has multiple trips attached
$totalCountValid += 1;
}else{
//Ow my, the case is invalid!
$totalCountInvalid += 1;
//Call the markInvalid from functions.php
$totalCountValid += 1;
markInvalid($table, '2', 'ritid', $ritid);
}
}
//Echo the result
$output .= 'Total cases: '.$totalCount.'<br>Valid: '.$totalCountValid.'<br>Invalid: '.$totalCountInvalid; echo $output;
Your basic problem is that you are doing the following.
1) Getting all cases that haven't been marked as invalid.
2) Looping through the cases obtained in step 1).
What you can easily do is to combine the queries written for 1) and 2) in a single query and loop over the data. This will speed up the things a bit.
Also bear in mind the following tips.
1) Selecting all columns is not at all a good thing to do. It takes ample amount of time for the data to traverse over the network. I would recommend replacing the wild-card with all columns that you really need.
SELECT * <ALL_COlumns>
2) Use indexes - sparingly, efficiently and appropriately. Understand when to use them and when not to.
3) Use views if you can.
4) Enable MySQL slow query log to understand which queries you need to work on and optimize.
log_slow_queries = /var/log/mysql/mysql-slow.log
long_query_time = 1
log-queries-not-using-indexes
5) Use correct MySQL field types and the storage engine (Very very important)
6) Use EXPLAIN to analyze your query - EXPLAIN is a useful command in MySQL which can provide you some great details about how a query is ran, what index is used, how many rows it needs to check through and if it needs to do file sorts, temporary tables and other nasty things you want to avoid.
Good luck.

Categories