MySQL: Sort By Verse Number - mysql

I'm trying to sort by data in Ascending order. Here's how my results are being displayed now:
1:1
1:10
1:2
1:3
1:4
1:5
1:6
1:7
1:8
1:9
2:1
Instead, I want them like this: 1:1, 1:2, 1:3, 1:4, 1:5, 1:6, 1:7, 1:8, 1:9, 1:10, 2:1.
Currently, my approach is to replace the : with a .. By adding the +0 in my query below, I thought it would simply treat my values as integers with decimal places. Any feedback on what I'm missing here?
"SELECT myverses.*
FROM myverses
INNER JOIN biblebooks ON myverses.book = biblebooks.name
ORDER BY biblebooks.id ASC, REPLACE(myverses.reference, ':', '.')+0 ASC;";

1.2 is the same as 1.20, which is why it's higher than 1.10.
You can use:
ORDER BY biblebooks.id ASC,
SUBSTRING_INDEX(myverses.reference, ':', 1)*1000 + SUBSTRING_INDEX(myverses.reference, ':', -1) ASC
This will convert 1:1 to 1001, 1.10 to 1010, 1.2 to 1002, so they'll sort correctly.
Just make the multiplier larger than the maximum number of verses in a chapter.

Related

Natural Sorting query is not working in mysql

Sorry for my english. I want to sort, in natural sorting, my records. This are some:
MBT44-2N-1
MBT44-4N-3
MBT44-8N-1
MBT44-6N-3
MBT66-6N-1
MBT86-8N-3
MBT88-12N-1
MBT88-12N-3
MBT88-4N-1
MBT88-4N-3
MBT88-6N-1
MBT88-6N-3
MBT88-8N-1
MBT88-8N-3
MBT1212-12N-1
MBT1212-12N-3
MBT1212-4N-1
MBT1212-4N-3
MBT1212-8N-1
MBT1212-8N-3
MBT1616-6N-1
MBT1616-8N-3
MBT2020-8N-1
I already had the order using the length and column name order like this:
LENGTH({$wpdb->posts}.post_title) ASC, {$wpdb->posts}.post_title ASC
But the results is not working that we want. This is a short query result:
MBT1212-8N-1
MBT1212-8N-3
MBT1616-4N-1
MBT1616-8N-1
MBT1616-8N-3
MBT2020-8N-1
*MBT1212-12N-1*
*MBT1212-12N-3*
Note the "MBT1212-12N-1" and "MBT1212-12N-3" on the bottom of the results. They're should be next MBT1212-8N-3 product.
This is part of my query but It works by parts:
SELECT post_title FROM `wp_posts`
WHERE post_type = 'product' AND post_title LIKE 'MBT%'
order BY length(SUBSTRING_INDEX(`post_title`,'-',1)) ASC,
post_title ASC,
length(post_title) asc,
post_title asc;
This is another aproach:
SELECT post_title FROM `wp_posts`
WHERE post_type = 'product' AND post_title LIKE 'MBT%'
order BY length(SUBSTRING_INDEX(`post_title`,'-',1)) ASC,
CAST(SUBSTRING_INDEX(`post_title`,'-',1) as UNSIGNED) ASC,
post_title ASC,
length(post_title) asc,
post_title asc;
I try to split the record by "hyphen" and sort by each one part but it works a little odd.
I appreciate all your help. Thanks in advance.
Perhaps a clumsy way but it works fine for me.
Try this:
SELECT post_title,
RPAD(
CONCAT(
LPAD(SUBSTRING_INDEX(SUBSTRING_INDEX(post_title, "-", 1), "-", -1),12," "),
LPAD(SUBSTRING_INDEX(SUBSTRING_INDEX(post_title, "-", 2), "-", -1),12," "),
LPAD(SUBSTRING_INDEX(SUBSTRING_INDEX(post_title, "-", 3), "-", -1),12," "),
LPAD(SUBSTRING_INDEX(SUBSTRING_INDEX(post_title, "-", 4), "-", -1),12," ")
),48,"0"
) AS C
FROM mytable
ORDER BY C ASC
The basic idea is to transform each section in the string that is divided with a separator, in your case "-", to equally long substrings padded with blanks.
In your example the posts will be transformed to a 48 character, 4 levels times 12 characters, string as follows:
"MBT44-2N-1" --> "MBT44 2N 1 1 "
"MBT44-4N-3" --> "MBT44 4N 3 3 "
"MBT44-8N-1" --> "MBT44 8N 1 1 "
"MBT44-6N-3" --> "MBT44 6N 3 3 "
This strings are then used to sort the result.
If you have more levels you just add more LPAD lines as above and increase the value for the total length of the sorting string.
This presume that you know the maximum length of the substrings and also the maximum number of levels divided by "-" in you string. But this can be handeled with variables that you set after examined the strings.
You can also have different separators for different levels but only if they are on the same place for all values to sort.

How to natural sort “X-Y” string data, first by X and then by Y?

Given this data:
W18-40461
W19-1040
W20-4617
W20-100
I've tried several of the common natural sorting methods for mysql, but they won't sort these in a natural descending way, like:
W20-4617
W20-100
W19-1040
W18-40461
For example:
select theID
from Table
where theID
order by lpad(theID, 9, 0) desc
Assuming the parts on either side of the - are limited to 2 digits and 5 digits respectively, you can extract the two numeric values using SUBSTR (and LOCATE to find the - between the two numbers) and then LPAD to pad each of those values out to 2 and 5 digits to allow them to be sorted numerically:
SELECT *
FROM data
ORDER BY LPAD(SUBSTR(id, 2, LOCATE('-', id) - 2), 2, '0') DESC,
LPAD(SUBSTR(id, LOCATE('-', id) + 1), 5, '0') DESC
Output (for my expanded sample):
id
W20-12457
W20-4617
W20-100
W19-1040
W18-40461
W4-2017
Demo on db-fiddle
If the values can have more than 2 or 5 digits respectively, just change the second parameters to LPAD to suit.
I would do this as:
order by substring_index(col, '-', 1) desc,
substring_index(col, '-', -1) + 0 desc
This orders by the part before the hyphen as a string. And it converts the part after the hyphen to a number for sorting purposes.

Naturally ORDER a column containing hierarchical item names

I have a column (of VARCHAR type) with values like these:
1
2
3
1.1
1.1.1
1.2.1
5
4
7
8
9
10
10.2
10.1
I hop[e to select this column and order it naturally, like the following:
1
1.1
1.1.1
1.2.1
2
3
4
5
...
I have tried ordering it with this for example and a lot of other query
SELECT data
FROM sample
ORDER BY LEN(data), data
Does someone have an idea how to do this?
try this
ORDER BY data, LEN(data)
or this
ORDER BY CONVERT(SUBSTRING_INDEX(data, ',', -1), SIGNED), Len(data)
i give demo in mysql as tsql there is not in sqfiddle .
DEMO
You seem to be hoping to order a series of hierarchically named items in a natural order. It looks like these items' names take the form.
token [ .token [. token [ .token ]]]
where subsequent tokens after the first are optional.
I suppose you want each token, if it's numeric, to be handed as a number. That is, for example, you want 1.123 to come after 1.2 because 123 is numerically greater than 2.
You didn't say what you want done with alphabetical tokens, e.g. 401.k and 403.b. I suppose they should come after the numerical ones, but in lexical order.
This query (http://sqlfiddle.com/#!2/81756/2/0) will do the trick out to five hierarchical levels of tokens.
SELECT col
FROM T
ORDER BY
FLOOR(SUBSTRING_INDEX(col,'.',1)),
SUBSTRING_INDEX(col,'.',1),
FLOOR(SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',1)))),
SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',1))),
FLOOR(SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',2)))),
SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',2))),
FLOOR(SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',3)))),
SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',3))),
FLOOR(SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',4)))),
SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',4)))
Why does this work? FLOOR() converts the leftmost part of a string to an integer, so it picks up the leading integer. If it doesn't find any numbers in the string it's trying to convert, it returns zero.
And, SUBSTRING(col, 2+LENGTH(SUBSTRING_INDEX(col,'.',NNN))) picks up the part of the col item to the right of the NNNth dot.
The data seems something like Hierarchical. With the current set of data supplied, if data is converted to hierarchical Data the order by can be done using something similar to:-
SELECT data FROM sample ORDER BY CAST ( '/' + replace( data, '.',
'/' ) + '/' as hierarchyid )

Calculate similarity between to records in MySQL

I am currently trying to figure out how to calculate the similarity between two records. My first record would be from a deactivated advertisement - so I want to find e.g. the 10 most similar advertisement regarding to some VARCHAR-fields equalness.
The thing, I can't figure out is, if there is any MySQL function, that can help me in any way - or if I need to compare the strings in some weird way?
EDIT #1
Similarity would be defined by these fields:
Title (weight: 50 %)
Content (weight: 40 %)
Category (weight: 10 %)
EDIT #2
I want the calculation to be like this:
Title: Words that match in the title field (only words >2 letters are matched).
Description: Words that match in the title field (only words >2 letters are matched).
Catgory: Match the category and if that doesn't match match the parent category with less weight :)
An equation of this could be:
#1 is the old, inactive post, #2 is the active post:
#2 title matches #1 title in 3 words out of #2's total of 10 words.
That gives 30 % match = 30 points.
#2 description matches #1 description in 10 words out of #2's total
of 400 words. That gives a 4 % match = 4 points.
#2 category doesn't match #1's category, therefore 0 % match. That
gives 0 points.
Then the sum would be 34 points for #2. :)
Edit #3
Here's my query - but it doesn't return different rows, but a lot of the same row.
SELECT
a.AdvertisementID as A_AdvertisementID,
IF(a.Topic LIKE a2.Topic, 50, 0) + IF(a.Description LIKE a2.Description, 40, 0) + IF(a.Cate_CategoryID LIKE a2.Cate_CategoryID, 10, 0) as A_Score,
a.AdvertisementID as A_AdvertisementID,
a.Topic as A_Topic,
LEFT(a.Description, 300) as A_Description,
a.Price as A_Price,
a.Type as A_Type
FROM
".DB_PREFIX."A_Advertisements a2,
".DB_PREFIX."A_Advertisements a
WHERE
a2.AdvertisementID <> a.AdvertisementID
AND
a.AdvertisementID = :a_id
ORDER BY
A_Score DESC
If you can literally compare the fields you are interested in, you could have MySQL perform a simple scoring calculation using the IF() function, for example
select
foo.id,
if (foo.title='wantedtitle', 50, 0) +
if (foo.content='wantedcontent', 40, 0) +
if (foo.category='wantedcategory', 10, 0) as score
from foo
order by score desc
limit 10
A basic 'find a fragment' could be achieved using like
select
foo.id,
if (foo.title like '%wantedtitlefragment%', 50, 0) +
if (foo.content like '%wantedcontentfragment%', 40, 0) +
if (foo.category like '%wantedcategoryfragment%', 10, 0) as score
from foo
order by score desc
limit 10
There are other techniques, but they might be slow to implement in MySQL. For example, you could calculate the Levenstein distance between two string - see this post for an example implementation.

MySQL sorting product codes that have both alpha and numeric characters

I have to sort output of a query for product names where the names have both alpha and numeric characters.
I have already found various solutions that convert the variables to numeric values (+0, etc.), and they sort the numeric part of the product names. But the preceding part of the product name string is of varying lengths, so the names aren't sorted alphabetically:
Post Lantern PL1
Post Lantern PL2
Post Lantern PL10
Post Lantern PL22
Landscape Light LV1
Landscape Light LV2
Landscape Light LV10
Landscape Light LV11
I guess the shorter names are sorted first?
I want the results sorted naturally: alphabetically, with the numbers in a natural order as well. I have tried:
ORDER by CAST(`product_name` AS DECIMAL), product_name
...
ORDER by product_name+0
The shorter names get sorted first, even though they are later in the alphabet. The numbers in the last part need to be in numerical order.
Here's a long query doing what you're looking for. I'm not sure about performance, though. You might also want to make some tests on several records to make sure.
SELECT
*
,SUBSTRING(
REVERSE(CAST(REVERSE(CONCAT(`product_name`,'8')) AS UNSIGNED)),1,
CHARACTER_LENGTH(
REVERSE(CAST(REVERSE(CONCAT(`product_name`,'8')) AS UNSIGNED))
)-1
) AS 'numericVal'
FROM `some_table`
ORDER BY
SUBSTRING(`product_name`,1,CHAR_LENGTH(`product_name`)-CHAR_LENGTH(`numericVal`)),
CAST(`numericVal` AS UNSIGNED INTEGER)
The 8's in the CONCAT() functions are there for numbers that end with zero(s). Otherwise when you reverse e.g. the string "etc30" and parse the number there it will be 3, not 03. So reversing it back will again produce 3 instead of 30.
You can change those two 8's in the CONCAT() functions with any single digit(s) (except zeros) you like.
[EDIT 2]
Here's the breakdown.
# Example record "Post Lantern PL10"...
SELECT
*
,SUBSTRING( # 5a) substring of this is calculated
REVERSE( # 4) gets re-reversed into "108"
CAST( # 3) gets casted into an integer so "801" part is parsed
REVERSE( # 2) gets reversed: "801LP nretnaL tsoP"
CONCAT(`product_name`,'8') # 1) is concatenated with an 8: "Post Lantern PL108"
)
AS UNSIGNED)
),
1, # 5b) from the first character (index is 1 for this in SQL)
CHARACTER_LENGTH( # 5c) and the length is recalculated (steps 1-4 repeated)
REVERSE(
CAST(
REVERSE(
CONCAT(`product_name`,'8')
)
AS UNSIGNED)
)
)-1 # 5d1) minus 1 because at the beginning we appended an 8 and we
# 5d2) want to get rid of it now, so we're dropping the last digit
) AS 'numericVal'
FROM `some_table`
ORDER BY # 6) order by
SUBSTRING(`product_name`, # 7a) first, substring `product_name`
1, # 7b) from the first character
CHAR_LENGTH(`product_name`)-CHAR_LENGTH(`numericVal`) # 7c) with the total length - length of numeric part
),
CAST(`numericVal` AS UNSIGNED INTEGER) # 8a) then, by the numeric part, which gets casted into
# 8b) an integer for accurate numeric ordering
[EDIT 1]
I think the best shot you have (to have total control over the varying data) is to separate product_name into 3 columns - product_name (like "Landscape Light"), product_class (or whatever, like "LV") and product_version (the numeric part).