2

さて、私は「元帳」と呼ばれる基本的なテーブルを持っています。これには、さまざまなタイプ、整数、varcharなどのフィールドが含まれています。

私のプログラムでは、「from」述語のないクエリを使用してすべての行を収集していましたが、これはもちろん正常に機能します。しかし...コードを変更して、「where acctno = x」(Xはその時点で選択したいアカウント番号)を使用して一度に1行を選択できるようにしました。

これは私のプログラミング言語のクライアントライブラリのバグであるに違いないと思ったので、SQLiteコマンドラインクライアントでテストしましたが、それでも機能しません。

私はSQLiteに比較的慣れていませんが、OracleやMS SQL Serverなどを何年も使用しており、この種の問題はこれまで見たことがありません。

私が言える他のこと:*他の整数フィールドを使用したクエリも機能しません* charフィールドのクエリは機能します*文字列として(引用符で囲まれたアカウント番号を使用して)クエリを実行しても機能しません。(おそらく、数字が誤って文字列として格納されていると思いました)。* rowidによる行へのアクセスは正常に機能します。そのため、GUIツールを使用してデータベースを目立った問題なく編集できます。

例:

WHEREなしのクエリ(正常に動作):

1 | 0 | 0 | JPY |8|払込資本|C| X | 0 | X | 0 | 0 |||| 0 | 0 | 0 |
0 | 0 | 0 | JPY |11|ルートアカウント|P| X | 0 | X | 0 |0|システム|20121209| 150000 | 0 | 0 | 0 |
3 | 0 | 0 | JPY |13|三菱銀行Futsuu|A | X | 0 | X | 0 | 0 | SYSTEM | 20121209 | 150000 | 0 | 0 | 0 |
4 | 0 | 0 | JPY |14|ゆうちょ銀行|A| X | 0 | X | 0 |0|システム|20121209| 150000 | 0 | 0 | 0 |
..。

WHERE句を使用したクエリ:(結果なし)

sqlite> select * from ledger where acctno = 1;                                    
sqlite>

上記の1を引用符で囲むと、何も変わりません。

興味深いことに、「acctno>1の元帳から*を選択」は結果を返します。ただし、すべての結果が返されるため、それほど便利ではありません。

誰かがテーブルの構造について尋ねると確信しているので、ここに行きます:

sqlite>.schema元帳
CREATE TABLE "LEDGER"(
 "ACCTNO" integer(10,0)NOT NULL、
 "drbal" integer(20,0)NOT NULL、
 "crbal" integer(20,0)NOT NULL、
 "CURRKEY" char(3,0)NOT NULL、
 "TEXTKEY" integer(10,0)、
 "TEXT" VARCHAR(64,0)、
 "ACCTYPECD" CHAR(1,0)NOT NULL、
 「ACCSTCD」CHAR(1,0)、
 「PACCTNO」番号(10,0)NOT NULL、
 「CATCD」番号(10,0)、
 「TRANSNO」番号(10,0)NOT NULL、
 「extrefno」番号(10,0)、
 "UPDATEUSER" VARCHAR(32,0)、
 "UPDATEDATE" text(8,0)、
 "UPDATETIME" TEXT(6,0)、
 「PAYEECD」番号(10,0)NOT NULL、
 "drbal2" number(10,0)NOT NULL、
 "crbal2" number(10,0)NOT NULL、
 「delind」ブール値、
主キー( "ACCTNO")、
CONSTRAINT "fk_curr"外部キー( "CURRKEY")参照 "CURRENCY"( "CUR
RKEY ")更新カスケードで制限を削除する場合
);

最も奇妙なことは、これがうまく機能する他の同様のテーブルがあることです!

sqlite> select * from journalhdr where transno = 13;
13|テストトランザクションATM引き出し20130213|20130223 || 20130223 ||

そのテーブルのTransNoも整数(10,0)NOT NULLです-これが、値と関係があることを私に教えてくれます。

もう1つの手がかりは、並べ替え順序が数値ではなくASCIIに基づいているように見えることです。

sqlite> select * from ledger order by acctno;
0 | 0 | 0 | JPY |11|ルートアカウント|P| X | 0 | X | 0 |0|システム|20121209| 150000 | 0 | 0 | 0 |
1 | 0 | 0 | JPY |8|払込資本|C| X | 0 | X | 0 | 0 |||| 0 | 0 | 0 |
10 | 0 | 0 | USD | 20 | Sallie Mae | L | X | 0 | X | 0 | 0 | SYSTEM | 20121209 | 153900 | 0 | 0 | 0 |
21 | 0 | 0 | USD | 21 | Skrill | A | X | 0 | X | 0 | 0 | SYSTEM | 20121209 | 154000 | 0 | 0 | 0 |
22 | 0 | 0 | USD | 22 | AES | L | X | 0 | X | 0 | 0 | SYSTEM | 20121209 | 154200 | 0 | 0 | 0 |
23 | 0 | 0 | JPY |23|マルイ|L| X | 0 | X | 0 |0|システム|20121209| 154400 | 0 | 0 | 0 |
24 | 0 | 0 | JPY | 24 | Amex JP | L | X | 0 | X | 0 | 0 | SYSTEM | 20121209 | 154500 | 0 | 0 | 0 |
3 | 0 | 0 | JPY |13|三菱銀行Futsuu|A | X | 0 | X | 0 | 0 | SYSTEM | 20121209 | 150000 | 0 | 0 | 0 |

もちろん、journalhdr(selectが正しく機能する場所)のソート順は数値です。

解決しました!(一種の) データは次のように修正できます。

sqlite> update ledger set acctno = 23 where rowid = 13; sqlite> select * from ledger where acctno = 25; 25 | 0 | 0 | JPY |0|テスト|L| X | 0 | X | 0 |0|システム|20130224| 132500 | 0 | 0 | 0 |

それでも、文字列として保存されている場合は、いくつかの質問が残ります。1.引用符を使用して文字列として選択できなかったのはなぜですか。2.有効な整数であるため、どのようにして文字列として格納されましたか?3.奇妙な症状に気付く以外に、この問題を通常はどのように検出しますか?

データは通常私のプログラムによって入力されますが、一部はNavicatを使用して手動で作成されたものであるため、問題はそこにあるに違いないと思います。

4

1 に答える 1

0

あなたはSQLite動的型付けの犠牲者です。

SQLiteはタイプアフィニティのシステムを定義していますが、これは入力文字列または数値を実際の内部値に変換する方法に関するいくつかのルールを設定しますが、プリペアドステートメントを使用して任意のタイプ(およびデータ値)を明示的に設定するソフトウェアを妨げるものではありません列(これは行ごとに異なる場合があります!)。

これは、次の簡単な例で示すことができます。

CREATE TABLE ledger (acctno INTEGER, name VARCHAR(16));
INSERT INTO ledger VALUES(1,          'John'); -- INTEGER '1'
INSERT INTO ledger VALUES(2 || X'00', 'Zack'); -- BLOB    '2\0'

2番目の行をINTEGERとしてではなく、埋め込まれたゼロバイトを含むバイナリ文字列として挿入しました。これにより、問題が正確に再現されます。このSQLFiddleをステップバイステップで参照してください。でこれらのコマンドを実行することもできますsqlite3。同じ結果が得られます。

以下は、この問題を再現するPerlスクリプトです。

このスクリプトは、最初の行と2番目の行acctnoに整数の値を持つ2行だけを作成します。2バイトで構成される文字列を意味します。最初は数字で、2番目は(ゼロ)バイトです。1"2\0""2\0"20

もちろん、から視覚的に区別することは非常に困難です"2\0""2"、これは以下のスクリプトが示すものです。

#!/usr/bin/perl -w
use strict;
use warnings;
use DBI qw(:sql_types);
my $dbh = DBI->connect("dbi:SQLite:test.db") or die DBI::errstr();
$dbh->do("DROP TABLE IF EXISTS ledger");
$dbh->do("CREATE TABLE ledger (acctno INTEGER, name VARCHAR(16))");
my $sth = $dbh->prepare(
    "INSERT INTO ledger (acctno, name) VALUES (?, ?)");
$sth->bind_param(1, "1", SQL_INTEGER);
$sth->bind_param(2, "John");
$sth->execute();
$sth->bind_param(1, "2\0", SQL_BLOB);
$sth->bind_param(2, "Zack");
$sth->execute();
$sth = $dbh->prepare(
    "SELECT count(*) FROM ledger WHERE acctno = ?");
$sth->bind_param(1, "1");
$sth->execute();
my ($num1) = $sth->fetchrow_array();
print "Number of rows matching id '1' is $num1\n";
$sth->bind_param(1, "2");
$sth->execute();
my ($num2) = $sth->fetchrow_array();
print "Number of rows matching id '2' is $num2\n";
$sth->bind_param(1, "2\0", SQL_BLOB);
$sth->execute();
my ($num3) = $sth->fetchrow_array();
print "Number of rows matching id '2<0>' is $num3\n";

このスクリプトの出力は次のとおりです。

Number of rows matching id '1' is 1
Number of rows matching id '2' is 0
Number of rows matching id '2<0>' is 1

SQLiteツール(を含むsqlite3)を使用して結果のテーブルを見ると2、2番目の行が出力されます。文字列または数値に強制変換されると、BLOB内の末尾に0が表示されるため、すべてが混乱します。

タイプをBLOBに強制し、nullバイトの格納を許可するには、カスタムパラメータバインディングを使用する必要があることに注意してください。

 $sth->bind_param(1, "2\0", SQL_BLOB);

簡単に言えば、それはあなたのクライアントプログラムのいくつかか、それを台無しにしたNavicatのようなクライアントツールのいくつかのどちらかです。

于 2013-02-25T08:39:55.040 に答える