C# 如何检查数据库中是否存在记录-最快方法

C# 如何检查数据库中是否存在记录-最快方法,c#,database,string,performance,postgresql,C#,Database,String,Performance,Postgresql,我有一个存储唯一文本字符串的表,然后通过执行select检查数据库中是否存在该字符串 String checkIfAlreadyScanned = "SELECT id FROM \"STRINGS_DB\" where STR ='" + mystring + "'"; 然后我检查值是否存在。我的数据库有大约5英里的记录;我可以改进我的方法吗 例如,也许有一种方法可以创建一个新属性(hashedSTR),将字符串转换成一些唯一的数字值,然后得到这些数字,而不是字符串?那会更快吗?(这行得通

我有一个存储唯一文本字符串的表,然后通过执行select检查数据库中是否存在该字符串

String checkIfAlreadyScanned = "SELECT id FROM \"STRINGS_DB\"  where STR ='" + mystring + "'";
然后我检查值是否存在。我的数据库有大约5英里的记录;我可以改进我的方法吗

例如,也许有一种方法可以创建一个新属性(hashedSTR),将字符串转换成一些唯一的数字值,然后得到这些数字,而不是字符串?那会更快吗?(这行得通吗?)


如果结果集包含一行,则您有一条记录

将结果集限制为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);
[编辑] 限制返回的结果以返回它遇到的符合条件的第一条记录: 对于SqlServer:选择Top1。。。; 对于mysql/postgres:选择。。。限值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'
)

这些文本字符串有多长?如果字符串很长,您可以通过存储字符串的散列(以及原始字符串)来提高性能

您的哈希列可以存储MD5和、CRC32或您选择的任何其他哈希算法。它应该被编入索引

然后将查询修改为类似以下内容:

SELECT id FROM strings_db WHERE hash=calculate_hash(?)
如果文本字段的平均大小远远大于散列的大小,则在较短的字段上进行搜索将有助于磁盘I/O。这也意味着在插入和选择计算散列时会增加CPU开销,并增加存储散列的磁盘空间。因此,必须考虑所有这些因素


注意:始终使用准备好的语句来避免SQL注入攻击

虽然这里所有的答案都有其优点,但我想提到另一个方面

以这种方式构建查询并传递字符串无助于数据库引擎优化查询。相反,您应该编写一个存储过程,通过单个参数调用它,让数据库引擎构建一个查询计划并重用您的命令


当然,该字段应编入索引

为确保处理速度最快,请确保:

  • 您正在搜索的字段已编入索引(您说过有一个“唯一”字符串,因此我认为它已经存在。因此,不需要“限制1”。否则,应添加它)
  • 您正在使用命令对象的
    ExecuteScalar()
    方法

事实上,有一件事正是你想要的。但它也有一些局限性。PostgreSQL支持
哈希
索引类型:

CREATE INDEX strings_hash_idx ON "STRINGS_DB" USING hash (str);
适用于使用
=
进行简单的相等搜索,就像您拥有它一样。一、关于限制:

哈希索引操作目前未记录,因此哈希索引 数据库崩溃后,可能需要使用REINDEX重建。他们是 也不能通过流式或基于文件的复制进行复制。对于 由于这些原因,目前不鼓励使用散列索引


在现实生活中的表上进行快速测试,433k行,总计59MB:

从tbl中选择*其中email='some。user@some.domain.com'
--无索引,顺序扫描:总运行时间:188毫秒
--B树索引(默认):总运行时间:0.046毫秒
--哈希索引:总运行时间:0.032毫秒

这不是很大,但有点大。与我测试中的电子邮件地址相比,字符串较长的差异将更大。创建索引只需1或2秒。使用任一索引。

测试没有意义,只需在where子句中包含“测试”:

INSERT INTO silly_table(the_text)
 'literal_text'
WHERE NOT EXISTS (
    SELECT *
    FROM silly_table
    WHERE the_text = 'literal_text'
    );
现在,您将仅在需要时进行测试:在语句末尾,该行将存在。没有“尝试”这回事

对于那些不了解测试的人来说,测试毫无意义:如果测试后的情况不允许在测试后发生变化,那么测试就有意义。这需要一个测试和锁定场景。或者,更糟糕的是:事务内部的测试

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

输出:

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)

除了在STR字段上添加索引之外,您没有什么可以做的。出于好奇,查询字符串是怎么回事……为什么会有一个“]“STRINGS\u DB\”您也可能在那里进行sql注入,只是说。如果STRINGS表没有更新,那么打包页面(填充因子=100%)并将字符串作为主键(使用关联的唯一索引)是最快的方法。在某些数据库中,下面提到的exists()子句在这种情况下可能会稍微快一点。检查是无用的;下次尝试插入(或更新或删除)时,它可能已更改。(甚至)在传输中,您可以在(不)存在的位置添加
(…)
term到查询中。这将返回所有与不利于性能的条件匹配的行。如果使用postgres,请在末尾添加“限制1”:问题的第一句:“唯一文本字符串”。无倍数。@Clodoaldo:PostgreSQL B-Tree索引不执行此操作(据我所知,没有任何无损索引引擎能做到这一点,但可能有一些)。如果磁盘I/O是瞬时的,那么与psql的默认B-树相比,我的方法没有任何优势,因为B-树中必须遍历相同数量的索引记录,但是由于B-树中的每个节点包含的数据较少,从磁盘读取数据的速度更快。
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)