I want to count how many rows in my MySQL database have two certain values. My table is set up like this:
|---------------------|
| ids |
|---------------------|
|source_id | target_id|
|----------|----------|
| 2 | 6|
| 2 | 6|
| 3 | 4|
|---------------------|
I want to count how many rows have the source_id = 2 and target_id = 6
I tried this statement:
<?php
$prep_stmt = "SELECT source_id FROM ids WHERE source_id = 2 AND target_id = 6";
if (!$result = $mysqli->query($prep_stmt)) {
die("Failed");
} else {
$num_rows = $result->num_rows;
echo $num_rows;
}
?>
However, the PHP file ceases to function after the third line.
Your code looks a bit weird. If you want to use prepared statements, that's working totally differentely:
<?php
$stmt = $mysqli->prepare("SELECT COUNT(*) FROM `ids` WHERE `source_id` = ? AND `target_id` = ?");
$stmt->bind_param("ii", $source_id, $target_id);
$stmt->execute();
$stmt->bind_result($count);
$stmt->fetch();
$stmt->close();
echo $count;
And without prepared statements.
<?php
echo $mysqli->query("SELECT COUNT(*) FROM `ids` WHERE `source_id` = 2 AND `target_id` = 6");
And as a last note, if you asign anything within a condition be sure to enclose it in brackets:
<?php
function fn() {
return "something";
}
if (($foo = fn())) {
// The condition is true if $foo isset, or in other words not null after the function was called.
}
if (!($foo = fn())) {}
if (($foo = fn()) === null) {}
// ...
SELECT COUNT(*) FROM ids WHERE source_id=2 AND target_id=6
SELECT COUNT(*) FROM ids WHERE source_id = 2 AND target_id = 6";
will give you the number of entries corresponding to what you want.
(it will give one row with 1 column, containing the number of lines corresponding to the where close)
Related
I am a newbie to PHP and I am stuck at a certain point. I tried looking up a solution for it however, I didn't find exactly what I need.
My goal is to create a leaderboard, in which the values are displayed in descending order plus the rank and score are displayed. Furthermore, it should also display whether or not a tie is present.
The database should look like this:
+---------+------+----------------+-------+------+
| user_id | name | email | score | tied |
+---------+------+----------------+-------+------+
| 1 | SB | sb#gmail.com | 1 | 0 |
+---------+------+----------------+-------+------+
| 2 | AS | as#web.de | 2 | 0 |
+---------+------+----------------+-------+------+
| 3 | BR | br#yahoo.com | 5 | 1 |
+---------+------+----------------+-------+------+
| 4 | PJ | pj#gmail.com | 5 | 1 |
+---------+------+----------------+-------+------+
And the outputted table should look something like this:
+------+-------------+-------+------+
| rank | participant | score | tied |
+------+-------------+-------+------+
| 1 | BR | 5 | Yes |
+------+-------------+-------+------+
| 2 | PJ | 5 | Yes |
+------+-------------+-------+------+
| 3 | AS | 2 | No |
+------+-------------+-------+------+
| 4 | SB | 1 | No |
+------+-------------+-------+------+
I managed to display the rank, participant and the score in the right order. However, I can't bring the tied column to work in the way I want it to. It should change the value, whenever two rows (don't) have the same value.
The table is constructed by creating the <table> and the <thead> in usual html but the <tbody> is created by requiring a php file that creates the table content dynamically.
As one can see in the createTable code I tried to solve this problem by comparing the current row to the previous one. However, this approach only ended in me getting a syntax error. My thought on that would be that I cannot use a php variable in a SQL Query, moreover my knowledge doesn't exceed far enough to fix the problem myself. I didn't find a solution for that by researching as well.
My other concern with that approach would be that it doesn't check all values against all values. It only checks one to the previous one, so it doesn't compare the first one with the third one for example.
My question would be how I could accomplish the task with my approach or, if my approach was completely wrong, how I could come to a solution on another route.
index.php
<table class="table table-hover" id="test">
<thead>
<tr>
<th>Rank</th>
<th>Participant</th>
<th>Score</th>
<th>Tied</th>
</tr>
</thead>
<tbody>
<?php
require("./php/createTable.php");
?>
</tbody>
</table>
createTable.php
<?php
// Connection
$conn = new mysqli('localhost', 'root', '', 'ax');
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// SQL Query
$sql = "SELECT * FROM names ORDER BY score DESC";
$result = $conn->query("$sql");
// Initalizing of variables
$count = 1;
$previous = '';
while($row = mysqli_fetch_array($result)) {
$current = $row['score'];
$index = $result['user_id']
if ($current == $previous) {
$update = "UPDATE names SET tied=0 WHERE user_id=$index";
$conn->query($update);
}
$previous = $current;
?>
<tr>
<td>
<?php
echo $count;
$count++;
?>
</td>
<td><?php echo $row['name'];?></td>
<td><?php echo $row['score'];?></td>
<td>
<?php
if ($row['tied'] == 0) {
echo 'No';
} else{
echo 'Yes';
}
?>
</td>
</tr>
<?php
}
?>
I think the problem is here
$index = $result['user_id'];
it should be
$index = $row['user_id'];
after updating tied you should retrieve it again from database
So I solved my question by myself, by coming up with a different approach.
First of all I deleted this part:
$current = $row['score'];
$index = $result['user_id']
if ($current == $previous) {
$update = "UPDATE names SET tied=0 WHERE user_id=$index";
$conn->query($update);
}
$previous = $current;
and the previous variable.
My new approach saves the whole table in a new array, gets the duplicate values with the array_count_values() method, proceeds to get the keys with the array_keys() method and updates the database via a SQL Query.
This is the code for the changed part:
// SQL Query
$sql = "SELECT * FROM names ORDER BY score DESC";
$result = $conn->query("$sql");
$query = "SELECT * FROM names ORDER BY score DESC";
$sol = $conn->query("$query");
// initalizing of variables
$count = 1;
$data = array();
// inputs table into an array
while($rows = mysqli_fetch_array($sol)) {
$data[$rows['user_id']] = $rows['score'];
}
// -- Tied Column Sort --
// counts duplicates
$cnt_array = array_count_values($data);
// sets true (1) or false (0) in helper-array ($dup)
$dup = array();
foreach($cnt_array as $key=>$val){
if($val == 1){
$dup[$key] = 0;
}
else{
$dup[$key] = 1;
}
}
// gets keys of duplicates (array_keys()) and updates database accordingly ($update query)
foreach($dup as $key => $val){
if ($val == 1) {
$temp = array_keys($data, $key);
foreach($temp as $k => $v){
$update = "UPDATE names SET tied=1 WHERE user_id=$v";
$conn->query($update);
}
} else{
$temp = array_keys($data, $k);
foreach($temp as $k => $v){
$update = "UPDATE names SET tied=0 WHERE user_id=$v";
$conn->query($update);
}
}
}
Thank you all for answering and helping me get to the solution.
instead of the update code you've got use something simular
$query = "select score, count(*) as c from names group by score having c > 1";
then you will have the scores which have a tie, update the records with these scores and your done. Make sure to set tie to 0 at first for all rows and then run this solution
UPDATE for an even faster solution sql based:
First reset the database:
$update = "UPDATE names SET tied=0";
$conn->query($update);
All records have a tied = 0 value now. Next update all the records which have a tie
$update = "update docs set tied = 1 where score IN (
select score from docs
group by score having count(*) > 1)";
$conn->query($update);
All records with a tie now have tied = 1 as we select all scores which have two or more records and update all the records with those scores.
I have been scratching my head for a very long time about this PHP code. I am trying to achieve something like
->Get each status
->Get each user in user's friends list
->Display status' from each user that is in the user's friends list
and repeat until there is no more. I have been looking for a solution for more a few days and it is really bugging me. Here is the code I tried:
EDIT: posted schema as requested
https://kjf-tech.net/files/schema.png
<?php
$connect = new MySQLi($DBhost,$DBuser,$DBpass,$DBname);
$querya = "SELECT * FROM statuses ORDER BY `id` DESC";
$result = mysqli_query($connect, $querya);
$ALLDATA = array();
$DBcon2 = new MySQLi($DBhost,$DBuser,$DBpass,$DBname);
if ($DBcon2->connect_errno) {
die("ERROR : -> ".$DBcon2->connect_error);
}
while ($record = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
array_push($ALLDATA, $record);
$queryb = "SELECT * FROM friendslist WHERE idOfPerson1='".$record['idOfUser']."' OR idOfPerson2='".$record['idOfUser']."' OR idOfPerson2='".$userRow['user_id']."' OR idOfPerson1='".$userRow['user_id']."' ORDER BY `id` DESC";
$result2 = mysqli_query($connect, $queryb);
$ALLDATA2 = array();
while ($record2 = mysqli_fetch_array($result2, MYSQLI_ASSOC)) {
array_push($ALLDATA2, $record2);
if($record['idOfUser'] == $userRow['user_id']) {
echo '<div>You Posted on '.$record['whenPosted'].'<br />'.$record['content'].'</div>';
}
elseif($record2['idOfPerson1'] == $userRow['user_id']) {
$query2 = $DBcon2->query("SELECT * FROM tbl_users WHERE user_id='".$record2['idOfPerson2']."'");
$userRow2=$query2->fetch_array();
echo '<div>'.$userRow2['username'].' Posted on '.$record['whenPosted'].'<br />'.$record['content'].'</div>';
}
elseif($record2['idOfPerson2'] == $userRow['user_id']) {
$query2 = $DBcon2->query("SELECT * FROM tbl_users WHERE user_id='".$record2['idOfPerson1']."'");
$userRow2=$query2->fetch_array();
echo '<div>'.$userRow2['username'].' Posted on '.$record['whenPosted'].'<br />'.$record['content'].'</div>';
}
}
mysqli_free_result($result2);
}
$DBcon2->close();
mysqli_free_result($result);
?>
Your schema looks good, but let's take another look at the relations. I'm going to rename some of the columns for convenience.
Users:
+- user_id
| user_name
|
|
| Friendships:
| fid
+- user_id
| friend_id --------+
| friendship_start |
| |
| |
| Statuses: |
| sid |
+- user_id ----------+
post_date
content
If you just wanted to find statuses of your friends, the query would look thus:
SELECT statuses.content
FROM friendships, statuses
WHERE friendship.user_id=? AND
friendships.friend_id = statuses.user_id
You would, of course, bind the appropriate user_id value when you ->prepare() the statement.
(See http://php.net/manual/en/mysqli.prepare.php for the proper way to do sql. You don't ever want to do something like mysql_query("select * from table where id=".$_POST['id']) because it's open to SQL injection)
Unfortunately, though, this does not include your own status in the query results. We'll have to do a little more work on the query...
SELECT statuses.content
FROM friendships, statuses
WHERE
( friends.user_id = ? AND
friends.friend_id = stuatuses.user_id )
OR
statuses.user_id = ?
ORDER BY statuses.post_date DESC
So far, so good... but we don't have the names nor the post date. The post date is easy, just add that to the select:
SELECT statuses.content, statuses.post_date
To add the name, we have to get data from Users also.
SELECT users.user_name, statuses.content, statuses.post_date
FROM users, friendships, statuses
WHERE
users.user_id = ? AND
(
( users.user_id = friendships.user_id AND
friendships.friend_id = statuses.user_id )
OR
statuses.user_id = users.user_id
)
ORDER BY statuses.post_date DESC
And there you have it; the database does all the work for you. No need for nested queries and such. This will just give you the simple list to print on your page. Please keep in mind that this is off the top of my head, so you may have to tweak it if I overlooked something.
I'm trying to return a MySQL query using GROUP BY as a array.
My table looks like this:
user_id | type
--------------------
1 | test
1 | test
2 | test
1 | hello
1 | helloworld
And my code and query:
$number_of_workouts = array();
$query = mysqli_query(connect(),"SELECT type, COUNT(*) FROM `log` WHERE `user_id` = $user_id GROUP BY type");
$number_of_workouts = mysqli_fetch_assoc($query);
return $number_of_workouts;
This code above isn't returning a array with all types listed, it will only return the number of one type(assuming that $user_id = 1.
How can I return the result of the query as an array?
mysqli_fetch_assoc($query);
fetches 1 row only from the resultset
If you want ALL rows from the resultset, you have to loop for each row and add it to a stack like:
$number_of_workouts = array();
$query = mysqli_query(connect(),
"SELECT type, COUNT(*) AS count
FROM `log`
WHERE `user_id` = $user_id
GROUP BY type"
);
$array = array();
while ($number_of_workouts = mysqli_fetch_assoc($query)) {
$array[$number_of_workouts['type']] = $number_of_workouts['count'];
}
// Now you have all results like you want in the variable array:
print_r($array);
// print count of type test:
echo $array['test'];
Or you try out mysqli_fetch_all() (http://www.php.net/manual/en/mysqli-result.fetch-all.php)
(sorry for many updates)
You are fetching only first record here. Keep the fetching statement in while loop
while($number_of_workouts = mysqli_fetch_assoc($query))
{
echo "<pre>";
print_r($number_of_workouts);
}
Table books:
subject | userid | flag
---------+----------+--------
math | abc | 0
math | abc | 0
english | xyz | 0
I wanna search the number of math subject where userid and flag = 0.
How should I write the SQL statement?
I tried COUNT(subject) AS math FROM books WHERE userid = 'abc' AND flag = '0';
It doesn't work.
If you want to get only the math subjects with userid = 'abc' AND flag = '0', you can use below query:
SELECT COUNT(subject) AS math
FROM books WHERE userid = 'abc' AND flag = '0' AND subject = 'math';
check out the SQLFiddle
otherwise if you only want subjects with userid = 'abc' AND flag = '0', the query given by you is perfectly doing that:
SELECT COUNT(subject) AS math
FROM books WHERE userid = 'abc' AND flag = '0';
check out the SQLFiddle
For displaying in php using mysql_fetch_assoc you can give the column name to access the value like this:
$sql = "SELECT COUNT(subject) AS math
FROM books WHERE userid = 'abc' AND flag = '0'";
$result = mysql_query($sql);
if (!$result) {
echo "Could not successfully run query ($sql) from DB: " . mysql_error();
exit;
}
if (mysql_num_rows($result) == 0) {
echo "No math subjects exist";
exit;
}
while ($row = mysql_fetch_assoc($result)) {
echo $row["math"];
}
Maybe this:
SELECT COUNT(subject) AS math
FROM books
WHERE flag = '0'
GROUP BY userid
HAVING userid = 'abc';
I'm trying out my first recursive function (at least I think I am!) and it only half works. First, the code:
function check_title($i,$title) {
$q=mysql_query("SELECT Title FROM posts WHERE Title = '$title'");
$num=mysql_num_rows($q);
if($num==0) {
return $title;
}else {
$title=$title.' ('.$i++.')';
check_title($i,$title);
}
}
What I'm doing is taking a string (title) and checking if that title exists in the db already. If it does, I want to append a number to the newer of the duplicates (e.g. 'I Am A Title' becomes 'I Am A Title-2'). I then need to run the function again to check this new version of my title, and increase the appended value as required ('I Am A Title-3'). Once no duplication is discovered, return the Title in its acceptable form.
It works when no duplication is found (the easy bit), but fails when duplication is found. Instead of appending a number, the entire title variable is emptied.
Any help would by greatly appreciated!
As Mchl stated, the empty title is due to a lack of return in the else branch.
However, there is a problem with the function as it does not do what you intend. Currently, your function is building $title as 'Title-1-2-3-4-etc' the way you currently append the number to the title and check again. Instead of passing a modified title on the recursed call you should just pass the base title. Then, for the query, modify the title.
function check_title($title, $i = 0) {
$qtitle = $title . ($i == 0 ? '' : "-$i");
$q=mysql_query("SELECT Title FROM posts WHERE Title = '$qtitle'");
$num=mysql_num_rows($q);
if($num==0) {
return $title . ($i == 0 ? '' : "-$i");
}else {
return check_title(++$i,$title);
}
}
PS, I also changed the order of parameters that way your initial call doesn't need to specify 0.
$title = check_title($title);
PPS, I should mention this is a solution to do it via recursion. However, a recursive solution is not the proper solution here as it needlessly makes return trips to the DB. Instead, you should use an sql query that selects all titles LIKE "$title%" Order by title asc. Then, iterate through each result and do a regex comparison with the title to see if it matches a pattern <title>|<title>-<#>. If it does you increment a duplicate counter. At the end you spit out the title with an appended counter value. I'll leave that solution as an exercise for the original poster.
Use a loop instead...
$record_exists = true;
$title_base = "I Am A Title";
$title = $title_base;
$i = 0;
while($record_exists) {
$q=mysql_query("SELECT Title FROM posts WHERE Title = '$title'");
$num=mysql_num_rows($q);
if($num==0) {
$record_exists = false;
// Exit the loop.
}
else {
$i++;
$title = $title_base . "-" . $i;
}
}
echo $title; // last existing title
However, optimally you'd do more work with a single SQL query and iterate the result, saving a lot of trips to and from the database.
And just for fun...
$title_base = "I Am A Title";
$title = $title_base;
for ($i=1, $num=1; $num != 0; $i++)
{
$q=mysql_query("SELECT Title FROM posts WHERE Title = '$title'");
$num=mysql_num_rows($q);
$title = $title_base . "-" . $i;
}
echo $title; // next title in sequence (doesn't yet exist in the db)
You lack a return in else branch.
Recursion is not the best idea for this application. Hint: do a query like this SELECT MAX(Title) FROM posts WHERE Title LIKE '$title%');
Your recursive function is fine except for 2 things:
The original title isn't maintained between recursive calls. Hence each time $title = $title . ' (' . $i++ . ')' runs, another parenthesis is appended to the title, like "abc", "abc (1)", "abc (1) (2)" and so on.
You are returning $title when no more matches are found but no title is returned in the ELSE. It is important to do so. When the execution reaches the IF, it returns the title but the returned title is not assigned anywhere and hence is lost.
Here is the revised code:
$orgTitle = 'I am a title';
function check_title($i, $title = '') {
global $orgTitle;
$q = mysql_query("SELECT Title FROM posts WHERE Title = '$title'");
$num = mysql_num_rows($q);
if ($num == 0) {
return $title;
} else {
$title = $orgTitle . ' (' . ++$i .')';
return check_title($i, $title);
}
}
echo check_title(0, $orgTitle);
Note the addition of new variable $orgTitle. I've replaced it in the assignment statement inside the ELSE. This does the fix for point 1 above.
Also note the return added before check_title call in the ELSE. This solves point 2.
Hope it makes sense!
Add-on: Recursions are confusing, logically complex and tricky to debug. Also, recursive calls consume more memory (not in case of simple operations like your example) because the compiler/interpreter had to maintain the state variables for all steps in a recursion.
In order to minimise MySql interactions I'd recommend something similar to the following.
function checkTitle($title)
{
/*Return all iterations of the title*/
$res = mysql_query("SELECT COUNT(title) AS titleCount FROM posts
WHERE SUBSTR(title, 1,". strlen($title) .") = '$title' ");
/*Return the incremented title*/
return $title. (mysql_result($res, 0, "titleCount") + 1);
}
Example:
mysql> select title from posts;
+----------+
| title |
+----------+
| firefox1 |
| firefox2 |
| shoe |
| firefox3 |
+----------+
4 rows in set (0.00 sec)
mysql> SELECT COUNT(title) AS titleCount FROM posts WHERE SUBSTR(title, 1,7) = 'firefox' ;
+------------+
| titleCount |
+------------+
| 3 |
+------------+
1 row in set (0.00 sec)
mysql>
---- Follow up test
Test table structure.
mysql>SHOW COLUMNS FROM posts;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| title | varchar(12) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
/*Test code and output*/
function checkTitle($title)
{
/*Return all iterations of the title*/
$res = mysql_query("SELECT COUNT(title) AS titleCount FROM posts
WHERE SUBSTR(title, 1,". strlen($title) .") = '$title' ");
/*Return the incremented title*/
return $title. (mysql_result($res, 0, "titleCount") + 1);
}
mysql_connect("localhost","root", "password");
mysql_select_db("test");
echo checkTitle("firefox");
Output: firefox4