我有一个表存储唯一的文本字符串,然后通过选择
检查数据库中是否存在该字符串String checkIfAlreadyScanned = "SELECT id FROM \"STRINGS_DB\" where STR ='" + mystring + "'";
然后我检查值是否存在。我的数据库有大约5百万条记录;我可以改进我的方法吗?
也许有一种方法可以创建一个新属性(hashedSTR),例如将字符串转换为某个唯一的数字值,然后获取这些数字,而不是字符串?那会更快吗? (这会起作用吗?)
答案 0 :(得分:4)
为确保最快的处理速度,请确保:
ExecuteScalar()
方法答案 1 :(得分:2)
测试毫无意义,只需在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)
答案 2 :(得分:1)
String checkIfAlreadyScanned = "SELECT 1 FROM \"STRINGS_DB\" where STR ='" + mystring + "'";
如果您的结果集包含一行,那么您有一条记录
答案 3 :(得分: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);
答案 4 :(得分: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开销。因此,必须考虑所有这些因素。
P.S。始终使用预准备语句来避免SQL注入攻击!
答案 5 :(得分: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 = '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秒。无论哪种指数。
答案 6 :(得分:0)
[编辑] 返回限制结果以返回符合条件的第一条记录: 对于SqlServer:选择TOP 1 ...; 对于mysql / postgres:选择... LIMIT 1;
如果可以有倍数,可能在select语句中添加“TOP 1”可能会更快返回。
String checkIfAlreadyScanned = "SELECT TOP 1 id FROM \"STRINGS_DB\" where STR ='" + mystring + "'";
这样,它只需要找到字符串的第一个实例。
但是,如果你没有倍数,你可能不会看到这种方法带来太多好处。
就像其他人所说的那样,在其上加上一个索引可能有所帮助。
答案 7 :(得分:0)
假设您实际上不需要id
列,我认为这为编译器提供了最佳优化机会:
select 1
where exists(
select 1
from STRINGS_DB
where STR = 'MyString'
)
答案 8 :(得分:0)
虽然这里的所有答案都有其优点,但我想提到另一个方面。
以这种方式构建查询并传递字符串将无助于数据库引擎优化您的查询。相反,你应该编写一个存储过程,调用它传递一个参数,让数据库引擎构建一个查询计划并重用你的命令。
当然应该将该字段编入索引