PHP '5 out of 10' Helpfulness counter question - php

I have been searching the web and stackoverflow for 60 minutes but have yet to find a solution/tutorial on here or the web explaining how I can do an 45 out of 50 helpful system in an efficient manner!
Better explanation: I'm experimenting with PHP and am trying to make a helpful system i.e. '5 out of 10 found this article helpful' but am struggling to figure out the best way to approach it and I feel my way is a bit bulky and their might be simpler ways!
My table:
| id | user_id | opinion | type | type_id | ip | date
---------------------------------------------
1 2 1 1 1 ::1 dd/mm/yy
My Way:
My way is count all of the data for this type_id to get the 'out of total' and then to get the amount look for the '1' (yes) and count all of those! I'm just wondering if there is a faster way instead of me doing two count functions. (all about optimising)
My Current Count Code to get the outcome I want is this:
public function getTotalHelpfulness($id)
{
$bind = [':id' => $id];
$results = $this->db->select('helpful','type = 1 AND type_id = :id', $bind);
$totalRows = count($results);
if($results === FALSE){ $totalRows = '0'; }
return $totalRows;
}
public function getYesHelpfulness($id)
{
$bind = [':id' => $id];
$results = $this->db->select('helpful',
'opinion = 1 AND type = 1 AND type_id = :id', $bind);
$totalRows = count($results);
if($results === FALSE){ $totalRows = '0'; }
return $totalRows;
}
Thanks

Just my two cents since I see no other answers. In plain SQL I would do:
select
sum(case when opinion = 1 then 1 else 0 end) as positive_votes,
count(*) as total
from my_table
where type = 1 and type_id = :id;
This query performs a single run over all rows and generates two counts: positive votes, and total votes.

Related

Limit number of results using column value as a count

I have two queries, one which will get a list of IDs from the main table, and another which will get all records in another table that relate to that ID.
I'd like to split it into pages of results, but my google searches are only coming up with people who want to have a certain number of results per column value, not who want to limit the overall count by it.
The main table has a column that contains the number of records, so actually reading the other table isn't needed. The limit would be used as a minimum value, so if there are still records left in the current group after the limit, it'll continue displaying them. That'd be somehow calculated as part of the offset so it can start in the correct place.
Here's an example of what I mean:
Table 1:
ID | Records
1 | 2
2 | 3
3 | 28
4 | 7
...
Table 2 (contents don't need to be known for this question):
ID | GroupID | Value
1 | 1 | x
2 | 1 | x
3 | 2 | x
4 | 2 | x
5 | 2 | x
6 | 3 | x
...
If the limit was given as 3 for example, both 1 and 2 should display on the first page, since just 1 by itself is under the limit. The next page will then start on 3, and that'll take up the entire page.
I could just manually count up using PHP until I reach the limit, though it might end up going slow if there were a lot of pages (I've no idea if mysql would be any better in that regard though). Here's a quick example of how that'd work to get the offset:
$page = 2;
$limit = 40;
$count = 0;
$current_page = 1;
$query = 'SELECT ID, Records FROM table1';
$stmt = $conn->prepare($query);
$stmt->execute();
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
$count += $row['Records'];
if($count > $limit){
$current_page ++;
$count = 0;
if($current_page == $page){
$start_id = $row['ID'];
break;
}
}
}
Updated:
A "custom" Pagination is what you're looking for. So if you're planning to hop from page to page, you can't use hardcoded $page and $current_page values. Those values should be generated dynamically once you're on a particular page. In fact, you should have the ID column value in the query part of the URL so that the pagination links could satisfy your business logic.
Assuming the fact that your ID column value starts from 1, your code should be like this:
$id = isset($_GET['id']) && is_numeric($_GET['id']) ? $_GET['id'] : 1;
$limit = 40;
// Display query results based on particular ID number
$query = 'SELECT ID, Records FROM table1 WHERE ID >= ' . $id;
$stmt = $conn->prepare($query);
$stmt->execute();
$rowCount = $stmt->rowCount();
if($rowCount){
$total = 0;
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
if($total <= $limit){
$total += $row['Records'];
// Display $row details here
}else{
// Get the next $id value
$id = $row['ID'];
break;
}
}
}
// Display relevant ID links
$query = 'SELECT * FROM table1';
$stmt = $conn->prepare($query);
$stmt->execute();
$idArr = array();
$total = 0;
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
if($total == 0){
$idArr[] = $row['ID'];
}
if($total <= $limit){
$total += $row['Records'];
}else{
$total = 0;
}
}
foreach($idArr as $idValue){
if($idValue == $id){
echo '<a>'.$idValue.'</a> ';
}else{
echo ''.$idValue.' ';
}
}
Sidenote: If the ID column value of your table doesn't start from 1, then use a separate query to get the first ID value and update the following statement,
$id = isset($_GET['id']) && is_numeric($_GET['id']) ? $_GET['id'] : <FIRST_ID_VALUE>;
If you only need to create the "Next" button, you could try this way:
select t1.ID, t1.Records
from table1 t1
left join table1 t2
on t2.ID < t1.ID
and t2.ID > :last_selected_id_1
where t1.ID > :last_selected_id_2
group by t1.ID, t1.Records
having coalesce(sum(t2.Records), 0) < :limit
order by t1.ID
:last_selected_id_x is the last ID from the current page or 0 for the first page.
http://rextester.com/FJRUV28068
You can use MySQL session variables to create page links:
select page, min(ID) as min_id, max(ID) as max_id
from (
select ID
, #page := case when #sum = 0 then #page + 1 else #page end as page
, #sum := case when (#sum + Records) >= :limit
then 0
else #sum + Records
end as sum
from table1
cross join (select #sum := 0, #page := 0) initvars
order by ID
) sub
group by page
The result would look like:
page | min_id | max_id
1 | 1 | 2
2 | 3 | 3
3 | 4 | 4
http://rextester.com/LQVJWP18655
Note that it is officially (as of documentation) not recomended to use the session variables like that (read and write in one statement). Feature versions may break your code.
Here's the result I ended up with, I ended up basing it off Rajdeeps answer but tweaked so that it'll allow sorting.
$query = 'SELECT ID, Records FROM table1 ORDER BY something';
$stmt = $conn->prepare($query);
$stmt->execute();
$id_array = array(); // For storing each ID
$current_ids = array(); // For storing the current ID pages
$initial_ids = array(); // For storing the first page, in case the page is too high
$count = 0;
$current_page = 1;
while($row = $stmt->fetch(PDO::FETCH_ASSOC)){
$already_reset = false;
// Move the session to the next page if it's more over the limit than under
// For example, if a limit of 10 has 6 records already, and the next ID is 9 records, then start a new page
if($id_array and $count + $row['Records'] / 2 > $max_links){
array_push($id_array, $last_id);
$current_page ++;
$count = 0;
$already_reset = true; // Set a marker so that if this ID is larger than the limit then don't start a 2nd new page
}
// Backup the first results in case page is too high and needs to be reset to 1
if($current_page == 1){
array_push($initial_ids, $row['ID']);
}
$count += $row['Records'];
// The values that appear here are the results for the selected page
// They may not be in sequence so store the individual IDs
if($_GET['page'] == $current_page){
array_push($current_ids, $row['ID']);
}
// Start a new page if over the limit
if($count > $max_links and !$already_reset){
$current_page ++;
$count = 0;
array_push($id_array, $row['ID']);
}
$last_id = $row['ID'];
}
array_push($id_array, $last_id);
$total_pages = count($id_array);
// If page is invalid revert to default
if(!$current_ids){
$current_ids = $initial_ids;
$_GET['page'] = 1;
}
$_GET['page'] = max(1, min($total_pages, intval($_GET['page'])));
$current_ids is an array of the IDs for the current page, and $total_pages is pretty self explanatory.

Mysql count column issue

I can't figure-it out how to count all these dancers columns and echo with total of all dancers
id | dancer1 | dancer2 | dancer3 | dancer4, and so on..
---------------------------------------
1 alex michael dalm name
2 clare rose test
I have this for the start but is not working:
$counter = mysql_query("SELECT COUNT(*) AS id FROM table");
$num = mysql_fetch_array($counter);
$dancers = $num["id"];
echo "Total dancers: $dancers";
Any help is appreciated. Thanks!
Try this:
$counter = mysql_query("SELECT * FROM table");
$dancers = 0;
while($rows = mysql_fetch_array($counter)){
for($i = 1; $i <= 24; $i++){
$dan_id = 'dancer'.$i;
if($rows[$dan_id] != "" || $rows[$dan_id] != null )
$dancers++;
}
}
echo "Total dancers:". $dancers;
Note: Never design your database table like this.
I would actually save your dancers in a different (easier) way... For examle:
ID NAME SURNAME PHONE ....
1 Anna Brickstone 0975 ...
2 Jef Damen 0754 ...
That way you could use the following code to count tables:
$dancersCount="0";
$sql = "SELECT * FROM dancers";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
while($row = $result->fetch_assoc()) {
$dancersCount++;
}
} else {
echo "No dancers..";
}
echo"$dancersCount";
The counter will count +1 each time it finds a row (dancer)
If you really want to do it that way...
Then I don't really think there's an easy way to fix this... You will probably need to check how many columns you have in your database but that's not something i can help you with...
You need to change your table structure:
id | dancerNumber | name
1 1 alex
2 1 clare
3 2 michael
4 2 rose
5 3 dalm
6 3 test
7 4 name
8 4 dana
SELECT COUNT(*) AS id FROM table will return 8 dancers. If this is what you were looking for?
if you want to keep your structure then you need to do the following sql query
SELECT dancer1,
dancer2,
dancer3,
(CASE WHEN (dancer1 <> "" AND dancer1 IS NOT NULL) THEN 1 ELSE 0 END +
CASE WHEN (dancer2 <> "" AND dancer2 IS NOT NULL) THEN 1 ELSE 0 END +
CASE WHEN (dancer3 <> "" AND dancer3 IS NOT NULL) THEN 1 ELSE 0 END) AS COUNTER
FROM table
This will count all the non empty and non null columns and add a counter at the end of the table. this counter will then contain the number of dancers with your structure.
Full answer with your php code
$query = 'SELECT (CASE WHEN (dancer1 <> "" AND dancer1 IS NOT NULL) THEN 1 ELSE 0 END +
CASE WHEN (dancer2 <> "" AND dancer2 IS NOT NULL) THEN 1 ELSE 0 END +
CASE WHEN (dancer3 <> "" AND dancer3 IS NOT NULL) THEN 1 ELSE 0 END) AS COUNTER
FROM table'
$counter = mysql_query($query);
$num = mysql_fetch_array($counter);
$dancers = $num["COUNTER"];
echo "Total dancers: $dancers";

Avoid displaying duplicate results in PHP from database

I have been trying to manage duplicate data which is shown to users.
I thought I can add the varibales to arrays and use the function array_unique
I want to be able to manage the rows which contain a duplicate date and split them into different sections for example
if(duplicate.exists == true)
{
//do something to the duplicate row
}
else
{
//do something to the row which isnt a duplicate
}
I cant figure out why array_unique is not working.
Help would be appreciated, Thanks.
$result = mysqli_query($con, "SELECT *
FROM quotes order by DATE asc ");
$index1 = array();
$fact1 = array();
$newDate1 = array();
while ($row = mysqli_fetch_array($result)) {
$index = $row['id'];
$dbdate = $row['date'];
$fact = $row['quote'];
$newDate = date("d-m-Y", strtotime($dbdate));
$index1[] = $fact;
$fact1[] = $fact;
$newDate1[] = $newDate;
}
Then have a function which loops through each array and finds out if a certain date has already exists.
for($i=0; $i<count($index1); $i++) {
echo(array_unique($newDate1));
}
else
{
}
Thats an example of the data that will be in the DB.
It's the id, fact, date example 1, fact, 2015-01-22
1 Steve Jobs unveiled the first Apple #Mac computer and changed technology forever (1984) - 2015-01-24
2 In 2011, the Urban Technology Innovation Center was launched in New York City - 2015-01-25
3 #Bebo was launched a whole decade ago today (2005), who feels old? - 2015-01-26
4 Sun Microsystems was acquired by Oracle Corporation for $7.4 bn (2010) - 2015-01-27
Considering you are sorting your query on date and that makes something a duplicate, all you need to do is track the last date.
$lastdate = '';
while ($row = mysqli_fetch_array($result)) {
$dbdate = $row['date'];
if ($lastdate==$dbdate) {
//duplicate
} else {
//first or unique
}
$lastdate = $dbdate;
}
It can be quicker to do this in SQL
Find the duplicates
SELECT * FROM quotes GROUP BY `date` HAVING COUNT(`date`) > 1 order by DATE asc
Find the non-duplicates
SELECT * FROM quotes GROUP BY `date` HAVING COUNT(`date`) = 1 order by DATE asc
So as noted by the OP, he wants a way to detect duplicates and not remove them.
To detect duplicates you can use something like this, answered in another question.
I would prefer this:
function array_has_dupes($array) {
return count($array) !== count(array_unique($array));
}
Use SQL "count" and "group".
create table z (x varchar(100),y varchar(100));
insert into z values ('a','b');
insert into z values ('a','b');
insert into z values ('a','c');
select x,y,count(*) as count from z group by x,y;
You get values:
+------+------+-------+
| x | y | count |
+------+------+-------+
| a | b | 2 |
| a | c | 1 |
+------+------+-------+
And use it in php code.

PHP Pagination-related MySQL query issue

I'm trying to do 2 things.
1) Get the amount of rows in this query
SELECT
COUNT(*)
FROM
`my_table`
WHERE
`column_1` = 152
AND
`column_2` = 42
ORDER BY
`column_3`
As you can see that is no problem ^^
2) Determine the number within the range of rows that is returned by id
Ex: ID 765 is Item 4 of 7 where column_1 = 152 and column_3 = 42
Does anyone have any basic solutions to this problem with almost pure MySQL? I'd like to avoid iterating through all the rows and setup a counter to increment until it matches current id like this:
$sql = '
SELECT
*
FROM
`my_table`
WHERE
`column_1` = 152
AND
`column_2` = 42
ORDER BY
`column_3`
';
$query = mysqli_query($sql);
$current_id = 2523;
$i = 1;
while ($row = mysqli_fetch_assoc($query)) {
if ($row['id'] == $current_id) {
$current_position = $i;
}
$i++;
}
print 'Current position in range is: '. $current_position;
Also please don't worry about the actual syntax, I won't be using this exact script, but you get the logic that I'd like to avoid using. If anyone has a better solution, please let me know. Thanks in advance!!

how to display only the first result of an array that is fetched from the database?

Currently I have a code that gets the data from the database and a foreach loop that list all the result. But I want to display one result at a time. Just to warn you, I'm really new to this. Really appreciate your help.
function featured_topics(){
$featureNum = 162;
$query = mysql_query("SELECT *
FROM `topics`
WHERE `country_id` = '$featureNum'");
$numrows = mysql_num_rows($query);
while( $row = mysql_fetch_array($query)){
$featured_topics[] = $row;
}
return $featured_topics;
}
function get_featured_topic(){
$get_topics = featured_topics();
$topic_count = sizeof($get_topics);
$current_topic = $get_topic[$topic_count];
echo $current_topic['topic_id'];
}
function featured_topics(){
$featureNum = 162;
$query = mysql_query("SELECT *
FROM `topics`
WHERE `country_id` = '$featureNum' LIMIT 0,1");
$numrows = mysql_num_rows($query);
$row = mysql_fetch_array($query);
return $row;
}
$result = mysql_result($query, 0, 'column')
0 represents the 0'th row.
You can't do this in the way you want.
SQL rows don't have an intrinsic order, so there's no guarantee that the next time you execute this query the rows will be in the same order. Here is what might happen:
Query result:
points | name
--------------------
23 | Iceland
83 | Georgia
8 | Spain
10 | Argentina
You take the 1st row (Iceland). Then later, you do the same query. You get back this result:
points | name
--------------------
83 | Georgia
23 | Iceland
8 | Spain
10 | Argentina
Whooops! Now you got Iceland again and lost track of Georgia.
Solutions to this dilemma
Grab all the rows at once, cache them, then cycle through them at your leisure. I believe this is the best solution.
Specify some order using "ORDER BY", and be prepared to deal with issues where rows are inserted during your iteration.

Categories