【问题标题】:Best way to store versions in Postgresql在 Postgresql 中存储版本的最佳方式
【发布时间】:2020-04-02 10:59:03
【问题描述】:

我现在正在开发一个 API,我想存储使用日志。我的 API 现在有几个活动版本,在日志中我希望能够按版本排序和搜索。

目前版本以vX.X.XX 格式(从v0.0.16 开始)保存为字符串(varchars),因此无法通过where api_version > "vX.X.XX" 查询进行搜索。因此,这不是我的情况的最佳解决方案,我想切换到数字。

Postgresql 可以对作为 varchars 的版本进行排序,但速度很慢。

在 python 版本中创建为元组(例如(1, 0, 0)),因此可以轻松排序。

有没有最好的方法在 PostgreSQL 中存储版本? 什么是最好的类型,并且在排序和搜索中具有不错的速度性能?

【问题讨论】:

    标签: python sql database postgresql database-design


    【解决方案1】:

    不确定 Varchar 是否是最佳解决方案,如果您可以将字段类型更改为数字并在索引字段中重新保存现有数字,例如vX.Y.ZZ 为 (x*10000 + y*1000 + z*10)

    另一种方法是将您的字符串转换为数字然后排序 - 如 here 所述

    【讨论】:

    • 感谢您的想法,但在某些情况下它可能会成为问题。例如。 v0.0.16 = 160 或 v1.10.1 = 20010。但再次感谢您!我认为,强制排序并不是最佳解决方案。
    • Alex,作为一种解决方案,为了避免出现问题,您可以增加乘数,例如 x*100000 + y*1000 + z
    【解决方案2】:

    我认为,一个简单的解决方案是尝试将数组作为版本字段的数据类型。在您的情况下,它可能是int[]

    带有版本的表格示例:

    create table tbl (
        id serial not null primary key,
        "version" int[] null
    );
    

    之后,您可以像处理数组一样按版本过滤、排序或分组。

    select * from tbl where "version" > array[2, 3, 10] order by "version";
    

    【讨论】:

      【解决方案3】:

      在数据库中存储版本号的最简单方法是只为主要、次要等组件保留单独的列,例如

      major | middle | minor
      1     | 0      | 0
      

      这将使搜索给定版本的日志变得非常容易,例如

      SELECT *
      FROM logs
      WHERE
          major = 1 AND
          middle = 0 AND
          minor = 0;
      

      请注意,此方法使用关于版本号的sargable 查询。也就是说,版本列上的索引可以潜在地用于加速上述查询。

      存储版本字符串的问题是搜索单个版本组件要困难得多。而且大部分时间都不太可能使用任何索引。您仍然可以存储版本字符串,只是不要使用它进行搜索。

      【讨论】:

        【解决方案4】:

        在 Postgres 中最接近元组的是数组。我很确定这可以满足您的要求:

        • 平等比较
        • 不等式
        • 订购

        这是一个例子:

        with t as (
              select *
              from (values (array[1, 2, 3]),
                           (array[10, 15]),
                           (array[1, 2, 3, 4, 5]),
                           (array[1, 2, 4, 3])
                   ) v(version)
             )
        select t1.version, t2.version, t1.version < t2.version, t1.version = t2.version
        from t t1 cross join
             t t2
        order by t1.version desc, t2.version asc;
        

        我应该补充一点:您可以使用 array_to_string(version, '.') 轻松地将这些转换为字符串。

        【讨论】:

          【解决方案5】:

          我还没有找到适合我的情况的包含类型,所以我决定创建自己的类型。

          create type version as (
             major int,
             middle int,
             minor int
          )
          

          用法很简单:

          INSERT INTO table (version) VALUES (ROW(X,Y,Z));
          
          SELECT * FROM table WHERE version > ROW(X,Y,Z) ORDER BY version;
          

          在这种情况下,我可以轻松存储已创建的 python 元组,用于版本。

          【讨论】:

            【解决方案6】:

            实际上,您可以像 where "api_version > 'vX.X.XX" 那样进行比较——好吧。函数 regexp_split_to_array 可用于将版本号转换为整数数组(需要丢失 "v" )。 Postgres 可以使用常规比较运算符比较整数数组。

            -- fails to sort version properly and gets version incorrect 
            with versions (name, version) as 
               ( values ('first', 'v0.0.1')
                      , ('second', 'v1.0.1')
                      , ('third', 'v1.2.1')
                      , ('forth','v1.10.1')
               )
            select name, substring(version,2) as version 
                 , version >  'v1.3.5' "> v1.3.5"
              from versions 
             order by 2;
            
            -- properly sorts version and properly identifies the version order
            with versions (name, version) as    
               ( values ('first', 'v0.0.1')
                      , ('second', 'v1.0.1')
                      , ('third', 'v1.2.1')
                      , ('forth','v1.10.1')
               )
            select name, regexp_split_to_array(ver, '(\.)')::int[] as version
                 ,  regexp_split_to_array(ver, '(\.)')::int[] >  regexp_split_to_array('1.3.5', '(\.)')::int[]  "> v1.3.5"
              from (select name, substring(version,2) as ver  
                      from versions 
                   ) v
             order by 2;
            

            现在进行实际版本比较:

            with versions (name, version) as 
               ( values ('first', 'v0.0.1')
                      , ('second', 'v1.0.1')
                      , ('third', 'v1.2.1')
                      , ('forth','v1.10.1')
               )
               , target (version)  as
                 ( values ('v1.10.0') )
            select name, version
              from ( select name, regexp_split_to_array(ver, '(\.)')::int[] as version
                       from (select name, substring(version,2) as ver  
                              from versions 
                            ) v
                   ) v2
            where version > (select regexp_split_to_array( (substring(version,2))::text , '(\.)')::int[] from target) ;  
            

            上面的复杂性来自必须处理版本号中的“v”。没有它,这将简化为:

            with versions (name, version) as 
               ( values ('first', '0.0.1')
                      , ('second', '1.0.1')
                      , ('third', '1.2.1')
                      , ('forth','1.10.1')
               )
               , target     as
                 ( select regexp_split_to_array( '1.10.0', '(\.)')::int[] as version)
            select v.name, v.version 
              from (select name, regexp_split_to_array(version, '(\.)')::int[] as version from versions) v 
             where v.version > (select t.version from target t) ;
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2010-09-14
              • 1970-01-01
              • 2010-11-11
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多