Django/PostgreSQL varchar 到 UUID

Django/PostgreSQL varchar to UUID

我正在尝试将项目从 Django 1.7 更新到 1.9。不幸的是,它使用了内部使用 varchar 的 django-extensions UUIDfield。我正在尝试将这些字段更改为数据库中的 uuid 类型。

我已经创建了一个自定义迁移,告诉 Django 迁移将使用它自己的 SQL 来完成。当我这样做时,我的问题就来了(该列名为 guid):

alter table tablename alter column guid type uuid using guid::uuid;

我收到这个错误:

ERROR: operator class "varchar_pattern_ops" does not accept data type uuid

我真的不太熟悉 PostgreSQL,有点不知所措。我可以创建一个 CAST 或其他东西来解决这个问题吗?我不知道我会怎么做。

我正在尝试使用 script from here,它应该可以处理索引依赖关系,但我实在是不知所措。

type uuid 在您的 DDL 语句中是 shorthand for SET DATA TYPE uuidThe manual:

SET DATA TYPE

This form changes the type of a column of a table. Indexes and simple table constraints involving the column will be automatically converted to use the new column type by reparsing the originally supplied expression. [...]

varchar_pattern_ops 是一个 operator class,如果您 uuid 在任何索引中使用此运算符 class,则会在您的错误消息中提及。通常用于启用更快的排序、模式匹配和范围条件。

要修复,删除冲突的索引,更改数据类型,然后在没有特殊运算符的情况下重新创建索引 class - if 您仍然需要它们。

但是,一些使用 varchar_pattern_ops 索引的典型查询将停止使用数据类型 uuid 而不是 varchar。喜欢模式匹配:

  • PostgreSQL LIKE query performance variations

确保同时修复任何此类查询。

相关回答:

  • Postgresql operator class "varchar_pattern_ops" does not accept data type integer

我建议走一条稍微不同的路线。删除索引、更改数据类型并然后创建一个新索引更便宜 - 如果它仍然有用的话。

DROP INDEX tbl_guid_varchar_pattern_ops_idx;

ALTER TABLE tbl ALTER COLUMN guid TYPE uuid USING guid::uuid;

CREATE INDEX tbl_guid_idx ON tbl (guid);

如何找到违规索引?

I need to figure out how to examine the existent indices.

在现代版本的 Postgres 中,您可以在 psql 中获得 table 和 \d tbl 的现有索引。

获取给定 table 的所有完整 CREATE INDEX 语句:

SELECT pg_get_indexdef(indexrelid) || ';' AS idx
FROM   pg_index
WHERE  indrelid = 'public.tbl'::regclass;  -- optionally schema-qualified

仅使用 varchar_pattern_ops:

SELECT pg_get_indexdef(i.indexrelid) || ';' AS idx
FROM   pg_index i
JOIN   pg_opclass o ON o.oid = ANY (i.indclass)
WHERE  i.indrelid = 'public.big'::regclass
AND    o.opcname = 'varchar_pattern_ops';

详情:

我刚遇到这个问题,想补充信息。在解决这个问题时,我通过迁移解决了这个问题。任何人都可以添加到这里,请随意,包括更正。

此应用程序使用 Django 1.11、Py3,并且正在从 SQLite 上的早期本地开发人员迁移出去(只是为了证明一些概念)。迁移到 PG 时,我遇到了同样的错误:

ERROR: operator class "varchar_pattern_ops" does not accept data type uuid

我可以通过两种方式修复它。在应用程序的早期阶段,我能够擦除并从头开始。我知道这很少是一种选择,但我的行为为在迁移或升级场景中修复此问题提供了有用的线索。我有很好的 SQL/PG 印章来自 ORM 使用不常见的时间。这个问题真让我头疼。

问题

在我 5 岁的大脑中,问题来自于更改列类型,从字符串式到 UUID "native"。通过我的应用程序中的迁移,似乎创建了一个非 UUID 本机列。随着 Django 的模型 UUIDField 的引入,无法满足需求并抛出上述错误。这个问题类似于在数据库中使用非 int 值从 str 类型移动到 int 类型的愚蠢行为。

修复信息

我可以通过两种方式解决这个问题。

首先是通过 Django 的 squash 迁移。我已经在我的应用程序中遇到过与 UUID 使用相关的类似错误,因此我知道这种错误模式。通过压缩迁移,您可以跳过早期的非 UUIDField 列创建,只执行正确的列声明。当我重新运行(新鲜)migrate一切顺利。

第二种方式几乎没有变化,但我杀死了所有迁移并从当前状态开始。效果同上。

所以..

综上所述,对我来说,我能够以一种在我脑海中起作用的方式对问题进行逆向工程。 "fix" 永远不会创建除 UUIDField 友好方式以外的列。我的问题与 SQLite 到 PG 交换 (AFIK) 有关。

如果我正在进行升级,我认为杀死索引并重新创建它们的解决方案是可行的方法。

同样,我只是想提供一些关于此错误的信息,但 googleweb 没有返回任何真正吸引我的信息。所以我拿出了镐和手电筒。

从 Postgresql 9.5 开始可能还有另一个选项可用。现在有一个 uuid 运算符 class https://www.postgresql.org/docs/9.5/static/brin-builtin-opclasses.html. Of course, I still have 9.4 :-(. This blog post helps explain the problem and has a fix I assume still works, but it is also pre-9.5: https://coderwall.com/p/1b5eyq/index-for-uuid-array-data-type

请注意,该博客特别提到了在数组中使用 uuid,而新运算符 class 特别提到了 BRIN。买者自负。