2012-07-12 49 views
4

我在那裏我存儲唯一的文本字符串,然後我做選擇如何檢查是否在數據庫中存在的記錄 - 最快的方法

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

然後檢查是否在數據庫中存在該字符串表我是否值存在。我的數據庫有大約5mil的記錄;我可以改進我的方法嗎?

也許有一種方式創建一個新的屬性(hashedSTR)爲例,並將字符串轉換爲一些獨特的數字值,然後得到這些數字,而不是字符串?這會更快嗎? (?將是在所有的工作)

+0

什麼是數據庫? – 2012-07-12 19:07:14

+5

除了在STR字段中加入索引外,沒有太多可以做的事情。 – 2012-07-12 19:08:01

+0

出於好奇,查詢字符串有什麼問題..爲什麼有一個]「STRINGS_DB \」 – MethodMan 2012-07-12 19:09:45

回答

4

保證了最快的處理,確保:

  • 您正在搜索的字段建立索引(您講述一個「獨一無二」的字符串,所以我假設它已經是這樣了。因爲這個原因,「限1」是沒有必要的。否則,它應該被添加)
  • 您在使用Command對象的ExecuteScalar()方法
+0

+ ExecuteScalar()'建議 – 2012-07-12 19:23:10

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

如果您的結果集包含行,那麼你就會有記錄

+0

這將返回匹配條件的所有行,這些條件對性能不好 – 2012-07-12 19:17:07

1

限制結果設置爲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); 
+0

問題中的第一句:「唯一文本字符串」。沒有倍數。 – 2012-07-12 19:47:58

0

[編輯] 限制結果返回到返回它遇到的第一個記錄符合標準: 使用Sql Server:選擇TOP 1 ...; 對於mysql/postgres:select ... LIMIT 1;

如果可以有多個,那麼在您的select語句中添加「TOP 1」可能會返回得更快。

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

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

但是,如果您沒有倍數,那麼使用這種方法不會看到太多好處。

就像其他人所說的那樣,在它上面加上索引可能會有所幫助。

+0

-1在postgresql中沒有'TOP' – 2012-07-12 19:21:40

+0

如果您使用的是postgres,只需在末尾添加「limit 1」:http://forums.devshed.com/postgresql-help-21/how-to-take-top- 5-records-using-select-in-postgres-92744.html – Brandon 2012-07-12 19:25:53

+0

問題中的第一句話:「獨特的文本字符串」。沒有倍數。 – 2012-07-12 19:44:44

0

假設你實際上並不需要的id列,我認爲這使編譯器有機會最優化:

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

'EXISTS'半聯接可以幫助重複,但不會與「唯一的文本字符串」。 – 2012-07-12 19:46:16

1

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

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

您的哈希列可以存儲MD5總和,CRC32s或您選擇的任何其他哈希算法。它應該被索引。

然後修改您的查詢是這樣的:

SELECT id FROM strings_db WHERE hash=calculate_hash(?) 

如果你的文本字段的平均大小是比你的哈希的尺寸足夠大,這樣做在較短場搜索將磁盤I幫助/ O.這也意味着在插入和選擇時計算散列以及用於存儲散列的額外磁盤空間會產生額外的CPU開銷。所以所有這些因素都必須考慮在內。

P.S.始終使用預準備語句來避免SQL注入攻擊!

+0

這不是索引工作嗎? – 2012-07-12 19:25:58

+0

@Clodoaldo:PostgreSQL B樹索引不這樣做(我不知道任何無損索引引擎,但可能會有一些)。如果磁盤I/O是即時的,那麼我的方法對於psql的默認B-Tree沒有什麼好處,因爲必須在B-tree中遍歷相同數量的索引記錄,但是B樹中的每個節點都包含較少數據,從磁盤讀取數據會更快。我的磁盤I/O是免費的,這種方法沒有任何優勢。 – Flimzy 2012-07-12 19:37:49

0

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

以這種方式構建您的查詢並傳遞字符串不會幫助數據庫引擎優化您的查詢。相反,您應該編寫一個存儲過程,將其稱爲傳遞一個參數,並讓數據庫引擎構建一個查詢計劃並重用您的命令。

當然領域應該被索引

1

其實就是就是這樣的東西你喜歡的。但它有一些限制。 PostgreSQL支持一個hash索引類型:

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

作品進行簡單的等式搜索與=,就像你擁有了它。我quote the manual的限制:

散列索引操作目前未WAL-記錄,所以哈希索引 可能需要一個數據庫崩潰後要與REINDEX重建。它們是 也不通過流式傳輸或基於文件的複製進行復制。對於 這些原因,目前不推薦使用散列索引。


一個真正的生命表,433K行,59 MB總簡單的測試:

SELECT * FROM tbl WHERE email = '[email protected]' 
-- 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秒。與任一索引。

2

測試是沒有意義的,只是包括在where子句中的「測試」:

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) 
+0

好點,至今錯過了。 – 2012-07-12 23:19:26

+0

我剛剛添加了一些文字,因爲我有時似乎有點神祕。整個話題是一個DBA與應用程序員之間的事情。 (你可能知道這個結果;-)順便說一句:這是1/2的速度改進,因爲測試將與I/U/D一樣昂貴。 – wildplasser 2012-07-12 23:23:10

+0

你確定這是正確的語法嗎?我的postgre在'where'顯示錯誤' – Andrew 2012-07-13 00:42:07

相關問題