MySQL/PHP Sort with natural sort - php

I writing a code to get mysql field values sorted.
my filed values are as below
**downloads**
N/A
10
50
30
unlimited
N/A
70
unlimited
those are on mysql table field.
i need to sort those assenting and descending like below
Assending
N/A
10
30
50
70
unlimited
unlimited
Desending
unlimited
unlimited
70
50
30
10
N/A
The space is some rows don't have data. i wrote mysql query like below
SELECT * FROM fltable ORDER BY LENGTH(downloads), downloads DESC
But this not returns correct sort, can anyone help me with this using my sql or php solution.
Thank You

For assending value use:
SELECT downloads, (CASE WHEN downloads = 'N/A' THEN 0
WHEN downloads = '' THEN 1
WHEN downloads='unlimited' THEN 4
ELSE 3 END) as rank
FROM fltable
ORDER BY rank ASC;
For desending value use:
SELECT downloads, (CASE WHEN downloads = 'N/A' THEN 0
WHEN downloads = '' THEN 1
WHEN downloads='unlimited' THEN 4
ELSE 3 END) as rank
FROM fltable
ORDER BY rank, downloads DESC;

SELECT * FROM fltable
ORDER BY case when downloads = 'N/A' then 1
when downloads is null then 2
when downloads = 'unlimited' then 4
else 3
end DESC,
downloads * 1 DESC

I know, this solution is very similar to another in SQL.
You can use usort to implement any logic you need.
define('DV_NA', 1);
define('DV_EMPTY', 2);
define('DV_NUM', 3);
define('DV_UNLIMITED', 4);
define('DV_OTHER', 5);
function customDloadValue($n) {
switch($n) {
case "N/A": return DV_NA;
case null: case "": return DV_EMPTY;
case "unlimited": return DV_UNLIMITED;
default: return is_numeric($n) ? DV_NUM : DV_OTHER;
}
}
usort($strings, function ($a, $b) {
$av = customDloadValue($a);
$bv = customDloadValue($b);
if ($av != DV_NUM or $bv != DV_NUM) return $av - $bv;
return intval($a) - intval($b)
});

Another similar way :
SELECT download, (download= 'N/A') boolNA, (download= '') boolBlank,
(download+0 > 0) boolNum, (download= '0') boolZero
FROM table
ORDER BY boolNA DESC, boolBlank DESC, boolZero DESC, boolNum DESC,
(download+0), download
That way, you can create groups to sort.
Which could result in something like :
N/A
10
30
50
70
unlimited
unlimited
Same result as above, different way. If you only have a few datatype (less than 3) that you need to group, might be easier.

Related

How to lookup range value from database

How to find a value based on a range stored in the database.
My friend_levels table:
and on my profile table:
When I have friend_points = 1, I will get Normal Rate, then if I had friend_points = 180 I will get Great Friend, so it's basically like this on programming
if($profile_points >= 0 && $profile_points < 50) {
return 'Normal Rate';
} else if($profile_points >= 50 && $profile_points < 100) {
return 'Friend';
} else if($profile_points >= 100 && $profile_points < 150) {
return 'Good Friend';
}....
my question too is does it possible on QUERY? or I just make it on the PHP?
EDIT1: Is there a way to get the next target value?
For ex. If I'm on the Friend rate with 68 points how to get the 100 = Good Friend ? nevermind the substraction, I just want to get the next row.
If I understood you correctly, you can use CASE EXPRESSION like this:
SELECT id,user_id,
case when friend_points between 0 and 49 then 'Normal rate'
when friend_points between 50 and 99 then 'Friend'
when friend_points between 100 and 149 then 'Good friend'
.......
end as 'Friend_Status'
FROM profile
EDIT:
Or, if this names can change dynamicly then with a join:
SELECT t.id,t.user_id,s.name
FROM profile t
INNER JOIN friend_levels s ON(t.friend_points >= s.points_needed)
WHERE s.points_needed = (select min(f.points_needed)
from friend_levels f
where t.friend_points >= f.points_needed)
next smallest point
SELECT MAX (DISTINCT points_needed)
FROM friend_levels
WHERE points_needed < =$profile_points;
next highest point
SELECT MIN (DISTINCT points_needed)
FROM friend_levels
WHERE points_needed > $profile_points;
The CASE method proposed by Sagi works, but means that you need to update your code (SQL in this case rather than PHP) if you want to change your ranges.
Safin's method is only part of a solution - in order to find the corresponding description, Safin's query needs to be embedded in a select with joins/subselects as per Sagi's update, but this is somewhat inefficient.
Personally I wouldn't use aggregation for this:
SELECT user_id,
(SELECT fl.name
FROM friends_level fl
WHERE fl.points_needed<profiles.friend_points
ORDER BY fl.point_needed DESC
LIMIT 0,1) as level
FROM profiles

max() function not getting largest value

i have a table "orders" in mysql database , which contains column "order_number" . And because each order has several rows ( depending on size of the eshop order ) , the column "order_number" looks like this :
1
1
1
2
2
2
2
2
3
3
3
.... etc ... currently there is about 1000 orders , which means several thousands rows in table . What i am trying to do is just retrieve highest order number ... sounds easy enough :
$result=mysql_query("SELECT max(order_number) as max FROM orders");
$lastordernumber = mysql_fetch_array($result);
echo $lastordernumber["max"];
...and this returns "99" , while currently highest order number is close to 1000 . any ideas ?
thanks !
This occurs when the "order_number" is a string. You should store it as a numeric value, if that is what you want it to be. However, you can treat it as numeric for the purposes of this query:
select max(order_number + 0) as max
from orders;
Alternatively, you could write the query as:
select order_number
from orders
order by len(order_number) desc, order_number desc
limit 1;

SQL - ORDER a mix of numbers and words

I have an SQL database with I'm calling through a php file. The column can contain both numbers and words. For example it could countain:
1
4
clowns
12
46
Naturally, ORDER BY returns:
1
12
4
46
clowns
but I would like it to sort how it would sort integers with the numbers:
1
4
12
46
clowns
Is it possible to do this?
add some new columns to solve this, first order by whether or not it is a number, then by the number value, and finally alphabetically by the word value.
In MSSQL you have access to the isnumeric function.
In ORACLE you could create a custom function using THIS
In MYSQL you could create a custom function using THIS
select your_column from(
select *,
isnumeric(your_column) as ISNUMBER,
CASE WHEN ISNUMERIC(your_column) = 1 THEN CAST(your_column AS INT) ELSE null END as NUMBERVALUE,
case when isnumeric(your_column) = 1 then null else your_column end as TEXTVALUE )
order by isnumber desc, numbervalue asc, textvalue asc
...

Getting next results

I have a table that is something like the one below.
id | episode_number
1 55
2 56
3 57-58
4 59
5 60
6 61-62
7 63
8 64
9 65-66
10 67-68
How would I get the next 5 episodes after 57-58 without specificity the id, just the episode_number such as episode_number > 57-58 LIMIT 5.
But obviously episode_number > 57-58 would not work since it contains string in it. The database will just consider it as 5 and return the next 5 results after the episode_number 5.
Use CAST function to convert string to number, then your query:
SELECT episode_number
FROM table_name
WHERE CAST(episode_number AS UNSIGNED) > CAST('57-58' AS UNSIGNED)
ORDER BY CAST(episode_number AS UNSIGNED)
LIMIT 5
If there are two numbers, for example: '57-58', then CAST('57-58' AS UNSIGNED) converts and returns first one: 57
Try
SELECT TOP 5 * FROM tablename WHERE id >= (SELECT id FROM tablename WHERE episode_number = "57-58")
or if it's MySQL
SELECT * FROM tablename WHERE id >= (SELECT id FROM tablename WHERE episode_number = "57-58") LIMIT 5
You can select like this:
select id , case when LOCATE('-' , episode_number) = 0 then episode_number
else LEFT(episode_number , LOCATE('-' , episode_number)) end
as start_episode ,
case when LOCATE('-' , episode_number) = 0 then episode_number
else RIGHT(episode_number , LEN(episode_number) -LOCATE('-' , episode_number)) end
as end_episode
this should give you the base structure for such queries, you should consider converting the values into numbers to be able to easily compare them.
If possible restructure the table to something that has the start episode and end episode in separated fields

How can I optimize these queries?

Consider the following code snippet:
$beat = date('B'); // 1 beat = 86.4 seconds, 1000 beats = 1 day
$total = 'SELECT COUNT(id) FROM ads WHERE featured = 1;'; // number of featured ads
$current = 'SELECT * FROM ads WHERE featured = 1 ORDER BY id ASC LIMIT 1 OFFSET ' . ($beat % $total) . ';'; // current featured ad
Basically this cycles trough all the featured ads giving each one of them a beat (86.4 seconds) window where they will be given special highlight, example:
$beat $total $current
0 3 0
1 3 1
2 3 2
3 3 0
4 3 1
5 3 2
6 3 0
7 3 1
This works just fine, however I was wondering if somehow it would be possible to eliminate the need for the $total query and accomplish the same using just one query.
I don't see anyway to do this without using sub-queries but still, I'd like to hear your opinion on this.
$current = 'SELECT *, FOUND_ROWS(id) as num FROM ads WHERE featured = 1 ORDER BY id ASC LIMIT 1 OFFSET MOD(' . $beat . ', num)';
no, this is not possible. mysql requires an explicit constant value in LIMIT clauses. you can't put a calculation in a LIMIT clause.
You can't optimize queries. But you can optimize algorithm. Probably, you do need to do this, but... ))
If $total >= 1000 you will never show some ads. At any case some ads are shown more times then others.
long timestamp = ... // standard timestamp in millis
long period = 86400; // millis
long total = ... // you total query
long offset = (timestamp / period) % total;
current = ... // you last query, but with my offset

Categories