php loop in mysql query - php

I have a big mysql query like this:
SELECT u.userid AS `User`
, SUM(CASE WHEN activitydate='2011-07-01' THEN round(time/60) ELSE 0 END) AS `2011-07-01`
, SUM(CASE WHEN activitydate='2011-07-02' THEN round(time/60) ELSE 0 END) AS `2011-07-02`
.......
, SUM(CASE WHEN activitydate='2011-07-30' THEN round(time/60) ELSE 0 END) AS `2011-07-30`
FROM hoursbase h
JOIN person u
ON h.userid = u.id
WHERE h.activitydate BETWEEN '2011-07-01' AND '2011-07-30'
GROUP BY h.userid
ORDER BY h.userid
Is there any way that i can put above query in loop using php.
Also i am try to add one drop down menu and on selecting, the respective month will update in query.
Regards,
Chandru.

If you don't mind the query having a different output format you can rewrite it like so:
SELECT u.userid AS `User`
,activitydate
,sum(round(ifnull(time,0)/60)) as timetaken
FROM hoursbase h
JOIN person u ON h.userid = u.id
WHERE h.activitydate BETWEEN :startdate AND :enddate /*note the : params*/
GROUP BY h.userid, h.activitydate
ORDER BY h.userid, h.activitydate
This will return your data grouped by userid first and then by activitydate.
It will also run a lot faster.
Finally it will be easier to get the results per user per date in php.
And when you change months you don't have to change the number of columns.
Here's how to loop through it in php using a loop:
I've copied the code from this answer: How do I loop through a MySQL query via PDO in PHP?
// $attrs is optional, this demonstrates using persistent connections,
// the equivalent of mysql_pconnect
$attrs = array(PDO::ATTR_PERSISTENT => true);
// connect to PDO
$pdo = new PDO("mysql:host=localhost;dbname=test", "user", "password", $attrs);
// the following tells PDO we want it to throw Exceptions for every error.
// this is far more useful than the default mode of throwing php errors
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// prepare the statement. the place holders allow PDO to handle substituting
// the values, which also prevents SQL injection
$stmt = $pdo->prepare("SELECT u.userid AS `User`..... ");
// bind the parameters
$stmt->bindValue(":startdate", "2011-07-01");
$stmt->bindValue(":enddate", "2011-07-31");
// initialise an array for the results
$products = array();
if ($stmt->execute()) {
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
//do more useful stuff here
//escape all fields that can be entered by users using htmlspecialchars
//to prevent XSS exploits.
echo htmlspecialchars($row['User']);
echo htmlspecialchars($row['activitydate']);
echo $row['timetaken'];
}
}
// set PDO to null in order to close the connection
$pdo = null;
About htmlspecialchars()
You need to escape all string fields that can be entered by a user and that you output to screen.
In this case I escaped userid and activitydate because I'm only 95% sure these are integer and date fields, I'd skip escaping if I was 100% sure, but if I'm not I have to escape.
Links:
How to escape output in PHP
How do I loop through a MySQL query via PDO in PHP?

Related

Query works in phpmyadmin but same query won't return in PHP script

This is NOT a duplicate. None of the already existing threads have the same problem as me.
I have a database that stores athlete performances. It contains sessions, each session has sets, each set has "tables" (such as 4x100m, 12x50m and so on), and each table has times. I also have a table for athletes. Each athlete has an ID, each time links with the athlete through the AthleteID. Every session, set, timetable and time also have each unique IDs, used to link them with each other.
I want to make it so that when passing a session ID, it will return all the athletes that have at least 1 time in that session. I made a page that gets requests and the session ID is passed as GET search data (will make it POST later on). The request system works fine, but the problem is in the query. To do it I used inner joins to connect each table. This is my query (it is not the fastest method, but that's for another thread):
$q = "SET #evID = " . $method['sessID'] . ";";
$q .= "SELECT `athletes`.* FROM `events`
INNER JOIN `sets` ON `sets`.`EventID` = `events`.`EventID`
INNER JOIN `timetables` ON `timetables`.`SetID` = `sets`.`SetID`
INNER JOIN `times` ON `times`.`TableID` = `timetables`.`TableID`
INNER JOIN `athletes` ON `athletes`.`ID` = `times`.`AthleteID`
WHERE `events`.`EventID` = #evID
AND `times`.`TimeID` IN(
SELECT MIN(`TimeID`)
FROM `times`
WHERE `TableID` IN(
SELECT `TableID`
FROM `timetables`
WHERE `SetID` IN(
SELECT `SetID`
FROM `sets`
WHERE `EventID` = #evID
)
)
GROUP BY `AthleteID`
)";
Every single time I ran that in phpmyadmin it returned all the athletes, and the data was correct. However, when I run it in my script, the query value is false (such as if there is an error). I tried debugging like this:
$r = $db -> query($q);
var_dump($q);
var_dump($r);
var_dump($db->error);
The query is returned just fine (only difference is lack of newline characters), and when I copy what's returned in phpmyadmin the data is just the same. The rest however:
bool(false)
string(228) "You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'SELECT `athletes`.* FROM `events` INNER JOIN `sets` ON `sets`.`EventID` = `...' at line 1"
Other users with the same problem have not really gone that far to find out if they're wrong, but I have. This post is not a duplicate, and I didn't find any solutions online. Could this be a problem with the amount of queries in a single string? (There is one for setting #evID and one for the actual selection). Please explain the solution and methods kindly as I'm only 13 and still learning...
As #NigelRen has suggested, please use parameterized prepared statement.
Assuming that
$sessionid is storing the value for EventID, and assuming that this variable is of integer type; and
$conn is the connection
Then for Mysqli, you can use:
//$q = "SET #evID = " . $method['sessID'] . ";";
$sql = "SELECT `athletes`.* FROM `events`
INNER JOIN `sets` ON `sets`.`EventID` = `events`.`EventID`
INNER JOIN `timetables` ON `timetables`.`SetID` = `sets`.`SetID`
INNER JOIN `times` ON `times`.`TableID` = `timetables`.`TableID`
INNER JOIN `athletes` ON `athletes`.`ID` = `times`.`AthleteID`
WHERE `events`.`EventID` = ?
AND `times`.`TimeID` IN(
SELECT MIN(`TimeID`)
FROM `times`
WHERE `TableID` IN(
SELECT `TableID`
FROM `timetables`
WHERE `SetID` IN(
SELECT `SetID`
FROM `sets`
WHERE `EventID` = ?
)
)
GROUP BY `AthleteID`
)";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ii", $sessionid, $sessionid);
$stmt->execute();
$result = $stmt->get_result(); // get the mysqli result
$row = $result->fetch_assoc(); // fetch data
// do other things you want , such as echo $row['fieldname1'];

How to find out how many rows match a rule?

I want to find out how many rows in a table of my database meet a certain rule, specifically that "category" matches another variable and "published" is today or earlier. Then I want to simply echo that number.
I have no idea how to go about doing this, though.
I thought I could use count() but I'm not sure how to call just a single column and put the results of each row in an array.
Thanks!
Do this using SQL:
Try this in your database (your columns/tables may be different):
SELECT count(*) FROM blog_posts WHERE category = 'hot_stuff' and published <= NOW();
Then to execute this in PHP, depending on your framework and connection to the database:
$myCategory = 'hot_stuff';
$myTable = 'blog_posts';
$sql = "SELECT count(*) FROM {$myTable} WHERE category = '{$myCategory}' and published <= NOW();";
$rowCount = $db->query($sql);
echo $rowCount;
Connect to your database.
$pdo = new PDO($dsn, $user, $password);
Create a prepared statement. This is essential because you need to pass a value for category from your application to the query. It is not necessary to pass a value for the current date because the database can provide that for you. The ? is a placeholder where the parameter you pass will be bound.
$stmt = $pdo->prepare("SELECT COUNT(*) FROM your_table
WHERE category = ? AND published <= CURDATE()");
Do not concatenate the parameter into your SQL string (like WHERE category = '$category') because this will create an SQL injection vulnerability.
Execute the prepared statement using your specified value for category.
$stmt->execute([$category]); // assuming you have already defined $category
Use fetchColumn to return a single value, the count of rows that matched your criteria.
echo $stmt->fetchColumn();

Select data from multiple tables from mysql?

I have 4 tables in database and an unique clientid is common in all tables but rest field are different. if we search for any client id , how can we get the information stored corresponding to the searched client id from the any table.
$clientid=$_POST['client'];
$query = "SELECT * FROM 'pfs'
JOIN 'pfssurety'
JOIN 'iso'
JOIN 'incometax'
WHERE clientid='$clientid'";
$result = mysql_query($query)or die (mysql_error());
while($row=mysql_fetch_array($result)) {
echo $row['clientid'];
echo $row['name'];
}
Here is an implementation using mysqli and prevents injection for your $clientid where the id is a number grate then zero as AUTO_INCREMENT columns will never have a 0 value they start at 1
// as this is an int using inval will force it to be a valid whole number
// basic SQL Injection Protection for a fixed id
$clientid = intval($_POST['client']);
if($clientid === 0){
// it was not a valid number as an auto_increment field in mysql can never be 0
die("invalid client");
}
$query="SELECT * FROM `pfs`
JOIN `pfssurety` ON pfssurety.clientid = pfs.clientid
JOIN `iso` ON iso.clientid = pfs.clientid
JOIN `incometax` ON incometax.clientid = pfs.clientid
WHERE pfs.clientid=$clientid";
$result= mysqi_query($query) or die("Query Failed");
while($row=mysql_fetch_array($result))
{
echo $row['clientid'];
echo $row['name'];
}
You meant to use backtique instead of single quote. Otherwise your table names are considered as normal string literal. Also, you are missing ON condition for all your JOIN clauses So your query
SELECT * FROM 'pfs'
Should actually be
SELECT * FROM `pfs`
Change your query to be
SELECT * FROM `pfs`
JOIN `pfssurety` ON condition
JOIN `iso` ON condition
JOIN `incometax` ON condition
WHERE clientid='$clientid'
Maybe you need to set the on in your join as something like this:
SELECT * FROM 'pfs'
JOIN 'pfssurety' ON pfs.clientid=pfssurety.clientid
JOIN 'iso' ON pfs.clientid=iso.clientid
JOIN 'incometax' ON pfs.clientid=incometax.clientid
WHERE clientid='$clientid'
Y suposed that all tables have the clientid attribute.
Instead of using the SELECT * you could use the enumeration of the different attributes you need
You are on the right track using JOIN. You need to specify the common column that the JOIN should be made on.
https://www.w3schools.com/sql/sql_join_left.asp
SELECT a.fieldname, i.fieldname, t.fieldname, p.fieldname FROM 'pfs' as a
LEFT JOIN 'pfssurety' as p ON p.clientid = a.clientid
LEFT JOIN 'iso' as i ON i.clientid = a.clientid
LFT JOIN 'incometax' as t ON t.clientid = a.clientid
WHERE a.clientid='$clientid'";
Also, you should escape your variables to prevent SQL Injection. At a minimum:
a.clientid= "' . mysqli_real_escape_string($clientid) . '"';
http://php.net/manual/en/mysqli.real-escape-string.php
Your code is vulnerable to SQL Injection, you should never use user input directly into your SQL queries. In your code the problem is here:
$clientid = $_POST['client']; // anyone could manipulate this field to inject malicious code
# ...
WHERE clientid='$clientid'";
Check what happens if the value for $_POST['client'] is: ' or 1 = 1;
Next as mentioned in one of the comments stop using deprecated methods, instead for example you can use mysqli. Here is an example of how to use mysqli with prepared statements to avoid SQL Injection:
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
$stmt = $conn->prepare('SELECT * FROM `pfs` JOIN `pfssurety` ON condition JOIN `iso` ON condition JOIN `incometax` ON condition WHERE clientid = ?');
$stmt->bind_param('i', $clientid);
$stmt->execute();
$stmt->close();
$conn->close();
A prepared statement is a feature used to execute the same (or similar) SQL statements repeatedly with high efficiency.
Compared to executing SQL statements directly, prepared statements have three main advantages:
Prepared statements reduces parsing time as the preparation on the query is done only once (although the statement is executed multiple times)
Bound parameters minimize bandwidth to the server as you need send only the parameters each time, and not the whole query
Prepared statements are very useful against SQL injections, because parameter values, which are transmitted later using a different protocol, need not be correctly escaped. If the original statement template is not derived from external input, SQL injection cannot occur.
Finally one more thing worth mentioning, try not using * to fetch all columns, instead simply list the columns you need to get. Even if you need to get all columns there are good reasons why not to use *, but instead list all columns.

mysqli and passing a query as parameter - no results

This is my code:
$sql = $_POST['sql'];
....
$result = $mysqli->query($sql);
This does not return any results. So i echoed the $sql variable and this is the result:
SELECT o.entity_id, o.increment_id FROM sales_flat_order o JOIN sales_flat_order_payment p ON o.entity_id = p.parent_id JOIN sales_flat_order_address a ON o.entity_id = a.parent_id WHERE a.country_id = \'DE\' ORDER BY o.entity_id DESC LIMIT 10;
Now, when I assign this to the $sql variable directly, it works. What could be the problem?
Thanks
Well, first you could test $result and output the last error with $mysqli->error when it's false, that would give you details on what's wrong.
Secondly, you should NOT execute a query that's coming from POST or GET parameter, that's how you allow anyone to do anything on your database with sql injection. That's a big security breach.
Thirdly, the issue is probably on POST encoding (note the quotes \'DE\') so if you urldecode and/or stripslashes your $sql it would probably work

PHP PDO Query returning an empty array when using bindParam

Kind of stumped by this one. I have been developing for about a week now, maybe two so it might be a noob mistake, but here is what I have:
<?php
$msDB = new PDO('odbc:Driver={SQL Server Native Client 11.0};Server=SOMESERVER;Trusted_Connection=yes;');
try{
//set values for query
$s="4";
$d1="'2014-10-01 00:00:00'";
$d2="'2014-10-31 23:59:59'";
//create query variable
$q1 = "SELECT ID FROM SURVEY_QUESTION_RESPONSE AS t1 WHERE EXISTS
(SELECT * FROM SURVEY_RESPONSE AS tN
WHERE (tN.ID = t1.SURVEY_RESPONSE_ID)
AND (t1.SELECTION = :s)
AND (tN.RESPONSE_DATE BETWEEN :d1 AND :d2))";
//run prepare and bindParam
$tbe = $msDB->prepare($q1);
$tbe->bindParam(':s',$s, PDO::PARAM_INT);
$tbe->bindParam(':d1',$d1, PDO::PARAM_STR);
$tbe->bindParam(':d2',$d2, PDO::PARAM_STR);
//execute query
$tbe->execute();
//fetch resulting data
$res = $tbe->fetchAll(PDO::FETCH_ASSOC);}
//error handling
catch (PDOException $e) {
throw new pdoDbException($e);
}
//print the resulting array
print_r($res);
//set initial count
$cnt=0;
//loop through and increment count
foreach($res as $key=>$value){
foreach($value as $v2 ){
$cnt++;
}
}
//return count value
echo "Total:<br/>".$cnt."<br/>";
?>
I am expecting this to return a result set of the number 3. And when I specify the values in the query manually, everything works as expected and it returns the number 3.
If I however use the bindParam method it returns nothing and throws no errors of any sort. It simply returns an empty array.
I can also break up the query set in $q1 and concatenate the values into it, and it also works flawlessly. I have not really used bindParam before, but as far as I can tell, I am using it correctly.
Works:
//create query variable
$q1 = "SELECT ID FROM SURVEY_QUESTION_RESPONSE AS t1 WHERE EXISTS
(SELECT * FROM SURVEY_RESPONSE AS tN
WHERE (tN.ID = t1.SURVEY_RESPONSE_ID)
AND (t1.SELECTION = ".$s.")
AND (tN.RESPONSE_DATE BETWEEN ".$d1." AND ".$d2."))";
When I run the query in MSSQL Server Management Studio, it also returns the result set I expect.
Can anyone tell me what I am doing wrong?
The default data type passed by PDO::bindParam and PDO::bindValue is ALWAYS text. Conversion from datatype text in MSSQL is only possible to CHAR, VARCHAR, NCHAR, and NVARCHAR. It is because of the datatype issue that the value has to be converted from text into CHAR or VARCHAR and then into DATETIME from there. This is however an implicit conversion, and depending on the value passed to the query, may result in rounding errors, truncation, or simply a failed conversion.
This, does NOT work:
//create query variable
$q1 = "SELECT ID FROM SURVEY_QUESTION_RESPONSE AS t1 WHERE EXISTS
(SELECT * FROM SURVEY_RESPONSE AS tN
WHERE (tN.ID = t1.SURVEY_RESPONSE_ID)
AND (tN.RESPONSE_DATE BETWEEN :d1 AND :d2))";
//run prepare and bindParam
$tbe = $msDB->prepare($q1);
$tbe->bindParam(':d1',$d1, PDO::PARAM_INT);
$tbe->bindParam(':d2',$d2, PDO::PARAM_INT);
This however, does work:
$q1 = 'SELECT ID FROM SURVEY_QUESTION_RESPONSE AS t1 WHERE EXISTS
(SELECT * FROM SURVEY_RESPONSE AS tN
WHERE (tN.ID = t1.SURVEY_RESPONSE_ID)
AND (tN.RESPONSE_DATE BETWEEN CONVERT(datetime,CONVERT(VARCHAR(MAX),?))
AND CONVERT(datetime,CONVERT(VARCHAR(MAX),?))))';
//run prepare and bindParam
$tbe = $msDB->prepare($q1);
$tbe->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
$tbe->bindParam(1,$d1);
$tbe->bindParam(2,$d2);
Even if I casted the bound parameters as type INT, they were still passed to MSSQL as text causing the failure.
It would be my suggestion however to simply use the original workaround of just passing in the variables directly like so and have the variables be double quoted strings:
$q1 = "SELECT ID FROM SURVEY_QUESTION_RESPONSE AS t1 WHERE EXISTS
(SELECT * FROM SURVEY_RESPONSE AS tN
WHERE (tN.ID = t1.SURVEY_RESPONSE_ID)
AND (t1.SELECTION = '$s')
AND (tN.RESPONSE_DATE BETWEEN '$date1' AND '$date2'))";
The quoted string is much easier to handle, and far less prone to error because it need not be converted at all and is simply passed right along to MSSQL Server without issue.
I noticed a slight performance hit with the converts because of the extra processing by MSSQL Server. The query took about half a second longer to run with the conversion than without.
Cheers to #meda for helping with the resolution!
I get this error today and take a lot of my hours.
After upgrading PHP Version to PHP7, using sqlsrv:database driver instead of dblib:dbname make this problem occurs.
To avoid this, still using dblib:dbname event PHP7 FPM already support with sqlsrv:database driver.

Categories