簡體   English   中英

如何檢查數據庫中是否存在記錄 - 最快的方法

[英]How to check if record exists in database - fastest method

我有一個表,我存儲唯一的文本字符串,然后我通過選擇檢查數據庫中是否存在該字符串

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

然后我檢查值是否存在。 我的數據庫有大約5百萬條記錄; 我可以改進我的方法嗎?

也許有一種方法可以創建一個新屬性(hashedSTR),例如將字符串轉換為某個唯一的數字值,然后獲取這些數字而不是字符串? 那會更快嗎? (這會起作用嗎?)

為確保最快的處理,請確保:

  • 您正在搜索的字段已編入索引(您被告知“唯一”字符串,因此我認為已經是這種情況。因此,“限制1”不是必需的。否則,應該添加)
  • 您正在使用Command對象的ExecuteScalar()方法

測試毫無意義,只需在where子句中包含“test”:

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

現在,只有在需要時才進行測試:在語句結束時行將存在。 沒有嘗試這樣的事情。

對於那些不了解測試的人沒有意義 :如果測試后的情況不允許在測試后改變,那么測試有意義。 這需要測試和鎖定方案。 或者,更糟糕的是:在交易中進行測試。

更新:有效的版本(基本相同):

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)
String checkIfAlreadyScanned = "SELECT 1 FROM \"STRINGS_DB\"  where STR ='" + mystring + "'";

如果您的結果集包含一行,那么您有一條記錄

將結果集限制為1:

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

這個,該列的索引,以及ExecuteScalar()的@Laurent建議將產生最佳結果。

此外,如果mystring有任何機會被用戶觸摸,那么參數化查詢以避免sql注入。

更清潔的版本:

String checkIfAlreadyScanned = @"
    SELECT id 
    FROM ""STRINGS_DB""  
    where STR = '@mystring'
    limit 1
    ".replace("@mystring", mystring);

這些文本字符串有多長? 如果它們很長,您可以通過存儲字符串的哈希值(以及原始字符串)來提高性能。

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注入攻擊!

其實,就像你問這樣一個事情。 但它有一些局限性。 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秒。 與任何指數。

[編輯]返回限制結果以返回符合條件的第一條記錄:對於SqlServer:選擇TOP 1 ...; 對於mysql / postgres:select ... LIMIT 1;

如果可以有倍數,可能在select語句中添加“TOP 1”可以更快地返回。

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

這樣,它只需要找到字符串的第一個實例。

但是,如果你沒有倍數,你可能不會看到這種方法帶來太多好處。

像其他人所說的那樣,在其上加上指數可能有所幫助。

假設您實際上不需要id列,我認為這為編譯器提供了最大的優化機會:

select 1
where exists(
    select 1 
    from STRINGS_DB
    where STR = 'MyString'
)

雖然這里的所有答案都有其優點,但我想提到另一個方面。

以這種方式構建查詢並傳遞字符串將無助於數據庫引擎優化您的查詢。 相反,您應該編寫一個存儲過程,調用它傳遞一個參數,讓數據庫引擎構建一個查詢計划並重用您的命令。

當然應該將該字段編入索引

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM