Calculating place in a contest script - php

I was suggested to ask this question again but with more depth.
Here is my script:
<?php
//Loggedin
if($_SESSION['login']!=1)
{
print "You must be logged in.";
include($root . 'footer.php');
exit;
}
//Check banned account
elseif($ui['level']=="2"){
print "Sorry but your account is banned.";
include($root . 'footer.php');
exit;
}
//Check email verified
elseif($ui['email_check']=="0"){
print "Sorry but your account has not been verified, to verify your account now please visit <a href='index.php?index=verify&email=".$ui['email']."'>THIS LINK</a>.";
include($root . 'footer.php');
exit;
}
date_default_timezone_set('America/New_York');
$country= $ui['country'];
$dates=mysql_query("SELECT * FROM `contest` WHERE `countries` LIKE '%$country%'");
$timestamp = time();
$getcontests = $os_DB->query("SELECT * FROM contest WHERE date_1 <= '$timestamp' AND date_2 >= '$timestamp' AND countries LIKE '%$country%'");
$num = $os_DB->num($getcontests);
if($num == 0){
print"<td colspan='4'>There are currently no active contests</td>";
}
else
{
while ($dat = mysql_fetch_array($dates)) {
$tname = preg_replace('/\s+/', '', $dat['name']);
$places="(SELECT * FROM `".$tname."_contest` WHERE `username` <> 'cassa' ORDER BY `completed` DESC LIMIT ".$dat['rewards'].")";
$results=mysql_query($places) or die(mysql_error());
$reward = array("".$dat['reward_1'].",".$dat['reward_2'].",".$dat['reward_3'].",".$dat['reward_4'].",".$dat['reward_5'].",".$dat['reward_6'].",".$dat['reward_7'].",".$dat['reward_8'].",".$dat['reward_9'].",".$dat['reward_10']."");
$rewards = implode(",", $reward);
$rewardsa = explode(",", $rewards);
$i=0;
$a=1;
// Offers Contest
if(time() <= $dat['date_2'] && time() >= $dat['date_1'] && $dat['type'] == offer) {
print" <table width ='100%'><tr><th align='center'><font size='4'>{$dat['name']}</font></th><th align='right'><font size='1'>".date("m/d/Y h:i A", $dat['date_1'])."-".date("m/d/Y h:i A", $dat['date_2'])."</font></th></tr></table><br />".$dat['desc']."<br /><font size='1' color='white'>You must complete offers worth at least ".$dat['min_points']." points or $".$dat['min_cash']." to count towards contest!<br /><br />
You must also complete at least ".$dat['min_offers']." offers in order to be eligible for winnings.</font><br /><br />";
print" <table width ='100%'><tr><th align='left'>Place</th><th align='center'>User</th><th align='right'>Prize</th><th align='right'>Completed</th></tr>";
if(mysql_num_rows($results) == 0){
foreach($rewardsa as $rewa){
if(!empty($rewa['$i'])){
if($dat['r_type'] == points){
print" <tr><td align='left'>{$a}</td><td align='center'>......</td><td align='right'>{$rewardsa[$i]} points</td><td align='right'>--</td></tr>";
}
if($dat['r_type'] == cash){
print" <tr><td align='left'>{$a}</td><td align='center'>......</td><td align='right'>$".$rewardsa[$i]."</td><td align='right'>--</td></tr>";
}
$i++;
$a++;
}
}
}
while ($place = mysql_fetch_array($results)) {
if($dat['r_type'] == points){
print" <tr><td align='left'>{$a}</td><td align='center'>{$place['username']}</td><td align='right'>{$rewardsa[$i]} points</td><td align='right'>{$place['completed']}</td></tr>";
}
if($dat['r_type'] == cash){
print" <tr><td align='left'>{$a}</td><td align='center'>{$place['username']}</td><td align='right'>$".$rewardsa[$i]."</td><td align='right'>{$place['completed']}</td></tr>";
}
$i++;
$a++;
}
///Line I am working with///
$getyou= mysql_query("SELECT COUNT(*) AS Place, t.*
FROM ".$tname."_contest t
GROUP BY t.id
HAVING Place <= 3 OR username = '".$ui['username']."'");
$youu = mysql_fetch_array($getyou);
print" <tr><td align='left'>{$youu['Place']}</td><td align='center'>You</td><td align='right'>---</td><td align='right'>{$youu['completed']}</td></tr>";
}
}
}
?>
</table>
With this script I want to be able to show the logged user the place that they currently stand in the contest under the current winners.
This is what I want the table to look like.
-----------------------------------------
| Place | User | Prize | Completed |
| 1 | Someuser1 | $5.00 | 5 |
| 2 | Someuser2 | $2.50 | 3 |
| 3 | Someuser3 | $1.25 | 2 |
| 20 | You | --- | 1 |
-----------------------------------------
This is how it looks
-----------------------------------------
| Place | User | Prize | Completed |
| 1 | Someuser1 | $5.00 | 5 |
| 2 | Someuser2 | $2.50 | 3 |
| 3 | Someuser3 | $1.25 | 2 |
| 1 | You | --- | 1 |
-----------------------------------------
here is my table structure.
Column | Type | Null | Default
--------------------------------------
id |int(11)| No |
username |text | No |
completed|int(11)| No |
As you can see it is all coming from one table and the place isn't defined by the database, but by the script itself.
Hopefully this can clarify more than my last question.
Edit: With Sean's code this is what I get.
-----------------------------------------
| Place | User | Prize | Completed |
| 1 | kikkat | $5.00 | 1 |
| 2 |xXchris744Xx| $2.50 | 1 |
| 3 | kira423 | $1.25 | 1 | /// This line is me
| 7 | You | --- | 1 | /// But it shows my current place as 7
-----------------------------------------

This can be done with a nested query -
$getyou= mysql_query("SELECT
(SELECT count(*)+1 AS rank FROM contest WHERE completed >
(SELECT completed FROM contest WHERE username = '".$ui['username']."' ORDER BY completed DESC LIMIT 1)) as Place,
c.* FROM contest c WHERE username = '".$ui['username']."'");
$youu = mysql_fetch_array($getyou);
print" <tr><td align='left'>{$youu['Place']}</td><td align='center'>You</td><td align='right'>---</td><td align='right'>{$youu['completed']}</td></tr>";
Here is how the query works from the inside out-
The 1st (inside) SELECT gets the completed amount for the username =$ui['username']
SELECT completed FROM contest WHERE username = '".$ui['username']."' ORDER BY completed DESC LIMIT 1
The 2nd (middle) SELECT uses that completed amount and does a count of all the rows that have more completed, adds a 1 to that count, and saves it as the users Place.
SELECT count(*)+1 AS rank FROM contest WHERE completed > '#' // # represents the `completed` amount for the `$ui['username']` that we got in the 1st SELECT
The 3rd/Last (outside) SELECT now just gets the row data for username =$ui['username']
SELECT Place, c.* FROM contest c WHERE username = '".$ui['username']."' // Place was created/defined in early SELECTS, now we just get the rest of the data using c.*
Edit
To add a tiebreaker when one or more have the same number completed, you need to order your query by the id as well. So change your first query to -
$places="(SELECT * FROM `".$tname."_contest` WHERE `username` <> 'cassa' ORDER BY `completed`,`id` DESC LIMIT ".$dat['rewards'].")";
---
And add ORDER BY id and >= to the 2nd query
$getyou= mysql_query("SELECT
(SELECT count(*)+1 AS rank FROM contest WHERE completed >=
--
(SELECT completed FROM contest WHERE username = '".$ui['username']."' ORDER BY completed DESC LIMIT 1) ORDER BY `completed`,`id`) as Place,
-------------------------
c.* FROM contest c WHERE username = '".$ui['username']."'");
Edit #2
Try this new query. The previous one was not selecting the exact row, but the last of the tied.
$getyou= mysql_query("SELECT Place, c.* FROM
(SELECT #Place:=#Place+1 AS Place, c.*
FROM contest c, (SELECT #Place := 0) r ORDER BY completed DESC, id ASC ) c
WHERE username = '".$ui['username']."'");
This new query creates a temporary new column Place, using SELECT #Place := 0, and then we get the users 'Place' using #Place:=#Place+1 AS Place.

Related

Create line chart using value from SQL database

So I've been following this tutorial: https://www.plus2net.com/php_tutorial/chart-line-database.php
I am trying to add a line chart to my website to display number of sales for each month.
This is an example on how my SQL table looks like:
| ID | user | sale_id | date |
| 1 | RVN4372 | 1341234 | 2020-09-22 17:31:32 |
| 2 | OVI6517 | 5452351 | 2020-09-22 15:14:43 |
| 3 | RVN4372 | 8452176 | 2020-09-17 16:23:54 |
| 4 | FOK8905 | 7421312 | 2020-09-17 11:23:11 |
| 5 | DIF9127 | 4236123 | 2020-09-15 15:32:26 |
This is how my current query looks like:
<?php
if($stmt = $link->query("SELECT user,COUNT(*) FROM sales WHERE yearweek(DATE(date), 1) = yearweek(curdate(), 1) GROUP BY user order by COUNT(*) DESC")){
$php_data_array = Array(); // create PHP array
while ($row = $stmt->fetch_row()) {
$php_data_array[] = $row; // Adding to array
}
}else{
echo $link->error;
}
//print_r( $php_data_array);
// You can display the json_encode output here.
echo json_encode($php_data_array);
// Transfor PHP array to JavaScript two dimensional array
echo "<script>
var my_2d = ".json_encode($php_data_array)."
</script>";
?>
<div id="curve_chart"></div>
This is how it looks like on my website:
So this basically groups the users, and count how many sales each user has. On the X axis is display the user's name, and Y axis total number of sales.
I want to change this, so in the X asix is display the month, and Y asis total number of sales. How can I accomplish this?
EDIT: Hava been trying out some, but can't make it work. This is what I've got so far:
if($stmt = $link->query("
SELECT YEAR(date)
as SalesYear,
MONTH(date) as SalesMonth,
COUNT(*) AS TotalSales
FROM sales
GROUP BY YEAR(date), MONTH(date)
ORDER BY YEAR(date), MONTH(date)
AND COUNT(*) DESC
")){
If you have more than one year then you need to group in Year-month combination. Then change this query to this.
SELECT CONCAT(YEAR(date),'-' MONTHNAME(date)) as ym, COUNT(*) FROM sales GROUP BY ym ORDER BY count(*) DESC

PHP MySQL stop loop at next available number

i've got a database with the folowing info:
--------------
id | Book | Names
1 | 1 | Tom
2 | 8 | James
3 | 10 | Tom
4 | 2 | Tom
5 | 17 | James
6 | 2 | James
7 | 9 | James
8 | 7 | Tom
9 | 8 | Tom
This table shows books read by "Tom" and "James".
These are the requirements i need:
to show the next book not read. (eg. Tom's would be '3' and James's '1')
to skip book '1', '10' and '15' as these are no longer available. (so in James's case, the next book would be '3')
if it cannot be sequential, any random book not read will do as well.
here's what i did:
$sql = "Select * FROM books Group By names";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
$sql1 = "SELECT * FROM books WHERE names = '" . $row["names"]. "' ORDER BY book ASC";
$result1 = $conn->query($sql1);
if ($result1->num_rows > 0) {
$newbook = '1';
// output data of each row
while($row1 = $result1->fetch_assoc()) {
if ($row1["book"] == $newbook) {
echo "Exist<br><br>";
$newbook = $newbook+ 1;
} else {
if ($row1["book"] == '1' || $row1["book"] == '10' || $row1["book"] == '17') {
$newbook= $newbook+ 1;
} else {
echo "Add".$newbook."<br><br>";
break;
}
}
}
}
}
}
This is how far i've got. All help appreciated. Thanks
Off the top of my head, a calendar table approach might be a good solution to handle this problem completely in MySQL. First define a sequence table, containing the values from 1 to the highest book ID (I call this table seq_table). Then, to find the lowest book not read by a given user, a simple left join will do the trick:
SELECT MIN(t1.Book) AS next_book
FROM seq_table t1
LEFT JOIN books t2
ON t1.Book = t2.Book AND
t2.Names = 'Tom'
WHERE
t2.Book IS NULL;
If you instead wanted to choose a random book not read by Tom, then we can use the following query:
SELECT t1.Book AS next_book
FROM seq_table t1
LEFT JOIN books t2
ON t1.Book = t2.Book AND
t2.Names = 'Tom'
WHERE
t2.Book IS NULL
ORDER BY RAND()
LIMIT 1;
you will need another table to store available books let say books table
---------------------------------------------
id | Book_Title | Available
---------------------------------------------
1 | One million promises | no
2 | Two towers | yes
3 | Three musketeers | yes
4 | 4th Avenue Cafe | yes
5 | Famous Five | yes
6 | Six million dollar man | yes
7 | Seven Stars | yes
8 | Eighth Paladin | yes
9 | Ninth Heaven | yes
lets say that your read books table is named read_books, you can get the next book to read by Tom with this query:
select min(id) from books where Available='yes' and id not in (select book from read_books where names = 'Tom')

How to count and produce code if there is more than one occurency in a table

I have a page that dynamically looks for comments and replies in a databes and produce code accordingly. Now, i want to count the number of replies per comment (comment_id in the table below) and if they are more than 3 show "There are XX replies" else show all the replies.
The table of replies looks like this
+---------------------+----+---------+------------------+------------+
| date | id | user_id | reply | comment_id |
+---------------------+----+---------+------------------+------------+
| xxxx-xx-xx xx:xx:xx | 1 | 01 | adasdasdasdasdas | 8 |
| xxxx-xx-xx xx:xx:xx | 2 | 02 | test | 8 |
| xxxx-xx-xx xx:xx:xx | 3 | 03 | m no | 8 |
| xxxx-xx-xx xx:xx:xx | 4 | 03 | mno | 8 |
| xxxx-xx-xx xx:xx:xx | 5 | 05 | hehe | 10 |
+---------------------+----+---------+------------------+------------+
id is the id of the reply
user_id is the user who wrote the comment
comment_id is the id of the father'reply (a.k.a. the comment)
What i've tried so far is:
$querys = "SELECT * FROM replies
WHERE comment_id = {$writeComment['comment_id']}
ORDER BY date DESC;";
$findReplies = mysqli_query($_SESSION['connection'], $querys);
while ($reply = mysqli_fetch_assoc($findReplies)) {
$countReplies = "SELECT * FROM replies
GROUP BY comment_id
HAVING COUNT( DISTINCT comment_id ) > 3;";
$moreThanThree = mysqli_query($_SESSION['connection'], $countReplies);
if(!$moreThanThree){
// code for lass than 3 replies
} else {
// there are xx replies
// show all replies
}
You can use a group by query to find the count of replies. Then using these results you can display the text accordingly.
SELECT comment_id, count(*) FROM replies GROUP BY comment_id
you can use this query:
select reply, (select count(*) from replies replies_inner where replies_inner.comment_id = replies.comment_id) as reply_count from replies
then you can loop through php:
$previuos_comment=$rows[0]['comment_id'];
foreach($rows as $row) {
$reply_count =$row['reply_count '];
if($reply_count>3) {
echo 'xxxxxx';
} else {
if($previuos_comment==$row['comment_id']) {
echo $row['reply'];
}
$previuos_comment=$row['comment_id'];
}
}
So i managed to solve the problem, partially thank to #Gouda Elalfy:
while ($reply = mysqli_fetch_assoc($findReplies)) {
$querya = "SELECT comment_id, (SELECT COUNT(*) FROM replies
WHERE comment_id = {$writeComment['comment_id']})
AS reply_count FROM replies";
$findHowMany = mysqli_query($_SESSION['connection'], $querya);
$moreThanThree = mysqli_fetch_assoc($findHowMany);
if($moreThanThree['reply_count'] < 3){
//print comment
} elseif (isset($prevId)){
if(prevID != $reply['id']){
// print "there are xx comments"
break;
}
}
$prevId = $reply['id'];
}

selecting the minimum amount records where the sum is greater than a certain number

I'm trying to put together a query that for a restaurant reservation system. The idea is that if there is no table big enough to sit the party size then to look through the other free tables and find two tables big enough to be put together to accommodate the party size.
Ideally I would like to be able to select the minimum of tables to as closely match the size of the party.
For example if there is a request for a table of twelve I would like to ideally find two of the tables for six and no more.
This is the query I've tried but it gives an empty result
select tbl_id, sum(max_seats) as sumseats from tbl_list
group by tbl_id having sumseats> 11
I have put a link to sql fiddle to show the table structure
http://sqlfiddle.com/#!2/5a6904/2/0
Try something like the following(You'll require something like PHP in addition to MySQL):
Check if a single table can seat the required number of people. If yes, print all the such tables in ascending order of capacity.
If no single table has capacity greater than or equal to the required capacity, reserve the table with highest capacity and deduct the capacity from the required capacity.
Goto step 1.
Code :
$bookedTables = array();
while ($requiredCapacity > 0) {
$query = "SELECT * FROM (SELECT tbl_id, max_seats FROM tbl_list WHERE max_seats > $requiredCapacity)table1 ORDER BY table1.max_seats ASC";
$result = mysql_query($query);
if (count($result)!=0) {
array_push($bookedTables, $result[0]['tbl_id'];
$requiredCapacity = $requiredCapacity - $result[0]['max_seats'];
}
else {
$query = "SELECT * FROM (SELECT tbl_id, max_seats FROM tbl_list)table1 ORDER BY table1.max_seats DESC";
if (count($result)!=0) {
array_push($bookedTables, $result[0]['tbl_id'];
$requiredCapacity = $requiredCapacity - $result[0]['max_seats'];
}
else {
echo "No more tables left";
break;
}
}
}
I'm not proposing this as a definitive answer, but it's something to think about...
SELECT * FROM tables;
+----+------+
| id | size |
+----+------+
| 1 | 2 |
| 2 | 2 |
| 3 | 2 |
| 4 | 2 |
| 5 | 4 |
| 6 | 4 |
| 7 | 4 |
| 8 | 6 |
| 9 | 6 |
| 10 | 8 |
+----+------+
SELECT *
, x.size + y.size + z.size pax
FROM tables x
LEFT
JOIN (SELECT * FROM tables UNION SELECT 0,0) y
ON y.size < x.size OR (y.size = x.size AND y.id < x.id)
LEFT
JOIN (SELECT * FROM tables UNION SELECT -1,0) z
ON z.size < y.size OR (z.size = y.size AND z.id < y.id)
HAVING pax >= 12
ORDER
BY pax
, x.size DESC
, y.size DESC
, z.size DESC
LIMIT 1;
+----+------+------+------+------+------+------+
| id | size | id | size | id | size | pax |
+----+------+------+------+------+------+------+
| 10 | 8 | 7 | 4 | -1 | 0 | 12 |
+----+------+------+------+------+------+------+

How to speed up my php PSQL query

REQUESTED TABLE DEFINITIONS
Table "public.call_record"
Column | Type | Modifiers
-----------------+------------------------+---------------
cntrct_id | character varying(15) | not null
call_regard | text |
port_type | character varying(9) |
inst | text |
info_taken | character varying(40) |
log_date | date | not null
log_time | time without time zone | not null
act_taken | text |
use_material | text |
targ_pest | integer |
work_comp_by | text |
emp_no | integer |
comp_date | date |
job_start_time | time without time zone |
job_leave_time | time without time zone |
comp_val | boolean | default false
fti_call_regd | public.tsvector |
fti_inst | public.tsvector |
fti_act_take | public.tsvector |
route | character(3) |
act_port | text |
targ_pest_opt | text |
call_regard_opt | text |
targpest_other | text |
date_sched | date |
custord_num | integer |
dist_id | integer |
phone_slot | integer | default 0
Indexes:
"call_record_pkey" PRIMARY KEY, btree (cntrct_id, log_date, log_time)
"route_index" hash (route)
Check constraints:
"call_record_targ_pest_check" CHECK (targ_pest <= 100)
"call_record_targ_pest_check1" CHECK (targ_pest >= 0)
Table "public.per_call"
Column | Type | Modifiers
---------+----------------------+-----------
dist_id | character varying(2) |
route | character varying(2) |
type | character(1) |
total | integer |
I need to get data from 2 tables and print it in a single report. The report should look like this:
district | route | type | total | callbacks
| 01 | T | 12 | 5
| 02 | P | 0 | 0
| 03 | P | 3 | 1
2 | 01 | T | 4 | 1
| 02 | T | 1 | 0
| 03 | P | 0 | 0
etc... (this is theoretical sample data)
So, in essence I need to get the dist_id, route, type, and count(*) from the table per_call
and the count of call_backs from the table call_record
PROBLEM: looping through tables makes it go glacially slow. How can I adjust the following PSQL query so that I don't have to loop and I can echo the tabular data properly?
Let me know if anything is opaque and I will try to clarify
echo '<table align="center" border = 2>
<th>DISTRICT</th>
<th>ROUTE</th>
<th>TYPE</th>
<th>TOTAL</th>
<th>CALL BACKS</th>';
$SQL = " SELECT per_call.dist_id, per_call.route, per_call.type, per_call.total
FROM per_call, call_record
WHERE TRUE ";
if($type == 'termite'){
$SQL = $SQL." AND per_call.type = 'T' ";
}
else{
$SQL = $SQL." AND per_call.type = 'P' ";
}
$SQL = $SQL." AND call_record.dist_id = per_call.dist_id
AND call_record.log_date >= '$startDate'
AND call_record.log_date <= '$endDate'
ORDER BY per_call.dist_id, per_call.route, per_call.type ASC ";
echo $SQL;
/*AND call_record.log_date = '$startDate'
AND call_record.log_date = '$endDate'*/
$Q = pg_query($connect,$SQL);
while($row = pg_fetch_row($Q)){
$dist = $row[0];
$route = $row[1];
$type = $row[2];
$total = $row[3];
echo '<tr>';
echo '<td align="center">'.$dist.'</td>';
echo '<td align="center">'.$route.'</td>';
echo '<td align="center">'.$type.'</td>';
echo '<td align="center">'.$total.'</td>';
$SQL2 = "SELECT COUNT(*)
FROM call_record
WHERE dist_id = $dist
AND route = '$route'
AND substring(cntrct_id from 2 for 1) = '$type'
AND substring(call_regard_opt from 2 for 1) = '1'
";
$Q2 = pg_query($connect,$SQL2);
$row2 = pg_fetch_row($Q2);
$callbacks = $row2[0];
echo '<td align="center">'.$callbacks.'</td>';
echo '</tr>';
}
echo "</table>";
select pc.dist_id, pc.route, pc.type, pc.total,
count(
substring(cntrct_id from 2 for 1) = '$type'
AND substring(call_regard_opt from 2 for 1) = '1'
or null
) callbacks
from
per_call pc
inner join
call_record cr on cr.dist_id = pc.dist_id
where cr.log_date between '$startdate' and cr.log_date <= '$enddate'
group by pc.dist_id, pc.route, pc.type, pc.total
order by pc.dist_id, pc.route, pc.type asc
You don't have any indexes on dist_id in either table, which is going to make your join very slow. Add indexes on dist_id and see how much it improves.
Also, that query inside the loop is going to be the death of you, because you're going to be doing many many many queries. Work the inner query into your main query so you only execute one query in the database.
Unless index problems, your query is not badly written, so it can't be really optimized from a query form point of view. You can do that though which is cleaner :
$SQL = "SELECT per_call.dist_id, per_call.route, per_call.type, per_call.total
FROM per_call, call_record
WHERE call_record.dist_id = per_call.dist_id
AND per_call.type = '".($type == 'termite' ? "T" : "P")."'
AND call_record.log_date >= '$startDate'
AND call_record.log_date <= '$endDate'
ORDER BY per_call.dist_id, per_call.route, per_call.type"
Anyway, this is still vulnerable to SQL injections. Try using parameterized queries.

Categories