【问题标题】:Django with Oracle DB - ORA-19011: Character string buffer too small带有 Oracle DB 的 Django - ORA-19011:字符串缓冲区太小
【发布时间】:2021-08-26 14:19:08
【问题描述】:

我有一个 Oracle 数据库的以下模型,它不是我的 Django 项目的一部分:

class ResultsData(models.Model):
    RESULT_DATA_ID = models.IntegerField(primary_key=True, db_column="RESULT_DATA_ID")
    RESULT_XML = models.TextField(blank=True, null=True, db_column="RESULT_XML")
    
    class Meta:
        managed = False
        db_table = '"schema_name"."results_data"'

数据库本身的RESULT_XML 字段被声明为XMLField。由于没有字符限制,我选择在 Django 模型中将其表示为 TextField。 当我尝试使用该模型下载一些数据时,我收到以下错误:

DatabaseError: ORA-19011: Character string buffer too small

我想,这是因为 RESULT_XML 字段中存储的数据量很大,因为当我尝试使用 .values("RESULT_DATA_ID") 提取记录时,它可以正常提取。

关于如何解决此问题的任何想法?到目前为止,谷歌搜索没有任何结果。

【问题讨论】:

  • 遗憾的是,XML 团队从未通过 Oracle C 堆栈提供良好的客户端支持(幸运的是 JSON 支持要好得多!)通常存在长度问题。你能像SELECT XMLTYPE.GETCLOBVAL(mycol) FROM mytab 这样在表上创建一个视图并进行查询吗?这是一个“标准”解决方案:cx-oracle.readthedocs.io/en/latest/user_guide/…。您可能还需要告诉 cx_Oracle 以字符串形式获取 LOB,请参阅github.com/oracle/python-cx_Oracle/blob/main/samples/…中的输出类型处理程序@
  • 是的,根据您的回复,我实际上已经制定了一个解决方案,它给了我想要的东西,但是使用原始 SQL 查询,这不能满足使用 Django ORM 的一致性尽可能。我会尽快发布我自己问题的答案。

标签: python django oracle


【解决方案1】:

更新答案 我找到了一种更好的方法来处理这个问题 - 我编写了一个自定义字段值 Transform 对象,它会生成一个我想要的 Oracle SQL 查询:

OracleTransforms.py

from django.db.models import TextField
from django.db.models.lookups import Transform


class CLOBVAL(Transform):
    '''
    Oracle-specific transform for XMLType field, which returns string data exceeding 
    buffer size (ORA-19011: Character string buffer too small) as a character LOB type.
    '''
    function = None
    lookup_name = 'clobval'

    def as_oracle(self, compiler, connection, **extra_context):
        return super().as_sql(
            compiler, connection,
            template='(%(expressions)s).GETCLOBVAL()',
            **extra_context
        )

# Needed for CLOBVAL to work as a .values('field_name__clobval') lookup in Django ORM queries
TextField.register_lookup(CLOBVAL)

有了以上内容,我现在可以编写如下查询:

from .OracleTransforms import CLOBVAL

ResultsData.objects.filter(RESULT_DATA_ID=some_id).values('RESULT_DATA_ID', 'RESULT_XML__clobval')

or

ResultsData.objects.filter(RESULT_DATA_ID=some_id).values('RESULT_DATA_ID', XML = CLOBVAL('RESULT_XML'))

这对我来说是最好的解决方案,因为我会继续使用 QuerySet,而不是 RawQuerySet。 我现在看到这个解决方案的唯一限制是我需要在我的 ORM 查询中始终指定 .values(CLOBVAL('RESULT_XML')) ,否则 Oracle DB 将再次报告 ORA-19011,但我想这仍然是一个好结果。

旧答案 所以,感谢 Christopher Jones 的建议,我找到了解决问题的方法。

ORA-19011 是 Oracle DB 回复的错误,当它将作为字符串发送回的数据量超过允许的缓冲区时。因此,它需要作为字符 LOB 对象返回。

Django 没有直接支持该特定于 Oracle 的方法(至少我没有找到),因此问题的答案是原始 Django 查询:

query = 'select a.RESULT_DATA_ID, a.RESULT_XML.getClobVal() as RESULT_XML FROM SCHEMA_NAME.RESULTS_DATA a WHERE a.RESULT_DATA_ID=%s'

data = ResultsData.objects.raw(query, [id])

这样,你会得到一个 RawQuerySet,如果这个鲜为人知、不太喜欢的表亲是 Django 的 QuerySet。您可以遍历答案,RESULT_XML 将包含一个 LOB 字段,在查询时将转换为 String 类型。

处理 String 类型编码的 XML 数据是有问题的,所以我还使用了 XMLTODICT Python 包,让它变得更文明一点。

接下来,我可能应该寻找一种方法来仅针对 RESULT_XML 字段修改 Django 的 getter,并让它生成一个对 Oracle DB 的查询,其中包含 .getClobVal() 方法,但我将在不同的 StackOverflow 中讨论它问题:Django - custom getter for 1 field in model

【讨论】:

  • 你不能创建一个视图,让 Django 使用这个视图吗?
  • 很乐意,但这是对外部数据库的调用,超出了我的影响。据我所知,授予我的访问权限不允许我创建视图。
猜你喜欢
  • 2014-10-08
  • 1970-01-01
  • 2015-12-29
  • 2019-09-13
  • 1970-01-01
  • 1970-01-01
  • 2012-05-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多