(自动)将SQL字符串转换为' enums'?

时间:2015-06-18 09:35:42

标签: sql performance postgresql

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

CREATE TABLE entry (
  session integer NOT NULL,
  time integer NOT NULL,
  key text NOT NULL,
  data text NOT NULL
)

在此数据库中,key是开发人员定义的字符串,而data是用户定义的内容。换句话说,虽然有几乎无限多的data个条目,但key条目的数量非常有限。

有没有办法告诉SQL以这种方式告诉它key进行优化:

  

好吧,你可能会收到一个字符串,但是你真的应该将它转换为整数并将其存储为整数,因为你只会收到非常有限数量的这些(比如300)

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

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

感谢。

编辑:我忘了澄清,我们无法使用Enums的原因是因为key值是由一群没有数据库访问权限的分布式开发人员隐式定义的。这意味着,尽管只有有限数量的密钥,但它们是从数据库角度即时创建的。

编辑2:从数学角度讲,隐式应用,无碰撞但很小(就目标宽度而言)哈希函数可能会成功(如果存在)。

编辑3:并且使用第二个表格不可行的原因是因为我们有许多非常复杂的查询,这些查询多次引用key。如果我们不得不重新定向到第二个表,那么事情可能会变得更加混乱和难以理解。

4 个答案:

答案 0 :(得分:0)

您至少有两个选项:

  1. 如果您的密钥集是预定义的,PostgreSQL支持enumeration types
  2. 您可以创建一个外部表,其中包含来自任何整数类型(作为主键)和相应键字符串的显式映射,然后仅将外键存储到主表中的此表中。

答案 1 :(得分:0)

您可以将key规范化为域表,并为其添加FK。下面,我添加一个指向域列表的数字FK,但您也可以使用key文本字段来引用允许字符串表。 (它会使你的表变得更胖,但它也会使更新/插入更简单)另一种方法是在两个表周围包装一个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
        ;

答案 2 :(得分:0)

在正面(在应用程序中)创建哈希码并在数据库中使用该哈希码是不是更好?

int hash = key.GetHashCode();

在DB中,您将拥有一个包含字符串键/哈希键对的查找表。但它只会用于查找,以防您想知道属于哈希码的字符串,而不是查询。

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

答案 3 :(得分:0)

至少在PostgreSQL中,没有内置设施可以做你想要的事情。有效地执行此操作将需要对数据的存储方式进行显着更改,因为当前每行都独立于所有其他行(除了TOAST指针,用于在UPDATE中未更改的外部存储数据)。列存储可以通过高度压缩键来实现您想要的类型,但会为某些查询模式引入其他问题。

你最好的选择很可能是侧面查找表。为了解决查询复杂性(附加连接,计划时间等)的问题,我可能会编写一个查找函数,因此key的所有引用都可以替换为lookup_key(key)

lookup_key的简单实现只是一个执行SELECT的SQL函数。如果定义STABLE而不是STRICT,这样的函数甚至可以内联和优化,所以这可能是一个非常好的选择。

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