MYSQL - order from table by X starting with id Y - mysql

I my mysql db I have a table with 3 parameters ( name, views, id ). I need to get row ordered by views. I'm getting something like this.
query:
select
from table
order by views
Result:
id | name | views
------------------------
7 | xxxx | 9000
2 | yyyy | 8000
1 | aaaa | 7000
4 | bbbb | 6000
8 | dddd | 5000
6 | cccc | 4000
5 | oooo | 3000
3 | tttt | 2000
What I need to do, is to get rows ordered by views but starting with specyfic ID. Is it possible. All input that i have is ID. Let sat that ID is 6, this should be output:
id | name | views
------------------------
6 | cccc | 4000
5 | oooo | 3000
3 | tttt | 2000
I can't use LIMIT as I don't really know what is possition at the moment. I just need to get rows which are left starting with ID.
What I'm trying to do is to get infinite scroll, I requesting next elements base on last element that was displayed. Only tricky part is that I'm ordering by views parameter.

select * from table
where (views = 4000 and id>6) or (views < 4000)
order by views desc, id asc;
The tricky part is that you have to know (select) the views of the element with ID 6; also you need to use the ID as secondary sort criteria in order to get consistent results.
Actually this is a common case of a since,until type of paging

SELECT * FROM table
WHERE views <= (SELECT views FROM table WHERE id = 6)
ORDER BY views

Related

MySQL SELECT value from a table column based on a different table and sum()

I've got 2 tables setup like so:
> events
-----------------------------------
id | event | eventHandle | points
-----------------------------------
1 | Event One | eventOne | 5
2 | Event Two | eventTwo | 10
> entries
-----------------------------------
id | user | eventHandle
-----------------------------------
1 | 1 | eventOne
2 | 1 | eventTwo
3 | 1 | eventTwo
5 | 5 | eventOne
And what I need to do is get the amount of 'points' each user has gained related to each event.
For example, user 1 has got 25 points and user 5 has 5 points.
What I can't figure out is how get the points, based one the eventHandle and sum them together.
I managed to select the different data from different tables, and do a basic sum with a different query, but not combined. Mind boggling.
Any help is mighty appreciated!
All you need to do is a simple inner join between the 2 tables on eventHandle fields and sum points by users:
select en.user, sum(ev.points)
from events ev
inner join entries en on ev.eventHandle=en.eventHandle
group by en.user

Returns distinct record in a joins query - Rails 4

I'm trying to get and display an order list including the current status.
#orders = Order.joins(order_status_details: :order_status)
.order('id DESC, order_status_details.created_at DESC')
.select("orders.id, order_status_details.status_id, order_statuses.name, order_status_details.created_at")
It works good but is returning all the rows with order ids duplicated like this:
+----+-----------+----------------------+---------------------+
| id | status_id | name | created_at |
+----+-----------+----------------------+---------------------+
| 8 | 1 | Pending | 2016-01-31 16:33:30 |
| 7 | 3 | Shipped | 2016-02-01 05:01:21 |
| 7 | 2 | Pending for shipping | 2016-01-31 05:01:21 |
| 7 | 1 | Pending | 2016-01-31 04:01:21 |
+----+-----------+----------------------+---------------------+
The correct answer must return uniques ids, for the example above should be the first and second row.
I was already trying with distinct on select, .distinct, .uniq and .group but I'm getting an error.
Thanks.
First of all, I believe your model is "An Order has many OrderStatusDetail". So that is the reason why you have several different name in your result.
So you can modify the query like this:
#orders = Order.joins(order_status_details: :order_status)
.order('id DESC, order_status_details.created_at DESC')
.where('order_status_details.id IN (SELECT MAX(id) FROM order_status_details GROUP BY order_id)')
.select("orders.id, order_status_details.status_id, order_statuses.name, order_status_details.created_at")
Ideally, the where condition is used for selecting just the expected id of order_status_details, I use min_id for example, you can modify it as needed

Only return an ordered subset of the rows from a joined table

Given a structure like this in a MySQL database
#data_table
(id) | user_id | time | (...)
#relations_table
(id) | user_id | user_coach_id | (...)
we can select all data_table rows belonging to a certain user_coach_id (let's say 1) with
SELECT rel.`user_coach_id`, dat.*
FROM `relations_table` rel
LEFT JOIN `data_table` dat ON rel.`uid` = dat.`uid`
WHERE rel.`user_coach_id` = 1
ORDER BY val.`time` DESC
returning something like
| user_coach_id | id | user_id | time | data1 | data2 | ...
| 1 | 9 | 4 | 15 | foo | bar | ...
| 1 | 7 | 3 | 12 | oof | rab | ...
| 1 | 6 | 4 | 11 | ofo | abr | ...
| 1 | 4 | 4 | 5 | foo | bra | ...
(And so on. Of course time are not integers in reality but to keep it simple.)
But now I would like to query (ideally) only up to an arbitrary number of rows from data_table per distinct user_id but still have those ordered (i.e. newest first). Is that even possible?
I know I can use GROUP BY user_id to only return 1 row per user, but then the ordering doesn't work and it seems kind of unpredictable which row will be in the result. I guess it's doable with a subquery, but I haven't figured it out yet.
To limit the number of rows in each GROUP is complicated. It is probably best done with an #variable to count, plus an outer query to throw out the rows beyond the limit.
My blog on Groupwise Max gives some hints of how to do such.

Mysql query Max not working

What i want to happen is group by parentid first, then group by position, which i have done. In that group i want the name with the highest rating to be displayed, which isn't happening. Instead the lowest id for each group is being displayed. The results should be tv1,tv3,tv5,tv7; as these are the highest rated values for each group.
id | name| parentid| position| rating |
1 | tv1 | 1 | 1 | 6 |
2 | tv2 | 1 | 2 | 5 |
3 | tv3 | 1 | 2 | 7 |
4 | tv4 | 1 | 2 | 3 |
5 | tv5 | 5 | 1 | 8 |
6 | tv6 | 5 | 1 | 2 |
7 | tv7 | 3 | 1 | 9 |
8 | tv8 | 3 | 1 | 3 |
$getquery = mysql_query("SELECT name,MAX(rating) FROM outcomes GROUP BY position,parentid") or die(mysql_error());
while($row=mysql_fetch_assoc($getquery)) {
$name = $row['name'];
$rating = $row['rating'];
echo "<p>Name: $name - $rating</p><p></p>";
}
It's not that the lowest id is being displayed -- you're not actually selecting the id column. Probably what you are seeing is the first entry in the name column for each group.
SELECT name, MAX(rating)
doesn't do what you think it does -- it doesn't instruct MySQL to pick the maximum value from the rating column, and also return the name that is associated with that row (aside: what do you think it would return if there was a tie for the maximum rating? What do you think it would return if you used AVERAGE rather than MAX?)
What it does instead is return the correctly calculated MAX(rating), and then one of the names out of that group. It doesn't guarantee which one gets returned, and it can change depending on how it decides to execute the query.
In fact, because of the undefined nature of a query such as this, it's not even legal SQL in other databases. (Try this in Postgres, and you'll get an error. Heck, try it in MySQL with the ONLY_FULL_GROUP_BY option enabled, and you'll get a similar error)
If what you want to do is find the maximum rating for each group, and then find the name associated with it, you'll have to do something like this:
SELECT name, max_rating FROM outcomes
JOIN (SELECT position, parentid, MAX(rating) AS max_rating from outcomes group by position, parentid) AS aggregated_table
USING (position, parentid)
WHERE rating = max_rating
(There are four or five other ways to do this, searching this site for mysql and aggregation will likely turn them up)

Is there a query in MySQL that would allow variable group numbers and limits akin to this

I've checked out a few of the stackoverflow questions and there are similar questions, but didn't quite put my fingers on this one.
If you have a table like this:
uid cat_uid itm_uid
1 1 4
2 1 5
3 2 6
4 2 7
5 3 8
6 3 9
where the uid column in auto_incremented and the cat_uid references a
category of relevance to filter on and the itm_uid values are the one
we're seeking
I would like to get a result set that contains the following sample results:
array (
0 => array (1 => array(4,5)),
1 => array (2 => array(6,7)),
2 => array (3 => array(8,9))
)
An example issue is - select 2 records from each category (however many categories there may be) and make sure they are the last 2 entries by uid in those categories.
I'm not sure how to structure the question to allow an answer, and any hints on a method for the solution would be welcome!
EDIT:
This wasn't a very clear question, so let me extend the scenario to something more tangible.
I have a set of records being entered into categories and I would like to select, with as few queries as possible, the latest 2 records entered per category, so that when I list out the contents of those categories, I will have at least 2 records per category (assuming that there are 2 or more already in the database). A similar query was in place that selected the last 100 records and filtered them into categories, but for small numbers of categories with some being updated faster than others can lead to having the top 100 not consisting of members from every category, so to try to resolve that, I was looking for a way to select 2 records from each category (or N-records assuming it's the same per-category) and for those 2 records to be the last entered. A date field is available to sort on, but the itm_uid itself could be used to indicate inserted order.
SELECT cat_uid, itm_uid,
IF( #cat = cat_uid, #cat_row := #cat_row + 1, #cat_row := 0 ) AS cat_row,
#cat := cat_uid
FROM my_table
JOIN (SELECT #cat_row := 0, #cat := 0) AS init
HAVING cat_row < 2
ORDER BY cat_uid, uid DESC
You will have two extra columns in the results, just ignore them.
This is the logic:
We sort the table by cat_uid, uid descending, then we start from the top and give each row a "row number" (cat_row) we reset this row number to zero whenever cat_uid changes:
---------------------------------------
| uid | cat_uid | itm_uid | cat_row |
| 45 | 4 | 34 | 0 |
| 33 | 4 | 54 | 1 |
| 31 | 4 | 12 | 2 |
| 12 | 4 | 51 | 3 |
| 56 | 6 | 11 | 0 |
| 20 | 6 | 64 | 1 |
| 16 | 6 | 76 | 2 |
| ... | ... | ... | ... |
---------------------------------------
now if we keep only the rows that have cat_row < 2 we get the results we want:
---------------------------------------
| uid | cat_uid | itm_uid | cat_row |
| 45 | 4 | 34 | 0 |
| 33 | 4 | 54 | 1 |
| 56 | 6 | 11 | 0 |
| 20 | 6 | 64 | 1 |
| ... | ... | ... | ... |
---------------------------------------
This is called an adjacent tree model or a parent-child tree model. It's one of the simplier tree model where there is only 1 pointer or 1 leaf. You would solve your query with a recursion or using a Self Join. Sadly MySQL doesn't support recursive queries, maybe it's working with prepared statements. I want to suggest you an Self Join. With a Self Join you can get all the rows from the right side and the left side with a special condition.
select t1.cat_uid, t2.cat_uid, t1.itm_uid, t2.itm_uid From t1 Inner Join t2 On t1.cat_uid = t2.cat_uid