This question is continue from my last question.
I have jQuery Table Pagination using ORACLE database.
The code like this :
$cur_page = $page;
$page -= 1;
$per_page = 14;
$previous_btn = true;
$next_btn = true;
$first_btn = true;
$last_btn = true;
$start = $page * $per_page;
$query_pag_data = "
SELECT * FROM
(
SELECT x.*, ROWNUM as r FROM
(
SELECT P.PRODUCTION_STATUS, P.DATE_ADDED, P.FORM_NO, P.QTY_PLAN, ROWNUM, M.MODEL_NO, M.MODEL_NAME
FROM SEIAPPS_PRODUCTION_STATUS P, SEIAPPS_MODEL M
WHERE P.MODEL_NO = M.MODEL_NO
ORDER BY P.DATE_ADDED DESC, P.TIME
) x
)
WHERE r >= $start AND r <= $per_page
";
I limit per page is 14 row.
The result for first page is OK, no problem. But when I try to open next page, it's not load the data. Whereas I have more 20 row data in table.
Anyone please help.
Thanks.
Try like this,
SELECT * FROM
(
SELECT x.* FROM
(
SELECT p.production_status, p.date_added, p.form_no, p.qty_plan, ROWNUM r, m.model_no, m.model_name
FROM seiapps_production_status p, seiapps_model m
WHERE p.model_no = m.model_no
ORDER BY p.date_added DESC, p.TIME
) x
)
WHERE r >= $start AND r <= $per_page
;
If you want to get the result with page number and line per page, try with the below procedure.
CREATE OR REPLACE PROCEDURE pagination_proc(
i_page_num IN NUMBER,
i_lines_per_page IN NUMBER,
o_result OUT sys_refcursor)
AS
l_max_row NUMBER(4) := i_page_num * i_lines_per_page;
l_rec_count NUMBER(4);
BEGIN
IF i_page_num = 1 THEN
SELECT count(*)
INTO l_rec_count
FROM seiapps_production_status p, seiapps_model m
WHERE p.model_no = m.model_no;
END IF;
IF l_rec_count > 0 OR i_page_num >1 THEN
OPEN o_result FOR
SELECT production_status, date_added, form_no, qty_plan, model_no, model_name
FROM
(
SELECT production_status, date_added, form_no, qty_plan, model_no, model_name, ROWNUM rn
FROM
(
SELECT p.production_status, p.date_added, p.form_no, p.qty_plan, m.model_no, m.model_name
FROM seiapps_production_status p, seiapps_model m
WHERE p.model_no = m.model_no
)
WHERE ROWNUM <= l_max_row
)
WHERE rn > l_max_row - i_lines_per_page;
END IF;
END;
Related
I am trying to declare variables and run the query like this:
$sql = "SELECT #dateconsult := (YEARWEEK('2018-10-01',3)),
#countunits := ( SELECT COUNT(s.id_production)
FROM sw_sowing
WHERE status != 0
AND YEARWEEK(date,3) <= #dateconsult
GROUP BY id_production_unit_detail
),
#quadrants := ( SELECT DISTINCT value
FROM cf_config
WHERE parameter = 'PLANTHEALTH'
);
SELECT FORMAT(((count_quadrant * 100)/(total_units * Cuadrantes)),3) AS incidence
FROM (
SELECT #countunits AS total_units, #quadrants AS Cuadrantes,
FROM ph_planthealth
INNER JOIN ph_planthealth_detail ON ph_planthealth_detail.id_p = ph_planthealth.id
WHERE YEARWEEK(ph_planthealth.date,3) = #dateconsult
AND ph_planthealth.status = 200
AND ph_planthealth.id_tenant = 1
AND ph_planthealth_detail.id_plague != 0
GROUP BY ph_planthealth_detail.id_plague
) AS s
ORDER BY incidence DESC; ";
$plague = $this->db->fetchAll($sql, Phalcon\Db::FETCH_ASSOC, $options) ";
the problem is that it shows the result of the first SELECT which are the variables that I declared and not the one of the second SELECT that is the main query.
It's the first time I declare variables and I do not know if I'm doing it right.
I appreciate your comments and help regarding this topic.
You don't need to do the variable assignments in a separate SELECT. You can do them by joining with the main query.
SELECT FORMAT(((count_quadrant * 100)/(total_units * Cuadrantes)),3) AS incidence
FROM (
SELECT #countunits AS total_units, #quadrants AS Cuadrantes,
FROM ph_planthealth
INNER JOIN ph_planthealth_detail ON ph_planthealth_detail.id_p = ph_planthealth.id
WHERE YEARWEEK(ph_planthealth.date,3) = #dateconsult
AND ph_planthealth.status = 200
AND ph_planthealth.id_tenant = 1
AND ph_planthealth_detail.id_plague != 0
GROUP BY ph_planthealth_detail.id_plague
) AS s
CROSS JOIN (
SELECT #dateconsult := (YEARWEEK('2018-10-01',3)),
#countunits := ( SELECT COUNT(s.id_production)
FROM sw_sowing
WHERE status != 0
AND YEARWEEK(date,3) <= #dateconsult
GROUP BY id_production_unit_detail
),
#quadrants := ( SELECT DISTINCT value
FROM cf_config
WHERE parameter = 'PLANTHEALTH'
)
) AS vars
ORDER BY incidence DESC
Introduction
I have a highscore table for my game which uses ranks. The scores table represents current highscores and player info and the recent table represents all recently posted scores by a user which may or may not have been a new top score.
The rank drop is calculated by calculating the player's current rank minus their rank they had at the time of reaching their latest top score.
The rank increase is calculated by calculating the player's rank they had at the time of reaching their latest top score minus the rank they had at the time of reaching their previous top score.
Finally, as written in code: $change = ($drop > 0 ? -$drop : $increase);
Question
I am using the following two queries combined with a bit of PHP code to calculate rank change. It works perfectly fine, but is sometimes a bit slow.
Would there be a way to optimize or combine the two queries + PHP code?
I created an SQL Fiddle of the first query: http://sqlfiddle.com/#!9/30848/1
The tables are filled with content already, so their structures should not be altered.
This is the current working code:
$q = "
select
(
select
coalesce(
(
select count(distinct b.username)
from recent b
where
b.istopscore = 1 AND
(
(
b.score > a.score AND
b.time <= a.time
) OR
(
b.score = a.score AND
b.username != a.username AND
b.time < a.time
)
)
), 0) + 1 Rank
from scores a
where a.nickname = ?) as Rank,
t.time,
t.username,
t.score
from
scores t
WHERE t.nickname = ?
";
$r_time = 0;
if( $stmt = $mysqli->prepare( $q ) )
{
$stmt->bind_param( 'ss', $nick, $nick );
$stmt->execute();
$stmt->store_result();
$stmt->bind_result( $r_rank, $r_time, $r_username, $r_score );
$stmt->fetch();
if( intval($r_rank) > 99999 )
$r_rank = 99999;
$stmt->close();
}
// Previous Rank
$r_prevrank = -1;
if( $r_rank > -1 )
{
$q = "
select
coalesce(
(
select count(distinct b.username)
from recent b
where
b.istopscore = 1 AND
(
(
b.score > a.score AND
b.time <= a.time
) OR
(
b.score = a.score AND
b.username != a.username AND
b.time < a.time
)
)
), 0) + 1 Rank
from recent a
where a.username = ? and a.time < ? and a.score < ?
order by score desc limit 1";
if( $stmt = $mysqli->prepare( $q ) )
{
$time_minus_one = ( $r_time - 1 );
$stmt->bind_param( 'sii', $r_username, $time_minus_one, $r_score );
$stmt->execute();
$stmt->store_result();
$stmt->bind_result( $r_prevrank );
$stmt->fetch();
if( intval($r_prevrank) > 99999 )
$r_prevrank = 99999;
$stmt->close();
}
$drop = ($current_rank - $r_rank);
$drop = ($drop > 0 ? $drop : 0 );
$increase = $r_prevrank - $r_rank;
$increase = ($increase > 0 ? $increase : 0 );
//$change = $increase - $drop;
$change = ($drop > 0 ? -$drop : $increase);
}
return $change;
If you are separating out the current top score into a new table, while all the raw data is available in the recent scores.. you have effectively produced a summary table.
Why not continue to summarize and summarize all the data you need?
It's then just a case of what do you know and when you can know it:
Current rank - Depends on other rows
Rank on new top score - Can be calculated as current rank and stored at time of insert/update
Previous rank on top score - Can be transferred from old 'rank on new top score' when a new top score is recorded.
I'd change your scores table to include two new columns:
scores - id, score, username, nickname, time, rank_on_update, old_rank_on_update
And adjust these columns as you update/insert each row.
Looks like you already have queries that can be used to backfit this data for your first iteration.
Now your queries become a lot simpler
To get rank from score:
SELECT COUNT(*) + 1 rank
FROM scores
WHERE score > :score
From username:
SELECT COUNT(*) + 1 rank
FROM scores s1
JOIN scores s2
ON s2.score > s1.score
WHERE s1.username = :username
And rank change becomes:
$drop = max($current_rank - $rank_on_update, 0);
$increase = max($old_rank_on_update - $rank_on_update, 0);
$change = $drop ? -$drop : $increase;
UPDATE
Comment 1 + 3 - Oops, may have messed that up.. have changed above.
Comment 2 - Incorrect, if you keep the scores (all the latest high-scores) up to date on the fly (every time a new high-score is recorded) and assuming there is one row per user, at the time of calculation current rank should simply be a count of scores higher than the user's score (+1). Should hopefully be able to avoid that crazy query once the data is up to date!
If you insist on separating by time, this will work for a new row if you haven't updated the row yet:
SELECT COUNT(*) + 1 rank
FROM scores
WHERE score >= :score
The other query would become:
SELECT COUNT(*) + 1 rank
FROM scores s1
JOIN scores s2
ON s2.score > s1.score
OR (s2.score = s1.score AND s2.time < s1.time)
WHERE s1.username = :username
But I'd at least try union for performance:
SELECT SUM(count) + 1 rank
FROM (
SELECT COUNT(*) count
FROM scores s1
JOIN scores s2
ON s2.score > s1.score
WHERE s1.username = :username
UNION ALL
SELECT COUNT(*) count
FROM scores s1
JOIN scores s2
ON s2.score = s1.score
AND s2.time < s1.time
WHERE s1.username = :username
) counts
An index on (score, time) would help here.
Personally I'd save yourself a headache and keep same scores at the same rank (pretty standard I believe).. If you want people to be able to claim first bragging rights just make sure you order by time ASC on any score charts and include the time in the display.
I spent a lot of time trying to figure out what the rank logic is and put in a comment about it. In the meantime, here is a join query that you can run on your data - I think your solution will something something to this effect:
SELECT s.username, count(*) rank
FROM scores s LEFT JOIN recent r ON s.username != r.username
WHERE r.istopscore
AND r.score >= s.score
AND r.time <= s.time
AND (r.score-s.score + s.time-r.time)
GROUP BY s.username
ORDER BY rank ASC;
+----------+------+
| username | rank |
+----------+------+
| Beta | 1 |
| Alpha | 2 |
| Echo | 3 |
+----------+------+
(note that last AND is just to ensure you don't account for r.score==s.score && r.time==s.time - which i guess would be a "tie" game?)
I am not a MySQL guy, but I think that using self-join for ranking is a bad practice in any RDBMS. You should consider using of ranking functions. But there are no ranking functionality in MySQL. But there are workarounds.
There are some assumptions that have to be made here in order to move forward with this. I assume that the scores table has only one entry per 'username' which is somehow equivalent to a nickname.
Try this,
If I had a working db, this would be quick to figure out and test, but basically you are taking the 'sub query' you are running in the selected field and you are building a temp table with ALL of the records and filtering them out.
select a.nickname
, count(distinct b.username) as rank
, t.time
, t.username
, t.score
from
(
select
a.nickname
, b.username
from (select * from scores where nickname=? ) a
left join (select * from recent where istopscore = 1) as b
on (
b.score > a.score and b.time <= a.time -- include the b record if the b score is higher
or
b.score = a.score and b.time < a.time and a.username != b.username -- include b if the score is the same, b got the score before a got the score
)
) tmp
join scores t on (t.nickname = tmp.nickname)
where t.nickname = ?
I did not attempt to address your later logic, you can use the same theory, but it is not worth trying unless you can confirm that this method returns the correct rows.
If you would like to get deeper, you should create some data sets and fully setup the SQL Fiddle.
I am creating charts for a admin dashboard in our in-house CRM, however I am having a bit of trouble adding a trend line. I can't seem to find any documentation for a built in function so have attempted to do it using an sql query which is clunky and slow.
SELECT days.date,
(SELECT count(app.app_id) FROM li_appointments.li_appointments as app
where app_datetime >= date_add(days.date, interval -7 day)
and app_datetime <= days.date) / 5 as average
FROM li_appointments.li_days as days
where date >= date_add(date(now()), interval -30 day)
and date <= date(now())
It takes about 12 seconds just to grab the data for this one line on the chart. Is there any native way to do this with pChart, or is there a better PHP option available?
Here is the full code I am using right now with no trend line:
$getapps = "SELECT days.date, app.app_id as scheduled
,cnc.app_id as cnc
,dnc.app_id as dnc
,cancel.app_id as cancel
,covered.app_id as covered
FROM li_appointments.li_days as days
left Join (SELECT date(app_datetime) as app_datetime
, count(app_id) as app_id
FROM li_appointments.li_appointments
group by date(app_datetime))
as app on days.date = app.app_datetime
left Join (SELECT date(app_datetime) as app_datetime
, count(app_id) as app_id
FROM li_appointments.li_appointments
Where app_id in (select app_id from li_app_cnc)
group by date(app_datetime))
as cnc on days.date = cnc.app_datetime
left Join (SELECT date(app_datetime) as app_datetime
, count(app_id) as app_id
FROM li_appointments.li_appointments
Where app_id in (select app_id from li_app_dnc)
group by date(app_datetime))
as dnc on days.date = dnc.app_datetime
left Join (SELECT date(app_datetime) as app_datetime
, count(app_id) as app_id
FROM li_appointments.li_appointments
Where app_id in (select app_id from li_app_canceled)
group by date(app_datetime))
as cancel on days.date = cancel.app_datetime
left Join (SELECT date(app_datetime) as app_datetime
, count(app_id) as app_id
FROM li_appointments.li_appointments
Where terp_id is not null and terp_id <> ''
group by date(app_datetime))
as covered on days.date = covered.app_datetime
Where date > adddate(now(), Interval -30 day)
and date <= date(now())
group by date";
$result = mysql_query($getapps );
$date = array();
$scheduled = array();
$cnc = array();
$dnc = array();
$canceled = array();
$covered = array();
while($row = mysql_fetch_array($result)){
array_push($date, $row['date']);
array_push($scheduled, $row['scheduled']);
array_push($cnc, $row['cnc']);
array_push($dnc, $row['dnc']);
array_push($canceled, $row['cancel']);
array_push($covered, $row['covered']);
}
/* CAT:Line chart */
/* pChart library inclusions */
include("../class/pData.class.php");
include("../class/pDraw.class.php");
include("../class/pImage.class.php");
/* Create and populate the pData object */
$MyData = new pData();
$MyData->addPoints($scheduled,"Scheduled");
$MyData->addPoints($cnc,"CNC");
$MyData->addPoints($dnc,"DNC");
$MyData->addPoints($canceled,"canceled");
$MyData->addPoints($covered,"covered");
$MyData->setSerieTicks("Probe 2",4);
$MyData->setSerieTicks("Probe 3",4);
$MyData->setAxisName(0,"Appointments");
$MyData->addPoints($date,"Labels");
$MyData->setSerieDescription("Labels","Months");
$MyData->setAbscissa("Labels");
I have the script:
SELECT *, (pbct_hits + (COUNT(likes.rvw_usr_like) * 5) - (COUNT(unlikes.rvw_usr_like)) * 5) AS score
FROM tb_publications
LEFT JOIN tb_reviews_users likes ON likes.rvw_usr_fk_publication = pbct_id AND likes.rvw_usr_like IS TRUE
LEFT JOIN tb_reviews_users unlikes ON unlikes.rvw_usr_fk_publication = pbct_id AND unlikes.rvw_usr_like IS FALSE
GROUP BY pbct_id
ORDER BY score DESC;
I would not want to make two joins to the same table.
I believe it is possible to optimize the above script, but I'm not getting.
Edit
The question is solved:
-- Final Script:
SELECT pbct.*
FROM tb_publications pbct
LEFT JOIN tb_reviews_users ON rvw_usr_fk_publication = pbct_id
GROUP BY pbct_id
ORDER BY
(
(pbct_hits * 1) +
((SUM(CASE WHEN rvw_usr_like IS TRUE THEN 1 ELSE 0 END)) * 5) -
((SUM(CASE WHEN rvw_usr_like IS FALSE THEN 1 ELSE 0 END)) * 5)
) DESC, pbct_record ASC;
Is based on answer of #MikeSmithDev.
What about
SELECT pbct_id,
score =
(pbct_hits +
((SUM(CASE WHEN rvw_usr_like IS TRUE THEN 1 ELSE 0 END)) * 5) -
((SUM(CASE WHEN rvw_usr_like IS FALSE THEN 1 ELSE 0 END)) * 5))
FROM tb_publications
LEFT JOIN tb_reviews_users likes ON likes.rvw_usr_fk_publication = pbct_id
GROUP BY pbct_id
That should work... or do something simpler in SQL with math on php side
I would not do the math like that in the query. I would do:
SELECT *
FROM tb_publications
LEFT JOIN tb_reviews_users review_users ON review_users.rvw_usr_fk_publication = pbct_id
GROUP BY pbct_id
then I would manually do the math in php
$score = 0;
if($row['rvw_usr_like'])
$score += 5;
Also, depending on whether you insert likes or display score more often, you may want to consider storing an aggregate score in the publications table.
trying to rename duplicates in MySQL database so far using that code but this only adding 1 at the end of name. So if I have
UPDATE phpfox_photo n
JOIN (SELECT title_url, MIN(photo_id) min_id FROM phpfox_photo GROUP BY title_url HAVING COUNT(*) > 1) d
ON n.title_url = d.title_url AND n.photo_id <> d.min_id
SET n.title_url = CONCAT(n.title_url, '1');
Anna
Anna
Anna
Result is
Anna
Anna1
Anna11
When I got 200 Annas result is Anna1111111111111111111111111111111111111111111....etc
how do I do it to rename in the following inc
Anna
Anna1
Anna2
if i didn't miss something you can make a stored procedure that iterates throw your rows using cursors to do that as following:
DECLARE counter INT DEFAULT 0;
DECLARE num_rows INT DEFAULT 0;
DECLARE offset INT;
DECLARE title_urlvalue VARCHAR(50);
DECLARE no_more_rows BOOLEAN;
DECLARE ucur CURSOR FOR
SELECT
UPDATE phpfox_photo n
JOIN (SELECT title_url, MIN(photo_id) min_id
FROM phpfox_photo GROUP BY title_url HAVING COUNT(*) > 1) d
ON n.title_url = d.title_url AND n.photo_id <> d.min_id;
SET offset = 1;
SET no_more_rows = TRUE;
select FOUND_ROWS() into num_rows;
OPEN ucur;
uloop: LOOP
FETCH ucur
if counter >= num_rows then
no_more_rows = False;
endif
INTO title_urlvalue;
IF no_more_rows THEN
CLOSE ucur;
LEAVE uloop;
END IF;
update title_urlvalue = Concat(title_urlvalue,offset);
SET offset = offset + 1;
SET counter = counter + 1;
END LOOP uloop;
close ucur;
With User-Defined Variables
SET #counter:=0;
SET #title_url:='';
UPDATE phpfox_photo n
JOIN (SELECT title_url, MIN(photo_id) min_id
FROM phpfox_photo
GROUP BY title_url
HAVING COUNT(*) > 1) d
ON n.title_url = d.title_url AND n.photo_id <> d.min_id
SET n.title_url = IF(n.title_url <> #title_url, CONCAT(#title_url:=n.title_url, #counter:=1), CONCAT(n.title_url, #counter:=#counter+1));
Maybe you can use modulo to produce numbering, like this (SQLite example, but should be similar in mysql):
SELECT *, (rowid % (SELECT COUNT(*) FROM table as t WHERE t.name = table.name ) ) FROM table ORDER BY name
All you need is to translate rowid and modulo function, both availible in mysql.
Then you can CONCAT results as you desire.
UPDATE phpfox_photo n
JOIN
(SELECT title_url,
MIN(photo_id) min_id
FROM phpfox_photo
GROUP BY title_url
HAVING COUNT(*) > 1
)
d
ON n.title_url = d.title_url
AND n.photo_id <> d.min_id
SET n.title_url =
CASE
WHEN <last char is int>
THEN <replace last char with incremented last char>
ELSE <string + 1>
END