Sql query with mixed selected rows order - mysql

I have a table that looks like below:
| id | group_id | title |
-------------------------
| 1 | 1 | Hello |
| 2 | 1 | World |
| 3 | 2 | Foo |
| 4 | 2 | Bar |
My query may look like below to return the results above:
SELECT * FROM my_table ORDER BY id
Question
How can I order this table so that the group ids appears to be random, but still the same every time the query is executed.
Possible result example
This result looks to be in a random order. If I run the same query a week later, I want to see the exact same order which means it's not really random.
| id | group_id | title |
-------------------------
| 2 | 1 | World |
| 4 | 2 | Bar |
| 1 | 1 | Hello |
| 3 | 2 | Foo |
Appears to be random from a group_id perspective. It's no longer ordered by group_id like 1 1 2 2, but 1 2 1 2. It could also have been 2 1 1 2 or something that does not increase.
Should return the same results every time, not random each time.
I could order by title but if a title should change that row will be reordered. So the order needs to be made with the id I guess.
I want to avoid file or database caching if possible.
Is it possible?

How about taking the modulo function for your advantage.
SELECT * FROM my_table ORDER BY id % 3,id
Define a value to use with the modulo function (in my example 3) and order your table by the modulo of the id.
This should return the same order everytime you run the query and return some order that is pseudo random.
Since the modulo function can return the same value for different ids you also need to order by the original id to have a defined, reproducable order.

order this table so that the group ids appears to be random
Only ORDER BY RAND() may provide really random ordering.
but still the same every time the query is executed
Create separate static ordering table, fill it randomly with source table's ids, join it and order by it.

I did not solve the problem with the solution from #Kylro, but I found another way which works great.
SELECT * FROM my_table ORDER BY COS(id), id
Cos is sometimes a positive value and sometimes a negative value, almost random like. It works perfecty for this problem.

Related

MySQL Left Join / explain why original order of first table not been kept? [duplicate]

i have a mysql db with a table 'difficulties' with a few records. If i do "select * from difficulties" i get them back in the order they were added, ordered by primary key id:
mysql> select * from difficulties;
+----+-------+-----------+--------+----------+-----------+
| id | value | name | letter | low_band | high_band |
+----+-------+-----------+--------+----------+-----------+
| 1 | 1 | very_easy | VE | 1 | 1 |
| 2 | 2 | easy | E | 2 | 5 |
| 3 | 3 | medium | M | 6 | 10 |
| 4 | 4 | hard | H | 11 | 12 |
| 5 | 0 | na | NA | 0 | 0 |
+----+-------+-----------+--------+----------+-----------+
However, if i do "select name from difficulties" i get them back in a different order:
mysql> select name from difficulties;
+-----------+
| name |
+-----------+
| easy |
| hard |
| medium |
| na |
| very_easy |
+-----------+
My question is: what determines this order? Is there any logic to it? Is it something like "the order the files representing the records happen to be in within the filesystem" or something else that is to all intents and purposes random?
thanks, max
This is correct and by design: if you don't ask for sorting, the server doesn't bother with sorting (sorting can be an expensive operation), and it will return the rows in whatever order it sees fit. Without a requested order, the way the records are ordered can even differ from one query to the next (although that's not too likely).
The order is definitely not random - it's just whatever way the rows come out of the query, and as you see, even minor modifications can change this un-order significantly. This "undefined" ordering is implementation dependent, unpredictable and should not be relied upon.
If you want the elements to be ordered, use the ORDER BY clause (that's its purpose) - e.g.
SELECT name FROM difficulties ORDER BY name ASC;
That will always return the result sorted by name, in ascending order. Or, if you want them ordered by the primary key, last on top, use:
SELECT name FROM difficulties ORDER BY id DESC;
You can even sort by function - if you actually want random order, do this (caveat: horrible performance with largish tables):
SELECT name FROM difficulties ORDER BY RAND();
For more details see this tutorial and the documentation.
As Piskvor said, MySQL will order the query however it finds most convenient. To address the "why" part of your question, the different result orders are probably a side effect of different execution plans. If you have an index on difficulties, the second query would make use of it but the first would not.
Without the ORDER BY clause, the results are returned in random order. However, it seems logical to me that the easiest (and the fastest) way for db engine to return data as it's stored. So it's why the fist resultset is ordered by PK (no fragmentation, logical order is the same as physical). In the second case I would assume that you have an index on field name, and for the query select name from difficulties this index is covering, so db engine scans this index, and it's why you see results ordered by name. Anyway, you shouldn't rely on such "default" ordering.
select name from difficulties should return the values in alphabetical order as it is a text field.
And select * from difficulties will return in numeric order i believe. dont hold me to that lol
best thing to do is use ORDER BY if you care about what order things are

Using LIMIT 1 would be useful when there is an unique column on WHERE clause?

All my question is the title above.
Actually I want to know how limit works in mysql? suppose this table:
// colors
+----+-------+
| id | color |
+----+-------+
| 1 | blue |
| 2 | red |
| 3 | green |
| 4 | white |
| 5 | grey |
| 6 | brown |
| 7 | black |
| 8 | pink |
+----+-------+
As you know id column is unique (it is PK). And this is my query:
SELECT color FROM colors WHERE id = 5;
Now I want to know, would query above be more efficient if I use LIMIT 1 in the end of that?
no, because you are retrieving one row,
if you add limit 1 your dbms will count how many rows and decide if it is necessary to limit result or not.
this is an unnecessary work.
MySQL (and all other rdbms) are very efficient locating records based on primary keys. Adding a limit 1 will not have a significant impact in terms of speed.
However, a limit 1 clause will be handy if the above (or similar) query is used a correlated subquery in the select list. Such queries must return a single record and limit 1 will tell MySQL explicitly that the query cannot return more than 1 row.
select ..., (SELECT color FROM colors WHERE id = outer_query_field limit 1)
from ...

Get highest value from multiple columns and associated name column

I've got a table with these columns:
id | player1_name | player1_score | player2_name | player2_score | player3_name | player3_score | player4_name | player4_score | player5_name | player5_score
Given a single row, how do I get the highest playerX_score and the corresponding playerX_name?
I've tried using GREATEST(), but I can't get the playerX_name.
As an aside, I think your table would be better designed as id | name | score | position | teamid, where position goes from 1 to 5 and teamid serves to group everyone in the same team together. It would make this sort of query much easier (greatest-score-per-team with associated rows).
However, here's one way to do what you want with your current table:
SELECT GREATEST(player1_score,player2_score,player3_score,
player4_score,player5_score) as score,
CASE GREATEST(...) -- repeat the above
WHEN player1_score then player1_name
WHEN player2_score then player2_name
WHEN player3_score then player3_name
WHEN player4_score then player4_name
WHEN player5_score then player5_name
END as name
FROM mytable
I think your table structure isn't right for what you're trying to do. You want the database to know that there's some relationship between player1_name and player1_score, but that's not encoded in the table. A change that would make this much easier would be to give each player their own record, and use what you're currently calling id (which I assume is the ID for a particular game) to indicate which players go together.
It would look like this:
game_id | player_num | player_name | score
1 | 1 | Octern | 100
1 | 2 | Boris | 400
1 | 3 | Jarlsberg | 300
1 | 4 | Pete | 40000
...
Then, to find the high scorer for a given game (in this case, game #1), you'd say:
select player_name from scores
WHERE game_id = 1
ORDER BY score desc
LIMIT 1

Order the rows of a MySQL result based on a "next_id" field

I'm currently working with a database table that is structured as follows:
______________________________
| id | content | next_id |
|------|-----------|-----------|
| 1 | (value) | 4 |
| 2 | (value) | 1 |
| 3 | (value) | (NULL) |
| 4 | (value) | 3 |
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
The value of the next_id field defines the id of the row of data that should follow it. A value of NULL means that no row follows it.
Is there a way I can query the database in such a way that in the resulting rows will be ordered using this method? For example, in the case I gave above, the rows should be returned ordered so that the ids are in this order: 2, 1, 4, 3. I'm looking for a solution that can do this regardless of the number of rows in this sequence.
I know that it is possible to reorder the results after retrieving them from the database (using the programming language I'm working with), but I'm hoping that there is a way that I can do it in SQL.
I can't see a solution without as many self-joins as you have rows. Instead I would build a nested set out of it in a temp table using push down stack algorithm and then retrieve a full tree.
I've got something that's close.
/*one select to init the #next variable to the first row*/
select #next:= id from table1 order by isnull(next_id) asc, next_id asc limit 1;
select distinct a.id, a.next_id from table1 b
inner join
(
select #rank:= id as id, #next:= next_id as next_id from table1
where id = #next
) a
on (b.id = b.id);
This outputs
+----+---------+
| id | next_id |
+----+---------+
| 2 | 1 |
| 1 | 4 |
And then stops. If only I could find a way for it to continue....
Anyway this sort of force feeding values into a query is dodgy enough when doing ranking, let alone this sort of stuff, so maybe I'm going down a dead end.

MySQL - COUNT before INSERT in one query

Hey all, I am looking for a way to query my database table only once in order to add an item and also to check what last item count was so that i can use the next number.
strSQL = "SELECT * FROM productr"
After that code above, i add a few product values to a record like so:
ID | Product | Price | Description | Qty | DateSold | gcCode
--------------------------------------------------------------------------
5 | The Name 1 | 5.22 | Description 1 | 2 | 09/15/10 | na
6 | The Name 2 | 15.55 | Description 2 | 1 | 09/15/10 | 05648755
7 | The Name 3 | 1.10 | Description 3 | 1 | 09/15/10 | na
8 | The Name 4 | 0.24 | Description 4 | 21 | 09/15/10 | 658140
i need to count how many times it sees gcCode <> 'na' so that i can add a 1 so it will be unique. Currently i do not know how to do this without opening another database inside this one and doing something like this:
strSQL2 = "SELECT COUNT(gcCode) as gcCount FROM productr WHERE gcCode <> 'na'
But like i said above, i do not want to have to open another database query just to get a count.
Any help would be great! Thanks! :o)
There's no need to do everything in one query. If you're using InnoDB as a storage engine, you could wrap your COUNT query and your INSERT command in a single transaction to guarantee atomicity.
In addition, you should probably use NULL instead of na for fields with unknown or missing values.
They're two queries; one is a subset of the other which means getting what you want in a single query will be a hack I don't recommend:
SELECT p.*,
(SELECT COUNT(*)
FROM PRODUCTR
WHERE gccode != 'na') AS gcCount
FROM PRODUCTR p
This will return all the rows, as it did previously. But it will include an additional column, repeating the gcCount value for every row returned. It works, but it's redundant data...