How to compare version string (“x.y.z”) in MySQL?

前端 未结 10 2126
南旧
南旧 2020-12-16 14:02

I have firmware version strings into my table (like \"4.2.2\" or \"4.2.16\")

How can I compare, select or sort them ?

I cannot use standard strings compariso

10条回答
  •  一生所求
    2020-12-16 15:05

    I was searching for the same thing and instead ended up doing this -- but staying in mysql :

    • Installing this udf library into mysql because I wanted the power of PCRE.
    • using this statement

      case when version is null then null
      when '' then 0
      else
      preg_replace( '/[^.]*([^.]{10})[.]+/', '$1', 
          preg_replace('/([^".,\\/_ ()-]+)([".,\\/_ ()-]*)/','000000000$1.',
              preg_replace('/(?<=[0-9])([^".,\\/_ ()0-9-]+)/','.!$1',version
      ))) 
      end
      

    I'll break down what that means:

    • preg_replace is a function that the UDF library created. Because it's a UDF you can just call it from any user or dbspace like that
    • ^".,\\/_ () right now i'm considering all of these characters as separators or traditional "dots" in a version
    • preg_replace('/(?<=[0-9])([^".,\\/_ ()0-9-]+)/','.!$1',version) means to replace all the non-"dots" and non-numbers that are preceded by a number to be preceded by a "dot" and an exclamation point.
    • preg_replace('/([^".,\\/_ ()-]+)([".,\\/_ ()-]*)/','000000000$1.', ...) means to additionally replace all the "dots" with actual dots and to pad all the numbers with 9 zero's. Also any adjacent dots would be reduced to 1.
    • preg_replace( '/0*([^.]{10})[.]+/', '$1', ... ) means to additionally strip all the number blocks down to only 10 digits long and to preserve as many blocks as needed. I wanted to force 6 blocks to keep it under 64-bytes but needing 7 blocks was surprisingly common and thus necessary for my accuracy. Also needed blocks of 10 so 7 blocks of 9 was not an option. But the variable length is working well for me. -- remember strings are compared left to right

    So now I can handle versions like:

    1.2 < 1.10
    1.2b < 1.2.0
    1.2a < 1.2b
    1.2 = 1.2.0
    1.020 = 1.20
    11.1.1.3.0.100806.0408.000  < 11.1.1.3.0.100806.0408.001
    5.03.2600.2180 (xpsp_sp2_rtm.040803-2158)
    A.B.C.D = a.B.C.D
    A.A  <  A.B
    

    I chose exclamation point because it sorts in the collations sequences (that I'm using anyway) before 0. It's relative sort to 0 allows letters like b and a when used immediately adjacent to a number above to be treated like a new section and be sort before 0 -- which is the padding I am using.

    I am using 0 as padding so that vendor's mistakes like moving from a fixed 3 digit block to a variable one don't bite me.

    You can easily choose more padding if you want to handle silly versions like "2.11.0 Under development (unstable) (2010-03-09)" -- the string development is 11 bytes.

    You can easily request more blocks in the final replace.

    I could have done more but I was trying to do a as few paces as possible with a high-level of accuracy since I have several millions records to scan regularly. If anyone sees an optimization please repsond.

    I chose to keep it as a string and not cast into a number because the cast has a cost and also letters are important as we saw. One thing i was thinking about is doing a test on the string and returning an option that isn't as many passes or less expensive function for tidier cases. like 11.1.1.3 is a very common format

提交回复
热议问题