私は次のような Note モデルを持っています (これは従来の MS SQL Server データベースから取得しているため、これらのレコードのほとんどは Django によって作成されたものではありません)。
class Note(models.Model):
id = models.AutoField(primary_key=True, db_column="note_id")
content = models.TextField(db_column="note_content", blank=True, null=True)
date_created = models.DateTimeField(db_column="date_created", auto_now_add=True)
date_modified = models.DateTimeField(db_column="date_modified", null=True, blank=True)
date_removed = models.DateTimeField(db_column="date_deleted", null=True, blank=True)
一部のレコードに対して a を実行すると、データベースに存在するにもかかわらず、.get
a が返されていました。DoesNotExist
これは、MS SQL Server TEXT
(のようにCREATE TABLE Foo ( content TEXT null)
) フィールドのコンテンツの長さが一定量を超えた場合に発生します。具体的には 19455 文字です。
動作中の様子は次のとおりです。
>>> note = Note.objects.get(pk=1)
>>> note.content = "x" * 19455
>>> note.save()
>>> note = Note.objects.get(pk=1)
>>> note.content = "x" * 19456
>>> note.save()
>>> note = Note.objects.get(pk=1)
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/path/to/django/db/models/query.py", line 366, in get
% self.model._meta.object_name)
DoesNotExist: Note matching query does not exist.
私は FreeTDS を使用しており、テキスト サイズを 2147483647 に設定しています。これは、使用している MS SQL Server のバージョンの上限のようです。
this other question on truncationSET TEXTSIZE n
によると、 n がバイト単位の値である命令を先頭に追加する必要があります。これにより、データが切り捨てられる問題が解決されます。それで、それが私の場合に起こっているかどうか、そしてそれが修正されるかどうか疑問に思いました.
そこで、カーソルとSET TEXTSIZE
コマンドだけを使用するコードを書きました。
まず、レコードに記載すべき内容を見てみましょう。
print "長さ: %d; 最後の 40 文字: %s" % (len(note.content), note.content[-40:]) 長さ: 19456; 最後の 40 文字: rVEF1cCJeRaTtcdkXMqqQUxEVLZapMGVGSxMfJ2T
そして、ループスルーします。TEXTSIZE 設定をインクリメントするたびに、必要に応じてレコードを表示します。また、返されたレコード フィールドの長さと最後の 10 文字も表示します。
>>> for xx in xrange(19450, 19460):
... cursor = connection.cursor()
... try:
... qrys = 'SET TEXTSIZE %d SELECT [Notes].[note_id], [Notes].[note_content] FROM [Notes] WHERE [Notes].[note_id] = 1' % xx
... print qrys
... qry = cursor.execute(qrys)
... record = qry.fetchone()
... if record:
... record_id, record_content = record
... print record_id, len(record_content), record_content[-10:]
... else:
... print "No record found after TEXTSIZE set to %d" % xx
... break
... except Exception, inst:
... print "Error: %s (%s)" % (inst, type(inst))
... break
... finally:
... cursor.close()
...
SET TEXTSIZE 19450 SELECT [Notes].[note_id], [Notes].[note_content] FROM [Notes] WHERE [Notes].[note_id] = 1
1 19450 VLZapMGVGS
SET TEXTSIZE 19451 SELECT [Notes].[note_id], [Notes].[note_content] FROM [Notes] WHERE [Notes].[note_id] = 1
1 19451 LZapMGVGSx
SET TEXTSIZE 19452 SELECT [Notes].[note_id], [Notes].[note_content] FROM [Notes] WHERE [Notes].[note_id] = 1
1 19452 ZapMGVGSxM
SET TEXTSIZE 19453 SELECT [Notes].[note_id], [Notes].[note_content] FROM [Notes] WHERE [Notes].[note_id] = 1
1 19453 apMGVGSxMf
SET TEXTSIZE 19454 SELECT [Notes].[note_id], [Notes].[note_content] FROM [Notes] WHERE [Notes].[note_id] = 1
1 19454 pMGVGSxMfJ
SET TEXTSIZE 19455 SELECT [Notes].[note_id], [Notes].[note_content] FROM [Notes] WHERE [Notes].[note_id] = 1
1 19455 MGVGSxMfJ2
SET TEXTSIZE 19456 SELECT [Notes].[note_id], [Notes].[note_content] FROM [Notes] WHERE [Notes].[note_id] = 1
No record found after TEXTSIZE set to 19456
>>>
そのため、TEXTSIZE が 19456 より大きい数値に設定されたレコードを取得しようとすると、レコードは返されません。そして、文字列の最後の 10 文字が、上記の文字列から短すぎて欠落した文字を引いたものに一致することに気付くでしょう。たとえば、最後に見つかったレコードの場合、最後の 10 文字はMGVGSxMfJ2
. T
19455 の TEXTSIZE が問題のフィールドの長さより 1 小さいため、実際のレコードから が欠落しています。
だから今、もちろん私は疑問に思っています、何が起こっているのですか?これが django-pyodbc、pyodbc、または FreeTDS の問題であるかどうかを判断するために、他にできるトラブルシューティングはありますか? SQL Server の可能性もありますがSET TEXTSIZE 19456 SELECT [Notes].[note_id], [Notes].[note_content] FROM [Notes] WHERE [Notes].[note_id] = 1
、Server Management Studio で直接実行すると正しく動作し、正しい文字数が返されるようです。
また、保存が機能することにも注意してください。
>>> note.content = (note.content * 10)[:65536] # 65536 is max length allowed for TEXT, apparently
>>> len(note.content)
65536
>>> note.save()
>>> cursor = connection.cursor()
>>> qry = cursor.execute( 'SELECT [Notes].[note_id], DATALENGTH([Notes].[note_content]) FROM [Notes] WHERE [Notes].[note_id] = 1')
>>> record = qry.fetchone()
>>> record
(1, 65536)
>>>