mysql speed of query - selecting max value of every 3 rows - php

I have a table that holds price information. I need to select the max value of every three rows. EXAMPLE:
Table `daily_high`
____ _______
| ID | HIGH |
| 1 | 24.65 |
| 2 | 24.93 |
| 3 | 26.02 |
| 4 | 25.33 |
| 5 | 25.16 |
| 6 | 25.91 |
| 7 | 26.05 |
| 8 | 28.13 |
| 9 | 27.07 |
|____|_______|
Desired output to new table (ID will be auto-increment so don't assume an association exists between this ID 1 and the daily_high ID 1:
____ ___________
| ID | 3MaxHIGH |
|____|___________|
| 1 | 26.02 |
| 2 | 25.91 |
| 3 | 28.13 |
|____|___________|
I want to compare IDs 1,2, and 3 to determine the high value among them. Then once I have compared 1-3, I want to move on to 4 through 6, then 7 through 9, etc until I've done this for all values contained in the table (currently about 400,000 values). I have written code that uses
SELECT max(HIGH) FROM daily_high as dh1 JOIN (SELECT max(HIGH) FROM daily_high WHERE id >= dh1 AND id < (dh1.id + 3))
This works but is horribly slow. I've tried using the SELECT statement where I identify the column values to be pull for display, meaning between the SELECT and FROM parts of the query.
I've tried to use JOIN to join all 3 rows onto the same table for comparison but it too is horribly slow. By slow I mean just under 10 seconds to gather information for 20 rows. This means that the query has analyzed 60 rows (20 groups of 3) in 9.65879893303 seconds (I didn't make this up, I used microtime() to calculate it.
Anyone have any suggestions for faster code than what I've got?
Keep in mind that my actual table is not the same as what I've posted above, but it the concept is the same.
Thanks for any help.

If you ID it continous you can make this
SELECT floor(id/3) as range, max(HIGH) FROM daily_high GROUP BY range;

Why not to use DIV operator for grouping your aggregation:
SELECT (id-1) DIV 3 + 1 AS ID, MAX(high) AS 3MaxHIGH
FROM daily_high
GROUP BY (id-1) DIV 3
This query gives the same result.
ID 3MaxHIGH
1 26.02
2 25.91
3 28.13
I was unable to run your query, and I believe that this one is faster.
UPD: To ensure that you have valid groups for your ranges, use this query:
select id, high, (id-1) div 3 + 1 from daily_high
result:
id high (id-1) div 3 + 1
1 24.65 1
2 24.93 1
3 26.02 1
4 25.33 2
5 25.16 2
6 25.91 2
7 26.05 3
8 28.13 3
9 27.07 3

Fuller answer with an example. The following code will do what I think you want.
SELECT FLOOR((row - 1) / 3), MAX(Sub1.high)
FROM (SELECT #row := #row + 1 as row, daily_high.*
FROM daily_high, (SELECT #row := 0) r) Sub1
GROUP BY FLOOR((row - 1) / 3)
ORDER BY Sub1.ID

The below query worked for me on a test table. perhaps not the best, but the other solutions failed on my test table.
This does require the ID's to be sequential. Also be sure to put an index on High aswell for speed.
SELECT FLOOR(T1.Id/3)+1 AS Id, ROUND(GREATEST(T1.High, T2.High, T3.High),2) AS High FROM `daily_high` T1, `daily_high` T2, `daily_high` T3
WHERE T2.Id=T1.Id+1
AND T3.Id=T2.Id+1
AND MOD(T1.Id, 3)=1

logic: if(id is divisible by 3, id/3-1, id/3)
select if(mod(id,3) = 0,floor(id/3)-1,floor(id/3)) as group_by_col , max(HIGH)
FROM daily_high GROUP BY group_by_col;

Related

MySQL - Get similarity score from where two different rows meet same condition from two columns

What I want to do is generate a similarity percentage amount by comparing a ratings column from one table with the ratings column from another table.
However, this needs to be limited to instances where an id from one table matches an id in another table, is for a specific user and a rating exists in the ratings column from both tables.
For example, table1 has the following columns and data:
id | rate (out of 10)
=====================
1 | 8
2 | 10
3 | 5
4 | 4
5 | 0
6 | 9
7 | 8
And table2 has the following columns and data:
movid | userid | rating (out of 10)
================================
1 | 3 | 6
2 | 2 | 10
3 | 1 | 4
4 | 3 | 7
5 | 3 | 6
6 | 4 | 8
7 | 3 | 5
So lets say I want to use any rows where 'userid' = 3 and compare their 'rating' from table2 to the 'rate' column from table1 where 'rate' > 0 and id/movid from the two tables has the same number.
Using the example above, the results to compare should be limited to:
id | rate
=====================
1 | 8
4 | 4
7 | 8
movid | userid | rating
================================
1 | 3 | 6
4 | 3 | 7
7 | 3 | 5
Even though userid 3 had a rating for movid 5 in table2, it had a 0 rate (no rating) for id 5 in table1, so it will not compare those.
This would compare the ratings for each id/movid and then put it into an overall total. As the rating numbers are based on a score out of 10, I'm guessing the best way to determine the similarity percentage is to take the difference between each id/movid, subtract it from 10 to get the percentage number.
For id/movid 1, 'rate' in table1 is 8 and 'rating' in table2 is 6. The difference between those numbers is 2. We subtract 2 from 10, to get 80% similarity score for id/movid 1.
That would need to happen for each comparison and then total it all together.
So by my calculation, the similarity score for id/movid 1, 4 and 7 all combined would be 73% (rounded without decimals).
This total percentage amount is the end result I am trying to achieve. Can anyone help me out? I'm now bald after all of the hair I've pulled out trying to get this to work.
SELECT ROUND(SUM(10-ABS(table1.rate-table2.rating))*10/count(table1.id)) as per FROM table1 INNER JOIN table2 ON table1.id=table2.movieid WHERE userid=3 and table1.rate <> 0 GROUP BY userid
this will gives you what you need.
I suppose what you are looking for is:
SEELCT table1.id, table1.rate, table2.rating
FROM table1 INNER JOIN table2 ON table1.id=table2.movid
WHERE table1.rate>0 AND table2.userid=3
A simple INNER join plus filters in where clause to filter userid=3 and to show rates greater than zero
Following is not the best solution, but it should work:
select ROUND(sum(tmp.similarity_score)/max(tmp.cnt)) as Total_similarity_score
from
(
select t1.id,t1.rate,t2.movid,t2.rating,
(10 - abs(t1.rate-t2.rating))*10 as similarity_score,
(#cnt := #cnt +1) as cnt
from t1
inner join t2
on t2.movid = t1.id
cross join (select #cnt := 0)r
where userid = 3
and t1.rate <> 0
)tmp
;

Get the total of multiple column in sql

In database I have such table:
| id hotel_id | room_id | Ac_rooms | Non_ac_rooms | simple_rooms | Furnitured_room | other_rooms | added_by |
| 9 | 2 | 3 | 2 | 6 | 12 | 21 | raj |
I want to get the total numbers of room from SQL query (which is the total of room_id, Ac_rooms, Non_ac_rooms, simple_rooms, Furnitured_room, other_rooms).
What is the best way to get the total from SQL query? I need total number of rows.
Try this:
SELECT
SUM(Ac_rooms) as Ac_rooms,
SUM(Non_ac_rooms) as Non_ac_rooms,
SUM(Simple_rooms) as Simple_rooms,
SUM(Furnitured_rooms) as Furnitured_room,
SUM(Other_rooms) as Other_rooms,
SUM(Ac_rooms+Non_ac_rooms+Simple_rooms+Furnitured_room+Other_rooms) as Total_rooms,
FROM tbl_rooms
Or
SELECT
SUM(SUM(Ac_rooms)+SUM(Non_ac_rooms)+SUM(Simple_rooms)+SUM(Furnitured_room)+SUM(Other_rooms)) as Total_rooms,
FROM tbl_rooms
If I understood you correctly, you just need the sum
select (ac_rooms + Non_ac_rooms + simple_rooms + Furnitured_room + other_rooms) as total_rooms from YOUR_TABLE
Also you specified finding total number of rows, which you can get by using standart count function
select count(*) as number_of_rows from YOUR_TABLE
Or may be you are looking for the sum of types of the rooms through all the rows? In that case you will need
select sum(ac_rooms ), sum(Non_ac_rooms), sum(simple_rooms), sum(Furnitured_room), sum(other_rooms) from YOUR_TABLE
UPD: If I got you right, you need this
select sum(ac_rooms ) as ac_rooms_total,
sum(Non_ac_rooms) as non_ac_rooms_total,
sum(simple_rooms) as simple_rooms_total,
sum(Furnitured_room) as furnitured_room_total,
sum(other_rooms) as other_rooms_total,
sum(ac_rooms + Non_ac_rooms + simple_rooms + Furnitured_room + other_rooms) as TOTAL
from YOUR_TABLE

Sort by YES OR NO

I have search for Articles with dates. In MySQL is:
Article:
id | title
1 | first
2 | second
3 | third
4 | fourth
DatesArticle:
id | article_id | from | to
1 | 1 | 10-10-2010 | 11-11-2010
2 | 2 | 11-10-2010 | 12-12-2010
3 | 1 | 13-12-2010 | 12-01-2012
4 | 3 | 11-11-2012 | 12-12-2012
5 | 4 | 02-02-2013 | 02-02-2014
i would like get all Article with dates and sort this by availability.
for example i would like get all Articles and SORT this by dates FROM 12-10-2011 TO 12-01-2012
this should return me:
first (is in range FROM TO - DatesArticle.id = 3)
third (is in range FROM TO - DatesArticle.id = 4)
second (is NOT in range FROM TO)
fourth (is NOT in range FROM TO)
Is this possible with SQL or SQL and PHP? If yes, how?
Use the clause CASE, something like:
SELECT * FROM DatesArticle
ORDER BY CASE
WHEN id=3 AND CURRENT_DATE()<=from and to < CURRENT_DATE()>=to THEN 1
WHEN <condition_2> THEN 2
etc...
ELSE <any other condition>
END
Not saying the above is going to work as it is but it gives you and idea. If you add an example of the query you have tried or how are you building your where clause it would help for better answer.
My idea was to when it's id= 3 assign 1, if id = 4 assign 2, any other value assign 3, after that make the where condition and order by the number that you assign.
Try this:
select *,
case when t1.id=3 then 1 when t1.id=4 then 2 else 3 end as t
from article as t1
join DatesArticle as t2
on t1.id=t2.id
where CURRENT_DATE()<=from
and to < CURRENT_DATE()
order by t
You must first JOIN the tables in order to access article's data.
Then you ORDER on a logical condition (if there's a date within the given range).
SELECT title
FROM Article JOIN DatesArticle
ON (Article.id = DatesArticle.id)
ORDER BY DatesArticle.from > LastDate AND DatesArticle.to < FirstDate DESC;
(I never get ASC and DESC right in logical sorts -- try and see what happens)

How best to get someone's 'rank' from a scores table with php and mysql without looping

My table will hold scores and initials.
But the table wont be ordered.
I can get the total row count easy enough and I know I can get all of them and Order By and then loop through them and get the rank THAT way...
But is there a better way? Could this maybe be done with the SQL statement?
I'm not TOO concerned about performance so if the SQL statement is some crazy thing, then Ill just loop.
Sorry - Table has id as primary key, a string to verify unique app install, a column for initials and a column for score.
When someone clicks GET RANK... I want to be able to tell them that their score is 100 out of 1000 players.
SELECT s1.initials, (
SELECT COUNT(*)
FROM scores AS s2
WHERE s2.score > s1.score
)+1 AS rank
FROM scores AS s1
Do you have a primary key for the table or are you fetching data by the initials or are you just fetching all the data and looping through it to get the single record? How many results are you trying to fetch?
If you only want to fetch one record, as the title suggests, you would query the database using a WHERE conditional:
SELECT score FROM table WHERE user_id = '1';
See this answer: https://stackoverflow.com/a/8684441/125816
In short, you can do something like this:
SELECT id, (#next_rank := IF(#score <> score, 1, 0)) nr,
(#score := score) score, (#r := IF(#next_rank = 1, #r + 1, #r)) rank
FROM rank, (SELECT #r := 0) dummy1
ORDER BY score DESC;
And it will produce a result like this:
+------+----+-------+------+
| id | nr | score | rank |
+------+----+-------+------+
| 2 | 1 | 23 | 1 |
| 4 | 1 | 17 | 2 |
| 1 | 0 | 17 | 2 |
| 5 | 1 | 10 | 3 |
| 3 | 1 | 2 | 4 |
+------+----+-------+------+
Note that users with equal scores have equal ranks. :-)

PHP mysql Distinct, only load 1 set of ids

I'm trying to get data where order doesn't matter with unique ids. So simply my query would be
SELECT DISTINCT id1, id2 FROM messages ORDER BY date
If i have a database with the following data:
id1 | id2 | date
5 | 6 | 1/2/2011
6 | 5 | 1/1/2011
I would only need to load the column with the newest date because the ids are the same 2 people. Really i have to load ids where one of the ids is yours so my real query right now is
SELECT DISTINCT userid_1, userid_2
FROM messages
WHERE userid_2=$dbid
OR userid_1=$dbid
ORDER BY date
and i get a result as [6 5] [5 9] [9 5] [5 15] [5 6] [5 17]
Results 2 and 3 are the same and 1 and 5 are the same. Really only 4 results should be queried. Thanks!
One option is:
SELECT DISTINCT
if(userid_1 >= userid_2,userid_1, userid_2) AS 'id1',
if(userid_1 >= userid_2,userid_2, userid_1) AS 'id2'
FROM messages
WHERE userid_2=$dbid OR userid_1=$dbid ORDER BY date
This query shows two fields for each record.
The first field will be userid1 if it is bigger or equal to userid2, else will show userid2
The second field has the opposite logic
This makes sure that two similar sets of results will alwas be ordered the same way, so the DISTINCT will regard it as same.
If your goal is to get distinct pairings, you could do the following to get the smaller of the pair always in the left column and the larger into the right, thus ensuring that distinct works:
SELECT DISTINCT
(id1 - ( (id1 - id2) + abs(id1 - id2) ) * .5) as first,
(id2 + ( (id1 - id2) + abs(id1 - id2) ) * .5) as second
FROM messages ORDER BY date;
Since the pairs are always arranged, no need for any pairing functions.
Example
+------+------+--------+------------+
| id1 | id2 | val | pair_date |
+------+------+--------+------------+
| 4 | 5 | test 1 | 2010-12-25 |
| 5 | 4 | test 2 | 2011-10-31 |
| 17 | 50 | test 3 | 2011-07-04 |
| 50 | 17 | test 4 | 2001-01-01 |
+------+------+--------+------------+
If I run this query:
SELECT DISTINCT
(id1 - ( (id1 - id2) + abs(id1 - id2) ) * .5) AS first,
(id2 + ( (id1 - id2) + abs(id1 - id2) ) * .5) AS second
FROM pairs ORDER BY pair_date;
I get:
+-------+--------+
| first | second |
+-------+--------+
| 4.0 | 5.0 |
| 17.0 | 50.0 |
+-------+--------+
2 rows in set (0.00 sec)
Obviously using a boolean has the same effect, is easier to read, and probably is faster, but the above algorithm is handy if you just need to swap two numbers so that one side is always the lesser.
I'm not sure why you would have gotten an error. I'm running my tests directly from the MySQL command line....

Categories