4

一意のテキスト文字列を格納するテーブルがあり、select を実行してその文字列がデータベースに存在するかどうかを確認します

String checkIfAlreadyScanned = "SELECT id FROM \"STRINGS_DB\"  where STR ='" + mystring + "'";

次に、値が存在するかどうかを確認します。私のデータベースには約 500 万件のレコードがあります。メソッドを改善できますか?

たとえば、新しい属性 (hashedSTR) を作成し、文字列を一意の数値に変換して、文字列の代わりにこれらの数値を取得する方法があるでしょうか? それはより速く動作しますか?(それはまったく機能しますか?)

4

9 に答える 9

4

最速の処理を確実にするには、次のことを確認してください。

  • 検索しているフィールドにインデックスが付けられます(「一意の」文字列について説明したので、すでにそうなっていると思います。このため、「limit 1」は必要ありません。それ以外の場合は、追加する必要があります)。
  • ExecuteScalar()コマンドオブジェクトのメソッドを使用しています
于 2012-07-12T19:15:59.997 に答える
2

テストは意味がありません。where 句に「テスト」を含めるだけです。

INSERT INTO silly_table(the_text)
 'literal_text'
WHERE NOT EXISTS (
    SELECT *
    FROM silly_table
    WHERE the_text = 'literal_text'
    );

ここで、必要な場合にのみテストを行います。ステートメントの最後に行が存在します。tryというものはありません。

テスト意味をなさないことを理解していない人にとっては、テスト後の状況がテスト後に変化することが許されない場合、テストは意味があります。これには、テストとロックのシナリオが必要です。または、さらに悪いことに、トランザクション内のテストです。

更新: 動作するバージョン (基本的に同じ):

DROP TABLE exitsnot CASCADE;
CREATE TABLE exitsnot
        ( id SERIAL NOT NULL PRIMARY KEY
        , val INTEGER -- REFERENCES something
        , str varchar -- REFERENCES something
        );

INSERT INTO exitsnot (val)
SELECT 42
WHERE NOT EXISTS (
        SELECT * FROM exitsnot
        WHERE val = 42
        );
INSERT INTO exitsnot (str)
SELECT 'silly text'
WHERE NOT EXISTS (
        SELECT * FROM exitsnot
        WHERE str = 'silly text'
        );
SELECT version();

出力:

DROP TABLE
NOTICE:  CREATE TABLE will create implicit sequence "exitsnot_id_seq" for serial column "exitsnot.id"
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "exitsnot_pkey" for table "exitsnot"
CREATE TABLE
INSERT 0 1
INSERT 0 1
                                           version                                            
----------------------------------------------------------------------------------------------
 PostgreSQL 9.1.2 on i686-pc-linux-gnu, compiled by gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3, 32-bit
(1 row)
于 2012-07-12T23:07:15.977 に答える
1

これらのテキスト文字列の長さはどれくらいですか?それらが非常に長い場合は、文字列のハッシュを(元の文字列と一緒に)保存することでパフォーマンスが向上する可能性があります。

CREATE TABLE strings_db (
    id       PRIMARY KEY INT,
    text     TEXT,
    hash     TEXT
);

ハッシュ列には、MD5合計、CRC32、または選択したその他のハッシュアルゴリズムを格納できます。そして、それは索引付けされるべきです。

次に、クエリを次のように変更します。

SELECT id FROM strings_db WHERE hash=calculate_hash(?)

テキストフィールドの平均サイズがハッシュのサイズよりも十分に大きい場合は、短いフィールドで検索を実行すると、ディスクI/Oに役立ちます。これは、ハッシュを計算するための挿入および選択時の追加のCPUオーバーヘッド、およびハッシュを格納するための追加のディスク容量も意味します。したがって、これらすべての要因を考慮に入れる必要があります。

PS SQLインジェクション攻撃を回避するために、常にプリペアドステートメントを使用してください。

于 2012-07-12T19:14:23.650 に答える
1

実は、あなたが求めているようなものがあります。しかし、それにはいくつかの制限があります。PostgreSQL は次のhashインデックス タイプをサポートしています。

CREATE INDEX strings_hash_idx ON "STRINGS_DB" USING hash (str);

=あなたが持っているのと同じように、を使用した単純な等価検索に使用できます。制限に関するマニュアルを引用します。

ハッシュ インデックス操作は現在 WAL ログに記録されていないため、データベース クラッシュ後にハッシュ インデックスを REINDEX で再構築する必要がある場合があります。また、ストリーミングやファイルベースのレプリケーションでは複製されません。これらの理由から、ハッシュ インデックスの使用は現在推奨されていません。


実際のテーブル、433k 行、合計 59 MB での簡単なテスト:

SELECT * FROM tbl WHERE email = 'some.user@some.domain.com'
-- No index, sequnence scan: Total runtime: 188 ms  
-- B-tree index (default):   Total runtime:   0.046 ms  
-- Hash index:               Total runtime:   0.032 ms  

それは巨大ではありませんが、何か。私のテストでは、電子メール アドレスよりも長い文字列を使用すると、違いが大きくなります。インデックスの作成は 1 ~ 2 秒で完了しました。どちらのインデックスでも。

于 2012-07-12T19:40:54.750 に答える
1
String checkIfAlreadyScanned = "SELECT 1 FROM \"STRINGS_DB\"  where STR ='" + mystring + "'";

結果セットに行が含まれている場合、レコードがあります

于 2012-07-12T19:09:23.540 に答える
1

結果セットを 1 に制限します。

String checkIfAlreadyScanned = @"
    SELECT id 
    FROM ""STRINGS_DB""  
    where STR ='" + mystring + @"'
    limit 1";

これ、その列のインデックス、および@Laurentの提案ExecuteScalar()により、最良の結果が得られます。

またmystring、ユーザーが触れた可能性がある場合は、SQL インジェクションを回避するためにクエリをパラメーター化します。

よりクリーンなバージョン:

String checkIfAlreadyScanned = @"
    SELECT id 
    FROM ""STRINGS_DB""  
    where STR = '@mystring'
    limit 1
    ".replace("@mystring", mystring);
于 2012-07-12T19:12:03.993 に答える
0

ここでのすべての答えにはメリットがありますが、別の側面についても触れておきたいと思います。

この方法でクエリを作成して文字列を渡すことは、データベースエンジンがクエリを最適化するのに役立ちません。代わりに、ストアドプロシージャを記述し、単一のパラメータを渡してそれを呼び出し、データベースエンジンにクエリプランを作成させてコマンドを再利用する必要があります。

もちろん、フィールドにはインデックスを付ける必要があります

于 2012-07-12T19:15:35.627 に答える
0

実際には列が必要ないと仮定するとid、これによりコンパイラーが最適化する可能性が最も高くなると思います。

select 1
where exists(
    select 1 
    from STRINGS_DB
    where STR = 'MyString'
)
于 2012-07-12T19:13:50.450 に答える
0

[編集] 返される結果を、基準を満たす最初のレコードを返すように制限します。SqlServer の場合: TOP 1 ... を選択します。mysql/postgres の場合: select ... LIMIT 1;

複数ある可能性がある場合は、select ステートメントに「TOP 1」を追加すると、より速く返される可能性があります。

String checkIfAlreadyScanned = "SELECT TOP 1 id FROM \"STRINGS_DB\"  where STR ='" + mystring + "'";

そうすれば、文字列の最初のインスタンスを見つけるだけで済みます。

ただし、倍数がない場合、このアプローチではあまりメリットが得られない可能性があります。

他の人が言ったように、それにインデックスを付けると役立つかもしれません。

于 2012-07-12T19:12:28.173 に答える