(自动)将SQL字符串转换为';枚举';?

(自动)将SQL字符串转换为';枚举';?,sql,performance,postgresql,Sql,Performance,Postgresql,我们有一个PostgreSQL数据库,其中包含数十亿个条目,格式如下: CREATE TABLE entry ( session integer NOT NULL, time integer NOT NULL, key text NOT NULL, data text NOT NULL ) 在此数据库中,键是开发人员定义的字符串,而数据是用户定义的内容。换句话说,虽然几乎有无限多的数据条目,但关键字条目的数量非常有限 有没有一种方法可以告诉SQL在键上进行优化,即: 嗯,您可能

我们有一个PostgreSQL数据库,其中包含数十亿个条目,格式如下:

CREATE TABLE entry (
  session integer NOT NULL,
  time integer NOT NULL,
  key text NOT NULL,
  data text NOT NULL
)
在此数据库中,
是开发人员定义的字符串,而
数据
是用户定义的内容。换句话说,虽然几乎有无限多的
数据
条目,但
关键字
条目的数量非常有限

有没有一种方法可以告诉SQL在
键上进行优化,即:

嗯,您可能会收到一个字符串,但您确实应该将其转换为整数并将其存储为整数,因为您只会收到非常有限的数量(例如,300)

当然,我们可以创建第二个表,在执行查询时转换/散列传入字符串并进行显式(反向)查找。然而,我的感觉是,必须/应该有一种方法来实现自动化

我们目前正在使用PostgreSQL 9.3,但我们愿意升级到更高版本(甚至可能升级到其他(无)SQL解决方案),以便能够在时间和空间上高效地处理上述数据

谢谢

Edit:我忘了澄清,我们不能使用enum的原因是因为
值是由一群没有数据库访问权限的分布式开发人员隐式定义的。这意味着,尽管密钥数量有限,但它们是从数据库的角度动态创建的

编辑2:从数学上讲,如果存在隐式应用的、无冲突但很小(就目标宽度而言)的散列函数,那么它可能会起作用


编辑3:使用第二个表不可行的原因是因为我们有一系列非常复杂的查询,它们多次引用
键。如果我们每次都必须重定向到第二个表,事情可能会变得更加混乱和难以理解。

您至少有两种选择:

  • 如果您的密钥集是预定义的,则PostgreSQL支持
  • 您可以使用任何整数类型(作为主键)和相应的键字符串的显式映射创建外部表,然后仅将外键存储到主表中的该表

  • 您可以将
    规范化为域表,并向其中添加FK。下面,我添加了一个指向域选项卡的数字FK,但您也可以使用
    文本字段来引用允许的字符串表。(这会使您的表更胖,但也会使更新/插入更简单)另一种方法是围绕两个表包装一个updateble视图

    CREATE TABLE entry (
      session integer NOT NULL
      , time integer NOT NULL
      , key text NOT NULL
      , data text NOT NULL
      );
    
    CREATE TABLE key_domain
            ( key_id SERIAL NOT NULL PRIMARY KEY
            , key_text text NOT NULL
            );
    
    INSERT INTO key_domain (key_text)
    SELECT DISTINCT key FROM entry;
    
    ALTER TABLE entry
            ADD COLUMN key_id INTEGER
            ;
    UPDATE entry e
    SET key_id = k.key_id
    FROM key_domain k
    WHERE e.key = k.key_text
            ;
    
    ALTER TABLE entry
            ADD CONSTRAINT key_fk
            FOREIGN KEY (key_id) REFERENCES key_domain(key_id)
            ;
    ALTER TABLE entry
            ALTER COLUMN key_id SET NOT NULL
            ;
    ALTER TABLE entry
            DROP COLUMN key
            ;
    

    在前端(应用程序中)创建一个哈希代码并在数据库中使用该代码不是更好吗

    int hash = key.GetHashCode();
    
    在数据库中,将有一个具有字符串键/哈希键对的查找表。但它只用于查找,以防您想知道属于哈希代码的字符串,而不用于查询


    如果要按键查询条目表,则在应用程序中获取哈希代码,并在查询中直接使用它,而无需将条目表连接到查找表。

    至少在PostgreSQL中,没有内置的功能来执行您想要的操作。有效地执行此操作需要对数据的存储方式进行重大更改,因为当前每一行都独立于所有其他行(除了更新中未更改的离线存储数据的TOAST指针)。列存储可以通过高度压缩键来实现您想要的功能,但会给某些查询模式带来其他问题

    您的最佳选择很可能是侧边查找表。为了解决查询复杂性的问题(额外的连接、计划时间等),我可能会编写一个查找函数,这样所有对
    key
    的引用都可以替换为
    lookup\u key(key)

    lookup\u key
    的一个简单实现就是一个执行
    SELECT
    的SQL函数。如果定义了
    稳定的
    ,而不是
    严格的
    ,这样的函数甚至可以内联并优化,因此这可能是一个非常好的选择


    如果键查找表实际上是静态的,则更复杂的替代方法是编写一个函数,该函数在首次调用时在内存缓存中将会话生存期构建为表的关联数组(哈希)。您可能希望用过程语言(如PL/Python)或C编写它。后续调用只需查找关联数组,就完全不需要访问另一个表。如果用C语言实现,这可能会带来很大的性能提升,但我怀疑用PL/Python或PL/Perl实现这一点的成本实际上会超过避免对缓存表进行简单扫描的好处。如果函数找不到行,则必须返回SPI SQL查询,因为它可能是在缓存构建后添加的。

    我有点困惑,您在标题中提到了枚举。您的问题似乎是枚举的设计目的。但你不会说为什么使用枚举不合适。抱歉,为了清晰起见,更新了我的帖子。这些键是未知的a-priori.Ad edit3:可以通过触发器(或可更新的视图)实现自动化。允许任何人插入任何值都不能很好地与您通过枚举(或“域表”的外键)限制插入的目标相结合。这些键有任何类型的模式吗?长度是否有上限?示例可以是
    productX.moduleY.functionZ
    some.interest.event
    。仔细想想,它们唯一的共同点就是它们是由点分隔的ascii字符串。哈希理论上是可行的。但是,我主要关心的是冲突的概率与存储效率的差异(例如,1字节存储==保证冲突,8字节存储==无冲突但效率非常低);而且不可能“发现”现有密钥。32位整数的范围为40亿个数字。